Skip to content

[FEATURE]: Support B3 single header (b3single) for extract and inject #342

Description

@wannfq

Describe the goal of the feature

Add support for B3 single-header propagation (b3: {traceid}-{spanid}-{sampled}) for both extraction and injection, alongside the existing B3 multi-header style (X-B3-*).

Proposed configuration aliases (aligned with other Datadog tracers, e.g. Java):

  • b3single (preferred for env-var lists; avoids space-splitting issues)
  • optionally also accept B3 single header if parsing can treat it as one token

Today PropagationStyle only has:

  • DATADOG
  • B3 (multi; also parsed from b3 / b3multi)
  • W3C (tracecontext)
  • BAGGAGE
  • NONE

There is no distinct B3-single style. Please add something like PropagationStyle::B3_SINGLE (name TBD) and wire it through extract/inject + DD_PROPAGATION_STYLE_* / DD_TRACE_PROPAGATION_STYLE_* parsing.

Is your feature request related to a problem?

Yes. We run Datadog tracing in Istio/Envoy, which uses dd-trace-cpp. Our mesh is configured for B3 multi so Envoy can rewrite x-b3-spanid hop-by-hop.

Many application SDKs (OpenTelemetry default B3 propagator, some dd-trace configs) still inject or prefer the single b3 header. When both single and multi headers are present, downstream extractors that prefer single can parent to a stale b3 value while Envoy has already advanced the multi headers — breaking end-to-end trace linkage across gateway → sidecar → app.

We want Istio/Envoy (via dd-trace-cpp) to be able to:

  1. Extract B3 single (b3single) in addition to B3 multi, so inbound single-header context can join the mesh trace
  2. Optionally inject B3 single when needed for interoperability — while we would typically keep inject as multi-only in our mesh to avoid the stale-single-header problem

Without first-class b3single support, setting DD_PROPAGATION_STYLE_EXTRACT=B3,b3single,tracecontext fails with UNKNOWN_PROPAGATION_STYLE because b3single is not recognized (and note that today b3 is aliased to multi, not single).

Describe alternatives you've considered

  1. Application-side only: Configure every app tracer to inject B3 multi only (b3multi) and extract both. This helps, but does not let the Envoy/Istio proxy itself accept or emit B3 single when peers still use it (e.g. Gantry/Lambda → Automat boundaries).
  2. Reuse b3 for single: Not viable in dd-trace-cpp today — parse_propagation_style maps both b3 and b3multi to PropagationStyle::B3 (multi). Changing that would be a breaking change.
  3. W3C Trace Context only: Useful long-term, but many existing services still speak B3; we need B3 single + multi interoperability during migration.

Additional context

Current parsing (src/datadog/propagation_style.cpp):

} else if (token == "b3" || token == "b3multi") {
  return PropagationStyle::B3;  // multi only
}

Docs: Datadog C++ language support currently lists B3 Multi (b3 / b3multi) but not B3 Single, unlike Java (b3single / b3 single header), Node/Go/PHP/.NET (B3 single header), Python/Ruby (b3 for single).

Envoy/Istio usage: We set these via Istio meshConfig.defaultConfig.proxyMetadata:

DD_PROPAGATION_STYLE_EXTRACT: B3,b3single,tracecontext
DD_PROPAGATION_STYLE_INJECT: B3

Happy to adjust alias naming to whatever you prefer for consistency with the shared tracer config contract — we mainly need a distinct style for single-header B3 that is not conflated with multi.

Thank you!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions