Traces and Spans
Before you dive in to the “traces and spans” concepts outlined below, review these concepts in the official OpenTelemetry documentation, found here.
The ExportTraceServiceRequest
All OpenTelemetry collectors receive OTLP traces from applications in the form
of an ExportTraceServiceRequest
. This is a well known message, and its
structure can be found in the official
opentelemetry-proto repository. The diagram below provides the general structure of
this message (some properties omitted for brevity).
When the OpenTelemetry Collector exports traces to an endpoint, it typically batches those traces up and
sends them by resource (eg. a microservice), hence the list of resources in an ExportTraceServiceRequest
.
Each InstrumentationScopeSpan
will be associated with one resource, and the same goes for the Spans
that are associated with an instrumentation scope.
For each Span
, then, you know what instrumentation library was used to create the Span
, and you also
know which Resource
produced the Span
.
What are Traces and Spans?
A trace is really just an ID attached to each individual telemetry signal that your application is producing that ties a request together. Think of it like the execution of a workflow, where the trace ID of the workflow is added to each step.
Spans represent the individual steps within the workflow. They contain numerous properties, such as when they started and ended, whether or not they completed successfully, and whether they belong to a parent. The full structure of the span can again be found in the official opentelemetry-proto repository.
In total, including the instrumentation scope properties and the resource properties, there are 25 properties associated with every span. You can find OddDotNet’s span definition here and in the PropertyFilter. Here’s a code snippet for reference:
message Where {
oneof value {
PropertyFilter property = 1;
OrFilter or = 2;
odddotnet.proto.common.v1.InstrumentationScopeFilter instrumentationScope = 3;
odddotnet.proto.resource.v1.ResourceFilter resource = 4;
odddotnet.proto.common.v1.StringProperty instrumentationScopeSchemaUrl = 5;
odddotnet.proto.common.v1.StringProperty ResourceSchemaUrl = 6;
}
}
message PropertyFilter {
oneof value {
odddotnet.proto.common.v1.ByteStringProperty traceId = 1;
odddotnet.proto.common.v1.ByteStringProperty spanId = 2;
odddotnet.proto.common.v1.StringProperty traceState = 3;
odddotnet.proto.common.v1.ByteStringProperty parentSpanId = 4;
odddotnet.proto.common.v1.StringProperty name = 5;
SpanKindProperty kind = 6;
odddotnet.proto.common.v1.UInt64Property startTimeUnixNano = 7;
odddotnet.proto.common.v1.UInt64Property endTimeUnixNano = 8;
odddotnet.proto.common.v1.KeyValueProperty attribute = 9;
odddotnet.proto.common.v1.UInt32Property droppedAttributesCount = 10;
EventFilter event = 11;
odddotnet.proto.common.v1.UInt32Property droppedEventsCount = 12;
LinkFilter link = 13;
odddotnet.proto.common.v1.UInt32Property droppedLinksCount = 14;
StatusFilter status = 15;
odddotnet.proto.common.v1.UInt32Property flags = 16;
}
}