Module: Foam::Otel::Metrics
- Defined in:
- lib/foam/otel/metrics.rb
Overview
Backs the four metric helpers — one per synchronous OTel instrument (BASE_PACKAGE_SPEC section 0 metrics row): counter, histogram, up/down counter, and gauge. Instruments are lazy-created and cached, keyed on (kind, name); the whole cache rebinds exactly once when the meter provider identity changes (init swapping the proxy for foam's provider, or a foreign provider owning the slot). Creating an instrument per call re-registers it under a mutex and warns — hence the cache. Names pass through verbatim. Never raises.
The source provider is always OpenTelemetry.meter_provider: foam's own when the metrics slot was FREE, or the FOREIGN provider when a foreign SDK owns it (inert mode, rule 18 B — the metric helpers emit through whoever owns the slot).
Constant Summary collapse
- MUTEX =
Mutex.new
Class Method Summary collapse
- .add_up_down_counter(name, by:, attributes: nil) ⇒ Object
- .build(meter, kind, name) ⇒ Object
-
.disabled? ⇒ Boolean
section 0: disabled means fully dark — the metric helpers never touch the live (possibly foreign) meter provider once foam is initialized with effective enabled=false.
- .increment_counter(name, by: 1, attributes: nil) ⇒ Object
-
.instrument(kind, name) ⇒ Object
---- internals -------------------------------------------------------.
- .record_histogram(name, value, attributes: nil) ⇒ Object
- .reset_for_tests! ⇒ Object
- .set_metric(name, value, attributes: nil) ⇒ Object
-
.stringify(attributes) ⇒ Object
Metric attributes ride the always-on redaction floor like span/log attributes (rule 14: no raw secret in ANY exported metric) — a secret-shaped label a caller passes to a metric helper is masked before it becomes a metric-stream dimension.
Class Method Details
.add_up_down_counter(name, by:, attributes: nil) ⇒ Object
52 53 54 55 56 57 58 59 60 |
# File 'lib/foam/otel/metrics.rb', line 52 def add_up_down_counter(name, by:, attributes: nil) return true if disabled? inst = instrument(:up_down_counter, name) or return false inst.add(Float(by), attributes: stringify(attributes)) true rescue StandardError, SystemStackError false end |
.build(meter, kind, name) ⇒ Object
92 93 94 95 96 97 98 99 |
# File 'lib/foam/otel/metrics.rb', line 92 def build(meter, kind, name) case kind when :counter then meter.create_counter(name) when :histogram then meter.create_histogram(name) when :up_down_counter then meter.create_up_down_counter(name) when :gauge then meter.create_gauge(name) end end |
.disabled? ⇒ Boolean
section 0: disabled means fully dark — the metric helpers never touch the live (possibly foreign) meter provider once foam is initialized with effective enabled=false.
104 105 106 |
# File 'lib/foam/otel/metrics.rb', line 104 def disabled? Foam::Otel.send(:helpers_disabled?) end |
.increment_counter(name, by: 1, attributes: nil) ⇒ Object
32 33 34 35 36 37 38 39 40 |
# File 'lib/foam/otel/metrics.rb', line 32 def increment_counter(name, by: 1, attributes: nil) return true if disabled? # fully dark — accepted, silently dropped (section 0) inst = instrument(:counter, name) or return false inst.add(Float(by), attributes: stringify(attributes)) true rescue StandardError, SystemStackError false end |
.instrument(kind, name) ⇒ Object
---- internals -------------------------------------------------------
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/foam/otel/metrics.rb', line 74 def instrument(kind, name) key = [kind, name.to_s] return nil if name.to_s.empty? provider = OpenTelemetry.meter_provider # The metric cache rebind is the one place foam notices a provider # identity change — surface a late-arrival displacement here too # (rule 11 / 18 step 5). Foam::Otel.send(:warn_if_displaced, :metrics, provider) MUTEX.synchronize do unless @provider.equal?(provider) @provider = provider @cache = {} end @cache[key] ||= build(provider.meter("foam"), kind, name.to_s) end end |
.record_histogram(name, value, attributes: nil) ⇒ Object
42 43 44 45 46 47 48 49 50 |
# File 'lib/foam/otel/metrics.rb', line 42 def record_histogram(name, value, attributes: nil) return true if disabled? inst = instrument(:histogram, name) or return false inst.record(Float(value), attributes: stringify(attributes)) true rescue StandardError, SystemStackError false end |
.reset_for_tests! ⇒ Object
25 26 27 28 29 30 |
# File 'lib/foam/otel/metrics.rb', line 25 def reset_for_tests! MUTEX.synchronize do @provider = nil @cache = {} end end |
.set_metric(name, value, attributes: nil) ⇒ Object
62 63 64 65 66 67 68 69 70 |
# File 'lib/foam/otel/metrics.rb', line 62 def set_metric(name, value, attributes: nil) return true if disabled? inst = instrument(:gauge, name) or return false inst.record(Float(value), attributes: stringify(attributes)) true rescue StandardError, SystemStackError false end |
.stringify(attributes) ⇒ Object
Metric attributes ride the always-on redaction floor like span/log attributes (rule 14: no raw secret in ANY exported metric) — a secret-shaped label a caller passes to a metric helper is masked before it becomes a metric-stream dimension. (Ruby official gems rarely emit metrics; the floor covers the foam-helper surface.)
nil (the helpers' own default) becomes {} — NEVER nil: the OTLP metrics exporter's encoder crashes on a nil-attributes data point (opentelemetry-exporter-otlp-metrics 0.10.0 metrics_exporter.rb number_data_point → attributes.map), and one such point silently drops the ENTIRE metrics export request, healthy instruments included (rule 15 silent data loss). Garbage degrades to {} too.
120 121 122 123 124 125 126 127 128 |
# File 'lib/foam/otel/metrics.rb', line 120 def stringify(attributes) # scrub_utf8 per value: an invalid-UTF-8 label would make the OTLP # metrics encoder drop the ENTIRE export request (same silent-loss # class as the nil-attributes crash below). stringified = (attributes || {}).each_with_object({}) { |(k, v), out| out[k.to_s] = Redaction.scrub_utf8(v) } Redaction.mask_attributes(stringified, Foam::Otel.active_config) rescue StandardError, SystemStackError {} end |