Otel.Resource (otel v0.4.1)

Copy Markdown View Source

SDK Resource (resource/sdk.md §"SDK").

Minikube hardcodes the resource to a fixed shape. The single user knob is config :otel, otp_app: :my_appservice.name derives from the atom and service.version from the loaded application's vsn (Application.spec(:my_app, :vsn), the same value as mix.exs's version: ...). The :otp_app key is itself optional; without it, both attributes fall back to the no-config defaults below. SDK identity (telemetry.sdk.*, deployment.environment) is baked in at build time. Resource merging and Schema URL are dropped — power users go to opentelemetry-erlang. The schema_url field is preserved at the data-model level for OTLP wire compliance, but stays at its default "" — there is no API to set it.

Configuration

# config/runtime.exs
config :otel, otp_app: :my_app
  • service.nameAtom.to_string(:my_app)
  • service.versionApplication.spec(:my_app, :vsn) (the version field of the loaded OTP application; matches mix.exs's version:)

When :otp_app is not configured, service.name falls back to "unknown_service" and service.version is nil (encoded as the OTLP empty AnyValue — same wire treatment in every backend, no Tempo/Mimir divergence). When :otp_app is set but the application isn't loaded yet, service.version is nil for the same reason; this is a transient state during boot that resolves once Application.ensure_all_started/1 completes.

Emitted attributes

AttributeSource
telemetry.sdk.namethis SDK's :app key from mix.exs (compile-time)
telemetry.sdk.language"elixir"
telemetry.sdk.versionthis SDK's :version (compile-time)
deployment.environmentMIX_ENV env var at SDK compile time (default "dev")
service.nameconfig :otel, otp_app: :my_app"my_app" (default "unknown_service")
service.versionApplication.spec(:my_app, :vsn) (default nil → empty AnyValue on the wire)

Reading at runtime keeps a single source of truth for service.version — the user's mix.exs. There is no :vsn knob in the SDK config, so version drift between mix.exs and the configured value is impossible. Application.spec/2 is an ETS lookup (microseconds); per project policy ("no runtime caching") new/0 recomputes on each call.

service.version falls back to nil rather than being omitted when the application isn't loaded (or :otp_app is unset). Spec convention (Recommended, not Required) and opentelemetry-erlang would omit the key entirely; minikube keeps the key with a nil value, which the OTLP encoder maps to %AnyValue{} (oneof unset) per common/README.md L50-L51 ("empty value if supported by the language") and common.proto L29-L31 ("It is valid for all values to be unspecified in which case this AnyValue is considered to be 'empty'"). Wire-level effect is uniform across backends — Tempo/Loki/Mimir all read this as null/absent, no {label=""} divergence.

deployment.environment is captured at SDK compile time from System.get_env("MIX_ENV") directly — not Mix.env/0. Mix.Tasks.Deps.Compile wraps every dep build in Mix.Dep.in_dependency (elixir/lib/mix/lib/mix/dep.ex L246-L270) which forces Mix.env(:prod) for the dep's compilation context regardless of the consuming app's MIX_ENV, so a Mix.env() call inside this module would always evaluate to :prod. Reading MIX_ENV from the OS environment bypasses that override — Mix mutates only its internal Mix.State (elixir/lib/mix/lib/mix/state.ex L65-L89), never the env var.

Read-time per attribute

AttributeRead timeWhy
telemetry.sdk.*compiledep mix.exs config only available during build
deployment.environmentcompileMIX_ENV is build-time intent; release boot doesn't export it
service.name / service.versionruntimeuser's :otp_app only set in user's runtime config; user app's vsn requires the app to be loaded

References

  • OTel Resource SDK: opentelemetry-specification/specification/resource/sdk.md
  • OTLP proto Resource: opentelemetry-proto/opentelemetry/proto/resource/v1/resource.proto
  • Semantic Conventions §service: semantic-conventions/docs/resource/service.md

Summary

Functions

Application (introspection) — Construct the SDK resource.

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()}

t()

@type t() :: %Otel.Resource{
  attributes: %{required(String.t()) => primitive_any()},
  schema_url: String.t()
}

Functions

new(opts \\ %{})

@spec new(opts :: map()) :: t()

Application (introspection) — Construct the SDK resource.

See module doc for the attribute set. service.name and service.version are read from config :otel, :otp_app and Application.spec/2 on every call (no caching), so updates take effect immediately. Caller may override any field via opts.