@span annotation that auto-wraps a function in
:telemetry.span/3. Companion to Otel.TelemetryTracer —
the tracer turns telemetry spans into OTel spans, the
decorator removes the boilerplate of wrapping function
bodies manually.
Usage
defmodule MyApp.Calculator do
use Otel.TelemetrySpanDecorator
@span [:my_app, :calculator, :add]
def add(a, b) do
a + b
end
endCompiles to roughly:
def add(a, b) do
:telemetry.span(
[:my_app, :calculator, :add],
%{
:"code.function.name" => "MyApp.Calculator.add",
:"code.file.path" => "/abs/.../calculator.ex",
:"code.line.number" => 4
},
fn -> {a + b, %{}} end
)
endThe event prefix passed to @span must match an entry in
the Otel.TelemetryTracer's events: list — registration
is the user's responsibility.
Auto-injected attributes
Always emitted, no opt-out (aligns with the OTel code.*
registry — semantic-conventions/registry/attributes/code.md):
| Attribute | Source |
|---|---|
code.function.name | "#{inspect(env.module)}.#{name}" |
code.file.path | env.file |
code.line.number | env.line |
Optional argument / return capture
Use the keyword form to opt in:
@span event: [:my_app, :calculator, :sub], capture_io: true
def sub(a, b), do: a - bWhen enabled:
| Where | Captured | Source |
|---|---|---|
start_metadata.__args__ | %{<arg_name> => <value>} | function args, source-name keys |
stop_metadata.__result__ | function's return value | last expression |
Plain vars and default args (x \\ 1) keep their
original name; pattern-match args (%{...}, [h | t],
etc.) fall back to a positional :arg_<idx> name;
underscore-prefixed args (_ignored) keep the leading
_. The __args__ / __result__ magic names follow the
__name__ convention (mirrors __struct__, __info__)
to avoid collision with any user arg literally named
args or result.
Privacy note — when
capture_io: true, all argument values AND the return value flow into the span attribute set. Avoid on functions whose args / returns carry secrets or PII unless your collector / sampler strips them.
Searchability note —
__args__and__result__are nestedkvlist_valueAnyValue attributes — spec-correct peropentelemetry-specification/specification/common/README.mdL41-54 ("arbitrary deep nesting of values for arrays and maps is allowed") and OTLPcommon/v1/common.protoL25-51 (kvlist_valueis a first-classAnyValuevariant). Backend support for indexing inner fields varies — Tempo (LGTM 0.26.0) displays them in the span detail view but does not index them for/api/search?tags=lookup. To make a field tag-searchable on Tempo, set it as a top-level attribute inside the function body viaOtel.Trace.Span.set_attribute(Otel.Trace.current_span(), key, value).
Multi-clause functions
Place @span once before the first clause; the
decorator wraps all clauses of the same name/arity via
defoverridable + super. Pattern-matching dispatch happens
inside the wrapped function, so exactly one span is emitted
per call regardless of which clause matched.
@span [:my_app, :calculator, :sign]
def sign(0), do: :zero
def sign(_), do: :nonzeroSpan shape
name: derived from event prefix ([:a, :b]→"a.b") — same convention asOtel.TelemetryTracer.kind: always:internal(no override; useOtel.Trace.with_span/4directly for non-internal kinds).status::okon normal return,:erroron exception (handled by:telemetry.span/3's:exceptionevent).attributes:code.*always;__args__/__result__only whencapture_io: true.
Implementation
Built on @on_definition + @before_compile +
defoverridable. The @on_definition callback records
each def / defp whose preceding @span attribute is
set; the @before_compile macro emits one
defoverridable + override per recorded name/arity.
Multi-clause definitions only need one override because
super(...) dispatches into the original clauses.
Summary
Types
An event prefix accepted by @span — same shape as
:telemetry.span/3's first argument.
Options accepted in the keyword form of @span.
Types
@type event_prefix() :: [atom()]
An event prefix accepted by @span — same shape as
:telemetry.span/3's first argument.
@type span_opts() :: [event: event_prefix(), capture_io: boolean()]
Options accepted in the keyword form of @span.
:event— required event prefix.:capture_io— whentrue, includes__args__in start metadata and__result__in stop metadata. Defaults tofalse.