Commit 38e07e6
feat(Instrumentation/Messenger): implement worker and middleware instrumentation (#173)
* chore(messenger): add trace parent in dispatched messages (AMQP)
* chore(messenger): use Messenger events to start/end span for instrumentation
* remove strict type, merge start/end span into 1 subscriber, close span on error and on message handled
* propagation not related to AMQP + read incoming trace (async context)
* rename event subscriber
* chore(messenger): use Messenger events to start/end span for instrumentation
* chore(messenger): add trace parent in dispatched messages (AMQP)
* remove strict type, merge start/end span into 1 subscriber, close span on error and on message handled
* propagation not related to AMQP + read incoming trace (async context)
* rename event subscriber
* fix(messenger): clean up worker subscriber and add functional tests
Fix several issues in the WorkerMessageEventSubscriber introduced by PR #173:
- Replace SDK Span import with API Span to respect API/SDK separation
- Implement InstrumentationTypeInterface for consistency with other subscribers
- Add event priorities (10000/-10000) to wrap all other processing
- Add messaging semantic convention attributes (operation.type, destination.name)
- Include message class name in span name for better trace readability
- Remove stale imports and duplicate propagation middleware service definition
- Clean up propagation middleware when messenger tracing is disabled
- Add PHPStan baseline entries for untyped $carrier interface params
- Add functional tests for worker message handled, failed, and attribute mode
- Reorganize messenger tests into Messenger/ subdirectory
- Disable retry on test transport to isolate worker span assertions
* test(messenger): add tests for transport tracing and propagation middleware
Cover TraceableMessengerTransport (get/ack/reject spans + TransportException
error recording) and AddStampForPropagationMiddleware (stamp skip, no-scope
passthrough, active-scope injection).
* fix(messenger): use recordException() instead of manual attributes
Align WorkerMessageEventSubscriber.endSpanOnError() with every other
subscriber in the codebase (HttpKernel, Console, Doctrine, Mailer,
TransportTracer) by using $span->recordException() which creates a
proper OTel exception event with exception.type, exception.message,
and exception.stacktrace per the OTel semantic conventions.
Update tests to assert on span events rather than span attributes.
* fix(messenger): unwrap HandlerFailedException in error spans
When Symfony Messenger handlers fail, the exception is wrapped in a
HandlerFailedException (or other WrappedExceptionsInterface implementors
like DelayedMessageHandlingException). Without unwrapping, the span
records the generic wrapper message instead of the actual root causes.
Now each nested exception is recorded as a separate span event via
recordException(), matching how Sentry handles these wrapped exceptions.
* fix(console): make messenger:consume exclusion configurable
Replace the hardcoded $notSupportedCommands array and isNotSupported()
method with a default value on the existing exclude_commands config node.
messenger:consume is now the default entry in console.tracing.exclude_commands,
following the same pattern as Sentry's configurable excluded_commands. Users
can override the list or clear it entirely via configuration.
* test(messenger): add unit test for TraceStampPropagator
Cover all branches of set(), get(), and keys() methods:
- Valid envelope carrier for set/get/keys
- Invalid carrier throws InvalidArgumentException
- Non-traceparent key is ignored (set) or returns null (get)
- Missing TraceStamp returns null from get()
Closes the 34.78% patch coverage gap reported by Codecov.
* fix(messenger): propagate tracestate alongside traceparent
The W3C Trace Context spec defines two headers: traceparent and
tracestate. Previously only traceparent was propagated, silently
dropping vendor-specific trace context (Datadog, AWS X-Ray, etc.).
Add optional traceState property to TraceStamp and update
TraceStampPropagator to handle both keys in set(), get(), and keys().
When tracestate arrives after traceparent, the existing stamp is
replaced with one carrying both values.
* fix(messenger): namespace bus.name attribute as symfony.messenger.bus.name
bus.name is not a standard OTel semantic convention. Rename to
symfony.messenger.bus.name to follow the existing codebase pattern
(symfony.console.*, symfony.kernel.*).
Only changes the worker subscriber attribute; the pre-existing
bus.name in TraceableMessengerStack is left unchanged (out of scope).
* fix(messenger): add scope leak safety in worker subscriber
If startSpan fires but neither endSpanWithSuccess nor endSpanOnError
fires (e.g. worker killed, unhandled error in another subscriber),
the OTel context scope leaks into subsequent messages.
Now startSpan checks for a lingering scope at the beginning of each
message and cleans it up: detaches the scope, marks the orphaned span
as ERROR, and ends it. This prevents context pollution across messages.
* feat(messenger): add retry awareness, attribute instrumentation, and namespace span attributes
- Namespace middleware span attributes (bus.name, event.category, event.current)
under symfony.messenger.* prefix for consistency with worker subscriber
- Add symfony.messenger.will_retry and symfony.messenger.retry_count attributes
to failure spans so operators can distinguish retriable from terminal failures
- Support #[Traceable] attribute on message classes for attribute-based
instrumentation mode, with custom tracer selection via TracerLocatorPass
* refactor(messenger): improve type safety, observability, and test coverage
Mark all new types as final for consistency with existing codebase.
Register TraceStampPropagator as a shared DI service instead of inline
construction. Default instrumentationType to Auto, add safe tracer
locator fallback, nullable logger consistency, debug logging on skipped
messages and null scopes, and remove dead commented-out code. Add
end-to-end propagation test, WorkerMessageEventSubscriber unit tests,
and PropagatorFactory test.
* fix(http-client): use lowercase header key for Content-Length lookup
Symfony's getHeaders() normalizes header names to lowercase, so the
Content-Length check was unreachable. Access the first array element
since headers are returned as arrays.
* fix(messenger): use explicit scope tracking and record errors on middleware spans
TraceableMessengerStack now tracks its own scope, span, and parent context
instead of probing global context storage — fixing scope leaks where earlier
middleware spans were never ended. TraceableMessengerMiddleware catches
exceptions and records them with STATUS_ERROR on the active span, aligning
with every other instrumentation in the bundle.
* fix(messenger): remove erroneous kernel.reset tag from transport factory
TraceableMessengerTransportFactory was tagged `kernel.reset` with
method `reset`, but the class has no such method. Symfony's
ServicesResetter invokes the declared method on every tagged service
between worker iterations, so `messenger:consume` threw as soon as a
retry (or any post-handler reset) ran:
Attempted to call an undefined method named "reset" of class
"...\TraceableMessengerTransportFactory".
The tag was introduced with the original messenger tracing (6f3c032)
and never had a corresponding method — likely speculative, possibly
confused with messenger transports that sometimes implement
ResetInterface.
Removal is preferred over adding an empty reset(): the factory's
dependencies (inner TransportFactory, TracerInterface, LoggerInterface)
are immutable DI services, createTransport() returns a fresh
TraceableMessengerTransport without retaining a reference, and
supports() is pure — there is no per-message state to clear. An empty
reset() would silence the error but imply a ResetInterface-style
contract the class does not fulfill and mislead anyone later adding
mutable state.
---------
Co-authored-by: Gaël Reyrol <me@gaelreyrol.dev>1 parent e2701b3 commit 38e07e6
40 files changed
Lines changed: 1886 additions & 82 deletions
File tree
- src
- DependencyInjection
- Compiler
- Instrumentation/Symfony
- Console
- HttpClient
- Messenger
- OpenTelemetry/Context/Propagator
- Resources/config
- tests
- Functional
- Application
- config/packages
- src
- MessageHandler
- Message
- Instrumentation
- Messenger
- Unit
- DependencyInjection
- Compiler
- Instrumentation/Symfony/Messenger
- OpenTelemetry/Context/Propagator
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
28 | | - | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
| 65 | + | |
65 | 66 | | |
66 | 67 | | |
67 | 68 | | |
| |||
127 | 128 | | |
128 | 129 | | |
129 | 130 | | |
130 | | - | |
| 131 | + | |
131 | 132 | | |
132 | | - | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
133 | 138 | | |
134 | 139 | | |
135 | 140 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
15 | 21 | | |
16 | 22 | | |
17 | 23 | | |
| |||
90 | 96 | | |
91 | 97 | | |
92 | 98 | | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
93 | 117 | | |
94 | 118 | | |
95 | 119 | | |
| |||
Lines changed: 4 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
31 | 34 | | |
32 | 35 | | |
33 | 36 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
26 | 30 | | |
27 | 31 | | |
28 | 32 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| 107 | + | |
107 | 108 | | |
108 | 109 | | |
109 | 110 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
283 | 283 | | |
284 | 284 | | |
285 | 285 | | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
286 | 289 | | |
287 | 290 | | |
288 | 291 | | |
| |||
Lines changed: 3 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
59 | 62 | | |
60 | 63 | | |
61 | 64 | | |
| |||
Lines changed: 2 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
138 | 138 | | |
139 | 139 | | |
140 | 140 | | |
141 | | - | |
142 | | - | |
| 141 | + | |
| 142 | + | |
143 | 143 | | |
144 | 144 | | |
145 | 145 | | |
| |||
Lines changed: 44 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
0 commit comments