Impact. Propagator
(Impact v0.0.1)
Copy Markdown
Cross-process OpenTelemetry context propagation.
The BEAM has no shared memory between processes, so the OTel context (the
active span + baggage from Impact.Context.put/1) does not automatically
travel across GenServer.call/3, Task.async/1, or Oban job boundaries. If
you want the spans on the other side to be children of the calling trace
instead of orphans, you have to carry the context explicitly.
This module wraps the common patterns.
GenServer.call across a process boundary
# Caller side
def chat(session_id, message) do
ctx = Impact.Propagator.current()
GenServer.call(via(session_id), {:chat, message, ctx})
end
# GenServer side
def handle_call({:chat, message, ctx}, _from, state) do
Impact.Propagator.attach(ctx)
Impact.trace [type: :task, name: "agent.loop"] do
...
end
endTask.start with context
ctx = Impact.Propagator.current()
Task.start(fn ->
Impact.Propagator.with_ctx(ctx, fn ->
do_async_work()
end)
end)Oban jobs
# Enqueue side — inject W3C textmap into job args
args = Map.put(args, :_otel, Impact.Propagator.inject())
MyWorker.new(args) |> Oban.insert()
# Worker side — extract on perform
def perform(%Oban.Job{args: args}) do
Impact.Propagator.extract_and_attach(args["_otel"])
...
endWhy two carrier formats
Two carrier formats are exposed, fit for different transports:
- Opaque context term (
current/0,attach/1) — cheap, no encoding cost, but only safe to pass via a BEAM message or capture in a closure that runs in the same node. - W3C textmap (
inject/0,extract_and_attach/1) —traceparent+tracestateHTTP-header-style strings, serializable into JSON / DB columns / Oban job args / outbound HTTP headers.
Summary
Functions
Attach ctx to the current process. Returns the previous context token,
which the caller can discard or pass to OpenTelemetry.Ctx.detach/1 to
restore later.
Return the current OTel context (opaque term).
Extract OTel context from a textmap (e.g. an Oban job arg map) and install it on the current process. No-op when the input is nil or empty.
Serialize the current OTel context as W3C textmap headers. Returns a plain
string-keyed map containing "traceparent" (and optionally "tracestate"),
suitable for storing in Oban job args, JSON columns, or outbound HTTP
headers.
Run fun inside ctx, restoring the previous context on exit (even if
fun raises). Preferred pattern for Task.start/Task.async closures
because it prevents context leakage into long-lived processes.
Functions
@spec attach(:otel_ctx.t()) :: :ok
Attach ctx to the current process. Returns the previous context token,
which the caller can discard or pass to OpenTelemetry.Ctx.detach/1 to
restore later.
Note: attach does not restore the previous context on its own. Use
with_ctx/2 if you want auto-restore semantics (recommended inside Task).
@spec current() :: :otel_ctx.t()
Return the current OTel context (opaque term).
@spec extract_and_attach(map() | nil) :: :ok
Extract OTel context from a textmap (e.g. an Oban job arg map) and install it on the current process. No-op when the input is nil or empty.
Serialize the current OTel context as W3C textmap headers. Returns a plain
string-keyed map containing "traceparent" (and optionally "tracestate"),
suitable for storing in Oban job args, JSON columns, or outbound HTTP
headers.
Returns %{} when no context is active.
@spec with_ctx(:otel_ctx.t(), (-> any())) :: any()
Run fun inside ctx, restoring the previous context on exit (even if
fun raises). Preferred pattern for Task.start/Task.async closures
because it prevents context leakage into long-lived processes.