One unit of traced work, in the shape Datadog receives it.
Almost every field on this struct is a field the agent is sent verbatim,
which is what makes it worth asserting on directly — DDTrace.Test's
matchers run against the values that go on the wire, with one exception:
the trace-level markings below are written when the trace is complete and
its chunk is on its way to the agent, which is after a span is captured.
Assert on those against the chunk the agent receives.
Spans are opened by DDTrace.start_span/2 and closed by
DDTrace.finish_span/2; this module is the shape they have in between.
Wire fields
trace_id— the 128-bit trace this span belongs to. Only its low 64 bits travel in thetrace_idfield; the upper half travels as the_dd.p.tidtag on the first span of the chunk.span_id— this span's own 63-bit identifier.parent_id— the enclosing span's identifier,0for a trace root.name— the operation, e.g."db.query"; the same across every instance of that operation.resource— what the operation acted on, e.g."SELECT * FROM orders"; defaults to the name.service— the service the work is attributed to.type— the Datadog span type, e.g."web"or"sql".start— when the span opened, in nanoseconds since the epoch.duration— how long it took, in nanoseconds;niluntil finished.error—1when the span failed,0otherwise.meta— string tags. A span closed by its parent carriesdd_trace_ex.abandonedhere.metrics— numeric tags.
Trace-level markings
Three keys say something about the trace rather than about the span
carrying them: _dd.p.tid, _dd.p.dm and the _sampling_priority_v1
metric. The agent reads them off the first span of each chunk, so that is
where the tracer writes them — when the trace completes, not when the
span opens, since a trace's sampling decision can change while it runs.
Everything else
Two fields exist only inside the tracer and never reach the agent:
finished?, which is what makes closing a span twice harmless, and
monotonic_start, the reading duration is measured against.
Timing
start comes from the system clock and duration from the monotonic
clock, so a clock adjustment between the two can shorten neither. Both
are overridden by the start_time: and finish_time: options, which
backfill a span around work that was measured elsewhere: the duration is
then exactly the difference between the two.
Summary
Types
A reading of both clocks: system time and monotonic time, in nanoseconds.
@type t() :: %DDTrace.Span{ duration: non_neg_integer() | nil, error: 0 | 1, finished?: boolean(), meta: %{optional(String.t()) => String.t()}, metrics: %{optional(String.t()) => float()}, monotonic_start: integer(), name: String.t(), parent_id: non_neg_integer(), resource: String.t(), service: String.t() | nil, span_id: non_neg_integer(), start: integer(), trace_id: non_neg_integer(), type: String.t() | nil }