@@ -378,9 +378,12 @@ flow_telemetry:
378378 tracer_provider:
379379 error_handler: default # name from error_handlers; defaults to "default"
380380 sampler:
381- type: always_on # always_on|always_off|trace_id_ratio|parent_based|attribute_matching|service
381+ type: parent_based # always_on|always_off|trace_id_ratio|parent_based|attribute_matching|service (default)
382382 ratio: 1.0 # Sampling ratio (0.0-1.0, only for trace_id_ratio)
383383 service_id: null # Custom sampler service (only for type: service)
384+ root:
385+ type: always_on # parent_based only: always_on|always_off|trace_id_ratio (default)
386+ ratio: 1.0 # root.type: trace_id_ratio only
384387 processor:
385388 type: batching # composite|memory|batching|passthrough|void|attribute_filtering|service
386389 batch_size: 512
@@ -392,18 +395,50 @@ flow_telemetry:
392395
393396| Type | Description |
394397|----------------------|---------------------------------------------------------------------------------------------------|
395- | `always_on` | Sample all traces (default) |
398+ | `always_on` | Sample all traces |
396399| `always_off` | Sample no traces |
397400| `trace_id_ratio` | Sample based on trace ID ratio |
398- | `parent_based` | Respect parent span's sampling decision |
401+ | `parent_based` | Respect the parent span's sampling decision; root spans fall back to `root` (**default**) |
399402| `attribute_matching` | Drop spans matching an attribute matcher (start-time attrs); defer the rest to a delegate sampler |
400403| `service` | Custom sampler service |
401404
402- **`attribute_matching`** is the OTel-idiomatic way to drop spans by attribute — the decision is made at span start, so a
405+ # ## `parent_based` root sampler
406+
407+ ` root` selects the sampler used for spans that have no parent — that is, requests arriving with no
408+ ` traceparent` header, or with a malformed one. It accepts `always_on` (default), `always_off`, and
409+ ` trace_id_ratio` (with `ratio`).
410+
411+ Setting `root.type : always_off` produces OpenTelemetry's `parentbased_always_off` behaviour: a trace is
412+ recorded only when an upstream service passes a sampled `traceparent`. This is the configuration to use
413+ when sampling is decided by a proxy such as nginx :
414+
415+ ` ` ` yaml
416+ flow_telemetry:
417+ tracer_provider:
418+ sampler:
419+ type: parent_based
420+ root:
421+ type: always_off
422+ ` ` `
423+
424+ Note that the sampler is global. Under `root.type : always_off`, console commands stop producing traces
425+ entirely, and so do messenger worker spans — even when the request that dispatched the message was
426+ sampled, because a worker-consumed span deliberately starts its own trace linked to the producer rather
427+ than continuing the producer's trace.
428+
429+ If you want that behaviour for HTTP only, leave `root.type` at `always_on` and use
430+ ` instrumentation.http_kernel.require_trace_context` instead.
431+
432+ # ## `attribute_matching` sampler
433+
434+ ` attribute_matching` is the OTel-idiomatic way to drop spans by attribute — the decision is made at span start, so a
403435matched span never records or reaches a processor. It only sees attributes available at span start (not end-state values
404436like status codes). It reuses the same `matcher` / `exclude` / `sources` / `cache_dir` options as the
405437[`attribute_filtering`](#attribute_filtering) processor, plus a `delegate` sampler for spans that don't match:
406438
439+ ` delegate` only accepts leaf sampler types (`always_on` | `always_off` | `trace_id_ratio`), not `parent_based` — so an
440+ ` attribute_matching` sampler cannot honour upstream sampling decisions for spans that don't match its matcher.
441+
407442` ` ` yaml
408443flow_telemetry:
409444 tracer_provider:
@@ -1073,6 +1108,10 @@ flow_telemetry:
10731108The schema replaced the `type : <name>` discriminator with a sub-block whose key matches the implementation. There is no
10741109BC shim.
10751110
1111+ **Breaking change:** the default `sampler.type` changed from `always_on` to `parent_based` (see
1112+ [TracerProvider](#tracerprovider) above). Spans whose parent carries an unsampled `traceparent` are now dropped by
1113+ default; pass `sampler.type : always_on` explicitly to restore the previous behaviour.
1114+
10761115**Before (legacy schema)**
10771116
10781117` ` ` yaml
@@ -1128,6 +1167,7 @@ flow_telemetry:
11281167 enabled: true
11291168 context_propagation: true # Extract context from incoming headers
11301169 context_propagation_query: false # Also extract from the URL query string (off by default; see note below)
1170+ require_trace_context: false # Suppress requests without a trace context (off by default; see note below)
11311171 route_naming: path # Span name / http.route source: 'path' (template, e.g. /orders/{id}; default) or 'name'
11321172 trace_controller: true # Controller body span (default ON)
11331173 trace_controller_resolution: false # controller.get_callable span (default OFF)
@@ -1178,6 +1218,18 @@ exists, so disabling `http_kernel` or excluding the path produces none.
11781218The resolution and argument toggles install service decorators only when enabled, so they add zero overhead
11791219when off.
11801220
1221+ ` require_trace_context` suppresses any request that arrives without a trace context, including its DBAL
1222+ and cache spans. It is scoped to HTTP : console commands and messenger workers are unaffected. It requires
1223+ `context_propagation : true` and the `flow-php/symfony-http-foundation-telemetry-bridge` package; without
1224+ either, the flag is silently ignored and requests are traced as if it were unset.
1225+
1226+ Use `require_trace_context : true` when a proxy such as nginx decides sampling for inbound traffic but you
1227+ still want cron jobs and workers traced. Use `sampler.root.type : always_off` when you want the
1228+ require-an-upstream-decision rule applied to every entry point in the process. The flag only checks
1229+ whether a trace context is *present*, not whether it was sampled, so it does not honour an upstream
1230+ ` sampled=0` decision on its own — that comes from the `parent_based` sampler's default. Pairing it with
1231+ `sampler.type : always_on` reintroduces traces for requests nginx explicitly declined to sample.
1232+
11811233# ### Security
11821234
11831235Decorates the request span with the authenticated user. Requires `symfony/security-core` (and
@@ -1950,8 +2002,10 @@ flow_telemetry:
19502002
19512003 tracer_provider:
19522004 sampler:
1953- type: trace_id_ratio
1954- ratio: 0.1 # Sample 10% of traces in production
2005+ type: parent_based
2006+ root:
2007+ type: trace_id_ratio
2008+ ratio: 0.1 # sample 10% of traces that originate here; upstream decisions are always honoured
19552009 processor:
19562010 type: batching
19572011 batch_size: 512
0 commit comments