Otel.API.Metrics.Meter behaviour (otel v0.2.0)

Copy Markdown View Source

Meter behaviour and dispatch facade (OTel metrics/api.md §Meter, Status: Stable, L157-L176).

A Meter creates instruments. Per spec L161-L162 "Meter SHOULD NOT be responsible for the configuration. This should be the responsibility of the MeterProvider instead" — we honour this by keeping configuration (resource, views, etc.) in Otel.API.Metrics.MeterProvider and exposing no config-related functions on the Meter itself. Spec L166-L174 enumerates seven instruments the Meter MUST provide creation functions for; all seven are covered (create_counter/3, create_histogram/3, create_gauge/3, create_updown_counter/3, plus the three create_observable_* pairs).

BEAM representation

A Meter is represented as a {dispatcher_module, state} tuple — the same shape Tracer and Logger use for facade dispatch. Callers obtain a Meter via Otel.API.Metrics.MeterProvider.get_meter/1 rather than constructing the tuple directly; the dispatcher_module MUST implement this behaviour.

All functions are safe for concurrent use (spec L1351-L1352, §Concurrency §Instrument).

Divergences from opentelemetry-erlang

opentelemetry-erlang's otel_meter.erl diverges in seven places. All are spec-aligned or intentional API-surface trims:

  1. Creation API shape — erlang has generic create_instrument/4,6(Kind) callbacks taking the kind as a parameter; we expose one callback per instrument kind (10 callbacks total across /3 and /5 arities). Matches the facade-per-kind pattern exposed by Counter, Histogram, ObservableGauge, and siblings.
  2. register_callback/5 signature — erlang has register_callback/4 (no opts, returns ok). We accept opts and return a registration() handle consumable by unregister_callback/1.
  3. unregister_callback/1 added — erlang has no such function. Spec api.md L419-L420 MUST "user MUST be able to undo registration of the specific callback"; we implement it.
  4. enabled?/2 added — erlang has no such function. Spec api.md L475-L495 SHOULD provides the instrument-enabled API; we implement it.
  5. record/3 without context — erlang threads Ctx through record/5. Our synchronous metric recording does not associate with context at the API boundary; the SDK attaches context via Otel.API.Ctx if relevant.
  6. scope/1 not exposed — erlang returns the instrumentation scope from a Meter. We do not expose this; MeterProvider holds the scope, and the {module, state} tuple carries whatever the SDK needs. Not a spec MUST; API surface trim.
  7. lookup_instrument/2 not exposed — erlang supports looking up an existing instrument by name. Not a spec operation; callers hold the struct handle returned by create_*. API surface trim.

Public API

FunctionRole
create_counter/3Application (OTel API MUST) — Counter creation (api.md L510-L542)
create_histogram/3Application (OTel API MUST) — Histogram creation (api.md L746-L777)
create_gauge/3Application (OTel API MUST) — Gauge creation (api.md L852-L872)
create_updown_counter/3Application (OTel API MUST) — UpDownCounter creation (api.md L1084-L1115)
create_observable_counter/3,5Application (OTel API MUST) — Async Counter creation (api.md L613-L703)
create_observable_gauge/3,5Application (OTel API MUST) — Async Gauge creation (api.md L934-L1031)
create_observable_updown_counter/3,5Application (OTel API MUST) — Async UpDownCounter creation (api.md L1176-L1277)
record/3Application (OTel API MUST) — Counter Add / Histogram Record / Gauge Record / UpDownCounter Add dispatch
register_callback/5Application (OTel API MUST) — callback creation (api.md L408-L410)
unregister_callback/1Application (OTel API MUST) — undo registration (api.md L419-L420)
enabled?/2Application (OTel API SHOULD) — Enabled (api.md L475-L495)
@callback declarations of the aboveSDK (OTel API MUST/SHOULD) — SDK dispatch contract

References

  • OTel Metrics API §Meter: opentelemetry-specification/specification/metrics/api.md L157-L176
  • OTel Metrics API §Synchronous Instrument API: opentelemetry-specification/specification/metrics/api.md L302-L348
  • OTel Metrics API §Asynchronous Instrument API: opentelemetry-specification/specification/metrics/api.md L350-L472
  • OTel Metrics API §General operations / Enabled: opentelemetry-specification/specification/metrics/api.md L473-L495
  • OTel Metrics API §Concurrency §Instrument: opentelemetry-specification/specification/metrics/api.md L1351-L1352
  • Reference impl: opentelemetry-erlang/apps/opentelemetry_api_experimental/src/otel_meter.erl

Summary

Types

Handle returned by register_callback/5. Pass to unregister_callback/1 to undo the registration.

t()

A {dispatcher_module, state} pair.

Callbacks

SDK (OTel API MUST) — Dispatch callback for create_counter/3 (api.md §Counter creation L510-L542).

SDK (OTel API MUST) — Dispatch callback for create_gauge/3 (api.md §Gauge creation L852-L872).

SDK (OTel API MUST) — Dispatch callback for create_histogram/3 (api.md §Histogram creation L746-L777).

SDK (OTel API MUST) — Dispatch callback for create_observable_counter/3 without inline callback (api.md §Asynchronous Counter creation L613-L703).

SDK (OTel API MUST) — Dispatch callback for create_observable_counter/5 with inline callback (api.md L613-L703 + L446-L447 MUST — callbacks registered at creation time).

SDK (OTel API MUST) — Dispatch callback for create_observable_gauge/3 without inline callback (api.md §Asynchronous Gauge creation L934-L1031).

SDK (OTel API MUST) — Dispatch callback for create_observable_gauge/5 with inline callback (api.md L934-L1031 + L446-L447 MUST).

SDK (OTel API MUST) — Dispatch callback for create_observable_updown_counter/3 without inline callback (api.md §Asynchronous UpDownCounter creation L1176-L1277).

SDK (OTel API MUST) — Dispatch callback for create_observable_updown_counter/5 with inline callback (api.md L1176-L1277 + L446-L447 MUST).

SDK (OTel API MUST) — Dispatch callback for create_updown_counter/3 (api.md §UpDownCounter creation L1084-L1115).

SDK (OTel API SHOULD) — Dispatch callback for enabled?/2 (api.md §General operations — Enabled, L475-L495).

SDK (OTel API MUST) — Dispatch callback for record/3 (api.md §Counter Add L545-L598 / §Histogram Record L781-L826 / §Gauge Record L876-L915 / §UpDownCounter Add L1118-L1156).

SDK (OTel API MUST) — Dispatch callback for register_callback/5 (api.md L408-L410 — callback creation for async instruments).

SDK (OTel API MUST) — Dispatch callback for unregister_callback/1 (api.md L419-L420 — undo callback registration).

Functions

Application (OTel API MUST) — "Counter creation" (api.md L510-L542). Dispatches to dispatcher_module.create_counter/3.

Application (OTel API MUST) — "Gauge creation" (api.md L852-L872). Dispatches to dispatcher_module.create_gauge/3.

Application (OTel API MUST) — "Histogram creation" (api.md L746-L777). Dispatches to dispatcher_module.create_histogram/3.

Application (OTel API MUST) — "Async Counter creation" without inline callback (api.md L613-L703). Dispatches to dispatcher_module.create_observable_counter/3.

Application (OTel API MUST) — "Async Counter creation" with inline callback (api.md L613-L703). callback is a 1-arity function receiving callback_args and returning [Measurement.t()] per spec L441-L442 list-return form. Dispatches to dispatcher_module.create_observable_counter/5.

Application (OTel API MUST) — "Async Gauge creation" without inline callback (api.md L934-L1031). Dispatches to dispatcher_module.create_observable_gauge/3.

Application (OTel API MUST) — "Async Gauge creation" with inline callback (api.md L934-L1031). Same callback contract as create_observable_counter/5.

Application (OTel API MUST) — "Async UpDownCounter creation" without inline callback (api.md L1176-L1277). Dispatches to dispatcher_module.create_observable_updown_counter/3.

Application (OTel API MUST) — "Async UpDownCounter creation" with inline callback (api.md L1176-L1277). Same callback contract as create_observable_counter/5.

Application (OTel API MUST) — "UpDownCounter creation" (api.md L1084-L1115). Dispatches to dispatcher_module.create_updown_counter/3.

Application (OTel API SHOULD) — "Enabled" (api.md §General operations — Enabled, L475-L495).

Application (OTel API MUST) — Records a measurement for the given instrument.

Application (OTel API MUST) — Registers a callback for one or more asynchronous instruments (api.md L408-L410 MUST support callback creation).

Application (OTel API MUST) — Undoes a prior register_callback/5 registration (api.md L419-L420 "user MUST be able to undo registration of the specific callback").

Types

primitive()

@type primitive() ::
  String.t() | {:bytes, binary()} | boolean() | integer() | float() | nil

primitive_any()

@type primitive_any() ::
  primitive() | [primitive_any()] | %{required(String.t()) => primitive_any()}

registration()

@type registration() :: {module(), term()}

Handle returned by register_callback/5. Pass to unregister_callback/1 to undo the registration.

Structurally a {dispatcher_module, state} pair — the same shape as t/0 — but distinct in role: registration identifies a callback registration rather than a Meter. The API layer treats state as opaque; callers should consume the handle exclusively through unregister_callback/1 rather than destructuring it.

Not declared @opaque because dispatcher implementations (e.g. Otel.API.Metrics.Meter.Noop, the SDK Meter) must construct the tuple directly — an @opaque type cannot be constructed from outside its defining module, which would require an extra factory function per the {module, state} dispatch idiom the project already uses unopaqued on Tracer.t/0, Logger.t/0, and Meter.t/0.

t()

@type t() :: {module(), term()}

A {dispatcher_module, state} pair.

The API layer treats state as opaque; only dispatcher_module knows how to interpret it. dispatcher_module MUST implement the Otel.API.Metrics.Meter behaviour.

Obtain a Meter via Otel.API.Metrics.MeterProvider.get_meter/1 rather than constructing the tuple directly.

Callbacks

create_counter(meter, name, opts)

@callback create_counter(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

SDK (OTel API MUST) — Dispatch callback for create_counter/3 (api.md §Counter creation L510-L542).

create_gauge(meter, name, opts)

@callback create_gauge(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

SDK (OTel API MUST) — Dispatch callback for create_gauge/3 (api.md §Gauge creation L852-L872).

create_histogram(meter, name, opts)

@callback create_histogram(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

SDK (OTel API MUST) — Dispatch callback for create_histogram/3 (api.md §Histogram creation L746-L777).

create_observable_counter(meter, name, opts)

@callback create_observable_counter(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

SDK (OTel API MUST) — Dispatch callback for create_observable_counter/3 without inline callback (api.md §Asynchronous Counter creation L613-L703).

create_observable_counter(meter, name, callback, callback_args, opts)

@callback create_observable_counter(
  meter :: t(),
  name :: String.t(),
  callback :: (term() -> [Otel.API.Metrics.Measurement.t()]),
  callback_args :: term(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

SDK (OTel API MUST) — Dispatch callback for create_observable_counter/5 with inline callback (api.md L613-L703 + L446-L447 MUST — callbacks registered at creation time).

create_observable_gauge(meter, name, opts)

@callback create_observable_gauge(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

SDK (OTel API MUST) — Dispatch callback for create_observable_gauge/3 without inline callback (api.md §Asynchronous Gauge creation L934-L1031).

create_observable_gauge(meter, name, callback, callback_args, opts)

@callback create_observable_gauge(
  meter :: t(),
  name :: String.t(),
  callback :: (term() -> [Otel.API.Metrics.Measurement.t()]),
  callback_args :: term(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

SDK (OTel API MUST) — Dispatch callback for create_observable_gauge/5 with inline callback (api.md L934-L1031 + L446-L447 MUST).

create_observable_updown_counter(meter, name, opts)

@callback create_observable_updown_counter(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

SDK (OTel API MUST) — Dispatch callback for create_observable_updown_counter/3 without inline callback (api.md §Asynchronous UpDownCounter creation L1176-L1277).

create_observable_updown_counter(meter, name, callback, callback_args, opts)

@callback create_observable_updown_counter(
  meter :: t(),
  name :: String.t(),
  callback :: (term() -> [Otel.API.Metrics.Measurement.t()]),
  callback_args :: term(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

SDK (OTel API MUST) — Dispatch callback for create_observable_updown_counter/5 with inline callback (api.md L1176-L1277 + L446-L447 MUST).

create_updown_counter(meter, name, opts)

@callback create_updown_counter(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

SDK (OTel API MUST) — Dispatch callback for create_updown_counter/3 (api.md §UpDownCounter creation L1084-L1115).

enabled?(instrument, opts)

@callback enabled?(
  instrument :: Otel.API.Metrics.Instrument.t(),
  opts :: Otel.API.Metrics.Instrument.enabled_opts()
) :: boolean()

SDK (OTel API SHOULD) — Dispatch callback for enabled?/2 (api.md §General operations — Enabled, L475-L495).

record(instrument, value, attributes)

@callback record(
  instrument :: Otel.API.Metrics.Instrument.t(),
  value :: number(),
  attributes :: %{required(String.t()) => primitive_any()}
) :: :ok

SDK (OTel API MUST) — Dispatch callback for record/3 (api.md §Counter Add L545-L598 / §Histogram Record L781-L826 / §Gauge Record L876-L915 / §UpDownCounter Add L1118-L1156).

register_callback(meter, instruments, callback, callback_args, opts)

@callback register_callback(
  meter :: t(),
  instruments :: [Otel.API.Metrics.Instrument.t()],
  callback :: (term() ->
                 [
                   {Otel.API.Metrics.Instrument.t(),
                    Otel.API.Metrics.Measurement.t()}
                 ]),
  callback_args :: term(),
  opts :: Otel.API.Metrics.Instrument.register_callback_opts()
) :: registration()

SDK (OTel API MUST) — Dispatch callback for register_callback/5 (api.md L408-L410 — callback creation for async instruments).

unregister_callback(state)

@callback unregister_callback(state :: term()) :: :ok

SDK (OTel API MUST) — Dispatch callback for unregister_callback/1 (api.md L419-L420 — undo callback registration).

Functions

create_counter(meter, name, opts \\ [])

@spec create_counter(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

Application (OTel API MUST) — "Counter creation" (api.md L510-L542). Dispatches to dispatcher_module.create_counter/3.

create_gauge(meter, name, opts \\ [])

@spec create_gauge(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

Application (OTel API MUST) — "Gauge creation" (api.md L852-L872). Dispatches to dispatcher_module.create_gauge/3.

create_histogram(meter, name, opts \\ [])

@spec create_histogram(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

Application (OTel API MUST) — "Histogram creation" (api.md L746-L777). Dispatches to dispatcher_module.create_histogram/3.

create_observable_counter(meter, name, opts \\ [])

@spec create_observable_counter(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

Application (OTel API MUST) — "Async Counter creation" without inline callback (api.md L613-L703). Dispatches to dispatcher_module.create_observable_counter/3.

create_observable_counter(meter, name, callback, callback_args, opts)

@spec create_observable_counter(
  meter :: t(),
  name :: String.t(),
  callback :: (term() -> [Otel.API.Metrics.Measurement.t()]),
  callback_args :: term(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

Application (OTel API MUST) — "Async Counter creation" with inline callback (api.md L613-L703). callback is a 1-arity function receiving callback_args and returning [Measurement.t()] per spec L441-L442 list-return form. Dispatches to dispatcher_module.create_observable_counter/5.

create_observable_gauge(meter, name, opts \\ [])

@spec create_observable_gauge(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

Application (OTel API MUST) — "Async Gauge creation" without inline callback (api.md L934-L1031). Dispatches to dispatcher_module.create_observable_gauge/3.

create_observable_gauge(meter, name, callback, callback_args, opts)

@spec create_observable_gauge(
  meter :: t(),
  name :: String.t(),
  callback :: (term() -> [Otel.API.Metrics.Measurement.t()]),
  callback_args :: term(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

Application (OTel API MUST) — "Async Gauge creation" with inline callback (api.md L934-L1031). Same callback contract as create_observable_counter/5.

create_observable_updown_counter(meter, name, opts \\ [])

@spec create_observable_updown_counter(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

Application (OTel API MUST) — "Async UpDownCounter creation" without inline callback (api.md L1176-L1277). Dispatches to dispatcher_module.create_observable_updown_counter/3.

create_observable_updown_counter(meter, name, callback, callback_args, opts)

@spec create_observable_updown_counter(
  meter :: t(),
  name :: String.t(),
  callback :: (term() -> [Otel.API.Metrics.Measurement.t()]),
  callback_args :: term(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

Application (OTel API MUST) — "Async UpDownCounter creation" with inline callback (api.md L1176-L1277). Same callback contract as create_observable_counter/5.

create_updown_counter(meter, name, opts \\ [])

@spec create_updown_counter(
  meter :: t(),
  name :: String.t(),
  opts :: Otel.API.Metrics.Instrument.create_opts()
) :: Otel.API.Metrics.Instrument.t()

Application (OTel API MUST) — "UpDownCounter creation" (api.md L1084-L1115). Dispatches to dispatcher_module.create_updown_counter/3.

enabled?(instrument, opts \\ [])

@spec enabled?(
  instrument :: Otel.API.Metrics.Instrument.t(),
  opts :: Otel.API.Metrics.Instrument.enabled_opts()
) :: boolean()

Application (OTel API SHOULD) — "Enabled" (api.md §General operations — Enabled, L475-L495).

Returns whether the instrument is enabled. Per spec L493-L495 the returned value is not static — it can change over time as configuration or sampling state evolves. Instrumentation authors SHOULD call this each time before recording to have the most up-to-date response.

record(instrument, value, attributes \\ %{})

@spec record(
  instrument :: Otel.API.Metrics.Instrument.t(),
  value :: number(),
  attributes :: %{required(String.t()) => primitive_any()}
) :: :ok

Application (OTel API MUST) — Records a measurement for the given instrument.

All synchronous recording paths route through here:

The instrument carries its dispatcher module in its meter field; record/3 pattern-matches on that and dispatches to module.record/3.

register_callback(meter, instruments, callback, callback_args, opts \\ [])

@spec register_callback(
  meter :: t(),
  instruments :: [Otel.API.Metrics.Instrument.t()],
  callback :: (term() ->
                 [
                   {Otel.API.Metrics.Instrument.t(),
                    Otel.API.Metrics.Measurement.t()}
                 ]),
  callback_args :: term(),
  opts :: Otel.API.Metrics.Instrument.register_callback_opts()
) :: registration()

Application (OTel API MUST) — Registers a callback for one or more asynchronous instruments (api.md L408-L410 MUST support callback creation).

All instruments MUST belong to the same Meter (spec L455-L457). The callback is 1-arity and receives callback_args; its return is a list of {Instrument, Measurement} pairs per spec L1302-L1303 and the L452-L453 MUST that "multiple-instrument Callbacks MUST distinguish the instrument associated with each observed Measurement value".

Returns a registration() handle — pass it to unregister_callback/1 to undo the registration.

unregister_callback(arg)

@spec unregister_callback(registration :: registration()) :: :ok

Application (OTel API MUST) — Undoes a prior register_callback/5 registration (api.md L419-L420 "user MUST be able to undo registration of the specific callback").

registration is the opaque handle returned by register_callback/5. After this call the callback is no longer evaluated during collection.