A trace, in the form that travels between processes.
DDTrace.current_context/0 takes one of these; parent: on
DDTrace.trace/2, DDTrace.with_trace/3 and DDTrace.start_span/2, and
DDTrace.with_context/2, put a new span underneath it:
ctx = DDTrace.current_context()
GenServer.cast(worker, {:work, item, ctx})
# in the worker
DDTrace.trace "worker.process", parent: ctx do
process(item)
endIt is plain data — integers and a string — so it survives everything a
term can travel through: a GenServer.cast, a message to another node,
a job serialized into a database.
Fields
trace_id— the 128-bit trace the new span joins.span_id— the span it becomes a child of.priority— the trace's sampling priority when the snapshot was taken.DDTrace.Prioritynames the four the tracer itself mints; the field is any integer, because an upstream service's decision is honoured exactly as it arrived even when it is one nobody here has a name for.nilwhen nothing has decided yet: a contextDDTrace.extract/1read from headers that carried no priority is undecided, and stays so until a span opened under it has to hand the trace on or ship it. A snapshot taken with a span open always has an integer.dm— what decided that priority, asDDTrace.Tags.decision_maker/0spells it on the wire, ornilwhere nothing did: a trace someone dropped is not being kept for a reason, and an undecided one is not being kept at all yet.origin— where the trace entered Datadog, when it came from a product that says so ("synthetics","rum"), ornil. OnlyDDTrace.extract/1produces one: minting an origin locally would claim the trace started somewhere it did not.propagation_tags— the_dd.p.*trace tags carried from upstream, minus the two the tracer keeps as fields of their own. Empty for a trace that started here.propagation_error— what went wrong reading or writing the tags header, asDDTrace.Tags.propagation_error/0spells it, ornilwhen nothing did.
_dd.p.tid and _dd.p.dm are fields, not entries
Both are _dd.p.* tags on the wire, and neither is ever a member of
propagation_tags: the upper half of a 128-bit trace id is part of
trace_id, and the decision maker is dm. Extraction parses them out of
the inbound header into those fields and injection derives them back, so
the map and the fields cannot disagree about what the trace is. What the
map holds is the rest — the pairs this service carries but does not read.
A snapshot is a photograph
Nothing that happens afterwards reaches it. A snapshot taken before the originating process changes the trace's sampling decision carries the decision as it was, and a process that opens spans under it ships that one — exactly as a service that received the same facts in headers would.
Summary
Types
@type t() :: %DDTrace.SpanContext{ dm: String.t() | nil, origin: String.t() | nil, priority: integer() | nil, propagation_error: String.t() | nil, propagation_tags: %{optional(String.t()) => String.t()}, span_id: non_neg_integer(), trace_id: non_neg_integer() }