You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat(flow-php/telemetry): time-based flush for batching processors
- add optional max_batch_age (monotonic hrtime) to span/metric/log
batching processors
* feat(flow-php/telemetry): derive trace id from active span; consumed messages get their own trace
- Context holds the active SpanContext instead of a standalone trace id;
root spans generate a new TraceId (OTEL-aligned)
- remove Context::withTraceId/create and the context(?TraceId) DSL
seeding; withActiveSpan takes a SpanContext
- symfony-telemetry-bundle: each consumed Messenger message is its own
trace, linked to the producer (link mode)
- symfony-telemetry-bundle: configurable worker-span link
(link_to_worker, default true) + per-message flush subscriber
* refactor(flow-php/telemetry): derive span parent solely from Context
- remove per-tracer span stack; active span lives in shared Context
- parent resolution falls through to Context::activeSpan() only
- nesting popped via Scope detach instead of stack pop
- add cross-tracer parenting + attached-context conformance tests
| `batch_size` | integer | `512` | Number of items per batch |
514
+
| `max_batch_age` | float | `null` | Max seconds before a partial batch is force-exported, measured from the first buffered signal; `null` disables time-based flush |
515
+
| `exporter` | string | - | Name of a top-level exporter (required) |
515
516
516
517
```yaml
517
518
processor:
518
519
type: batching
519
520
batch_size: 512
521
+
max_batch_age: 15.0 # export at least every 15s in long-running processes
520
522
exporter: otlp
521
523
```
522
524
525
+
By default a batch is exported only when it reaches `batch_size`, when `flush()`/`shutdown()` is called, or
526
+
when the process exits. In a request-scoped app that is fine — the request ends and the buffer drains. In a
527
+
**long-running process** (Symfony Messenger workers, daemons, or persistent runtimes such as ReactPHP,
528
+
RoadRunner, Swoole, AMP, or FrankenPHP worker mode) a low-rate signal can sit in the buffer until 512
529
+
accumulate or the process stops. Set `max_batch_age` (e.g. `15.0`) to bound how long any single signal waits
530
+
before export; leave it unset for request-scoped apps where behavior is unchanged.
531
+
532
+
> **PHP limitation — idle processes.** PHP has no background timer thread, so the age deadline is evaluated
533
+
> only when a signal is processed, not on a wall-clock schedule. A **fully idle** process — nothing ending
534
+
> spans or processing metrics/logs — cannot self-flush a partial batch on the age trigger. That residual
535
+
> case needs an external flush: the Messenger worker-event flush subscriber, a runtime loop calling
536
+
> `Telemetry::flush()`, or `shutdown()` when the process exits. The deadline is measured from a monotonic
537
+
> clock (`hrtime`), so it is immune to wall-clock adjustments.
538
+
523
539
#### composite
524
540
525
541
Combines multiple processors. Each child references its own exporter by name.
@@ -1083,21 +1099,38 @@ flow_telemetry:
1083
1099
enabled: true
1084
1100
context_propagation: true # Propagate context across message boundaries
1085
1101
propagation_style: link # How the consumer span relates to the producer span
1102
+
link_to_worker: true # Link each message trace back to the messenger:consume worker span
1086
1103
```
1087
1104
1088
1105
When `context_propagation` is enabled, `propagation_style` controls how a consumed message's span
1089
1106
relates to the producing (publishing) span:
1090
1107
1091
-
- `link`(default) — the consumer span stays in the worker's own trace (under the `messenger:consume`
1092
-
console span) and carries a span link back to the producer span. Producer and consumer get separate,
1093
-
clean traces connected by a link. Recommended for decoupled, batch, or long-delay queues, where
1094
-
continuing the trace would otherwise absorb the entire queue wait into a single span's duration.
1108
+
- `link`(default) — each consumed message is the **root of its own trace** and carries a span link back
1109
+
to the producer span (per the OpenTelemetry messaging conventions). Producer and consumer get separate,
1110
+
clean traces connected by a link, and a long-running worker no longer collapses every message into one
1111
+
trace. Recommended for decoupled, batch, or long-delay queues, where continuing the trace would otherwise
1112
+
absorb the entire queue wait into a single span's duration.
1095
1113
- `continue`— the consumer span adopts the producer's trace and becomes its child, so
1096
1114
publish → queue → consume is one continuous distributed trace. Fine for fast, 1:1 processing.
1097
1115
1098
1116
`propagation_style`has no effect when `context_propagation` is `false` (there is nothing to relate to).
1099
1117
The producer side is identical in both modes — the telemetry stamp is always written on dispatch.
1100
1118
1119
+
`link_to_worker`(default `true`) adds a span link from each consumed message's trace back to the active
1120
+
`messenger:consume`worker span, so you can pivot from a message trace to the worker that processed it.
1121
+
Set it to `false` to omit the link (e.g. if the consume command is not traced).
1122
+
1123
+
Buffered telemetry is flushed **after each handled or failed message** while the worker keeps running, so
1124
+
consumer-side traces/logs/metrics export promptly instead of only when the `messenger:consume` worker
1125
+
stops. (Without it, signals emitted inside handlers sit in the batching processors — default batch size
1126
+
512 — and stay invisible until the buffer fills or the worker exits.) Failures flush too, so error spans
1127
+
and exception logs are visible even when the message is retried or sent to the failure transport. This is
1128
+
independent of `context_propagation` / `propagation_style`.
1129
+
1130
+
Flushing per message means one exporter round-trip per message. For high-throughput workers, set
1131
+
[`max_batch_age`](#batching) on the batching processor so the batch coalesces across messages and a single
1132
+
idle worker still exports on a time bound rather than per message.
0 commit comments