B3 Propagation Specification

repository·master·Indexed 20 days ago

https://github.com/openzipkin/b3-propagation

A specification for trace context propagation using B3 headers to enable distributed tracing. It defines the encoding and transmission of TraceId, SpanId, ParentSpanId, and sampling decisions (Accept, Deny, Debug, Defer) across service boundaries via multiple header HTTP encoding, single header HTTP encoding, gRPC custom metadata, and JMS.

Tokens
2.8K
Snippets
6
Records
14
Agent score
20%

What's inside b3-propagation

  1. What is B3 Propagation?

    master
    B3 Propagation is a specification for trace context propagation across service boundaries using the b3 header or headers starting with x-b3-. It allows distributed tracing systems (like Zipkin) to collect all activity originating from the same root by passing identifiers through service calls (e.g., via HTTP headers).
  2. B3 Trace Identifiers

    master

    B3 uses opaque identifiers to position operations in a trace tree:

    • TraceId: A 64 or 128-bit identifier representing the overall ID of the trace. Every span in a trace shares this ID.
    • SpanId: A 64-bit identifier indicating the position of the current operation in the trace tree.
    • ParentSpanId: A 64-bit identifier indicating the position of the parent operation. This must be absent if the span is the root of the trace tree.
  3. Use the B3 Single Header format

    master

    The b3 single header is a hyphen-delimited string used to propagate trace context. It is the recommended format for JMS and is designed to be compatible with W3C tracestate.

    Format: b3={TraceId}-{SpanId}-{SamplingState}-{ParentSpanId}

    • TraceId: 32 or 16 lower-hex characters (Required unless only propagating sampling state).
    • SpanId: 16 lower-hex characters (Required unless only propagating sampling state).
    • SamplingState: A single hex character (Optional).
    • ParentSpanId: 16 lower-hex characters (Optional).

    Examples:

    • Full context: b3: 80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90
    • Debug trace: b3: 80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-d
    • Deny decision only: b3: 0
    b3: 80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90
  4. How B3 trace context propagation works

    master

    The most common use case is a client injecting trace context into an RPC request and a server extracting it. This ensures both sides of an operation appear in the same node of the trace tree.

    Typical Flow (Multiple Header Encoding):

    1. Client Tracer has a TraceContext (TraceId, SpanId, ParentSpanId, Sampling decision).
    2. Client performs Inject operation, creating HTTP headers:
      • X-B3-TraceId
      • X-B3-ParentSpanId
      • X-B3-SpanId
      • X-B3-Sampled
    3. Server receives the request and performs Extract to reconstruct the TraceContext.

    NoOp Tracing: If a service (like a proxy) wants to forbid tracing (e.g., for /health endpoints), it can send a sampling decision independently (e.g., X-B3-Sampled: 0). The receiver then creates a NoOp trace context to minimize overhead.

       Client Tracer                                                  Server Tracer     
    ┌───────────────────────┐                                       ┌───────────────────────┐
    │                       │                                       │                       │
    │   TraceContext        │          Http Request Headers         │   TraceContext        │
    │ ┌───────────────────┐ │         ┌───────────────────┐         │ ┌───────────────────┐ │
    │ │ TraceId           │ │         │ X-B3-TraceId      │         │ │ TraceId           │ │
    │ │                   │ │         │                   │ │         │ │                   │ │
    │ │ ParentSpanId      │ │         │ X-B3-ParentSpanId │         │ │ ParentSpanId      │ │
    │ │                   ├─┼────────>│                   ├─────────┼>│                   │ │
    │ │ SpanId            │ │         │ X-B3-SpanId       │ │         │ │ SpanId            │ │
    │ │                   │ │         │                   │ │         │ │                   │ │
    │ │ Sampling decision │ │         │ X-B3-Sampled      │ │         │ │ Sampling decision │ │
    │ └───────────────────┘ │         └───────────────────┘         │ └───────────────────┘ │
    │                       │                                       │                       │
    └───────────────────────┘                                       └───────────────────────┘
  5. Understand the B3 single header format

    master

    The B3 single header format is a positional encoding designed to be space-efficient and easy to parse. It maps multiple B3 fields into a single string value. This format can be used as a standalone b3 header or as the value within the W3C tracestate field.

    The positional mapping follows this pattern: b3={traceId}-{spanId}-{sampledOrDebug}-{parentSpanId}

    Note that the last two fields (sampledOrDebug and parentSpanId) are optional. The traceId and spanId are the only mandatory fields.

    b3={x-b3-traceid}-{x-b3-spanid}-{if x-b3-flags 'd' else x-b3-sampled}-{x-b3-parentspanid}
  6. B3 Sampling States

    master

    Sampling is applied consistently per-trace. Once a decision is made, the same value should be sent downstream so that either all spans sharing a trace ID are collected, or none are.

    Valid sampling states:

    • Defer: The decision is unknown. Used when trace identifiers are set by a proxy that doesn't send data to Zipkin (e.g., pre-provisioning IDs). In most encodings, this is represented by the absence of a sampling header.
    • Deny: Do not sample or record. Used for probabilistic rate limiting or to prevent specific paths (like health checks) from generating traces. Can be sent alone to avoid generating IDs.
    • Accept: Sample or record. Used to ensure certain paths are always traced. Spans should be reported to Zipkin.
    • Debug: Force trace. An emphasized 'Accept' decision that implies Accept and additionally reports Span.debug = true for each span. Often used for production troubleshooting via tools like curl.
  7. Use B3 with JMS (Java Message Service)

    master

    JMS constraints disallow headers prefixed with X-B3-. Therefore, you must use the single header format (b3) when propagating context via JMS.

    Because messaging spans typically do not share a SpanId, it is encouraged to omit the ParentSpanId field when using this format in JMS.

  8. Prevent unnecessary tracing with B3 Deny decisions

    master

    To prevent overwhelming a collector or to stop a proxy (like Envoy) from starting new traces for trace-reporting traffic, you can send an explicit Deny decision.

    It is recommended to use the single header format b3: 0 for this purpose. This ensures that even if a proxy is involved, the decision to not trace is propagated clearly.

    b3: 0
  9. Encode B3 trace context using the single header format

    master

    Depending on the state of the span (sampled, debug, or root), the B3 single header string will vary in length. Use the following patterns to construct or parse the header:

    Standard Scenarios

    • Full context (with parent): b3={traceId}-{spanId}-{sampled}-{parentSpanId}
    • Sampled root span: b3={traceId}-{spanId}-1
    • Not yet sampled root span (no decision): b3={traceId}-{spanId}
    • Debug RPC child span: b3={traceId}-{spanId}-d-{parentSpanId}

    Propagation Hints (Sampling only)

    You can propagate only a sampling decision by omitting trace identifiers:

    • Don't sample: b3: 0
    • Sampled: b3: 1
    • Debug: b3: d

    Examples

    • Multi-header to single header conversion:
      • X-B3-TraceId: 80f198ee56343ba864fe8b2a57d3eff7
      • X-B3-SpanId: e457b5a2e4d86bd1
      • X-B3-Sampled: 1
      • X-B3-ParentSpanId: 05e3ac9a4f6e3b90 Becomes: b3: 80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90
    • W3C tracestate usage: tracestate: b3=80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90
    b3: 80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90
  10. Use the 'b3' header for JMS and non-HTTP transports

    master

    When working with protocols like JMS (Java Message Service), standard B3 headers (e.g., X-B3-TraceId) can cause issues because JMS requires header names to follow Java naming conventions, which excludes hyphens and dots.

    To avoid complex mapping or incompatible naming patterns (like x_HYPHEN_b3_HYPHEN_traceid), use the single b3 header. This provides a consistent, hyphen-free format that works across both HTTP and messaging transports, facilitating a smoother transition to W3C Trace Context.

  11. Propagate B3 via gRPC Custom Metadata

    master

    B3 attributes can be propagated in gRPC using ASCII headers within the request's Custom Metadata. The encoding follows the same rules as HTTP headers, but header names are explicitly or implicitly down-cased.

    For example, the HTTP header X-B3-ParentSpanId: 0020000000000001 becomes the gRPC metadata key x-b3-parentspanid with the value 0020000000000001.