Skip to content

Commit ba52c96

Browse files
committed
fix: don't wrap the package's own tracing listeners in listener spans
TracingEventDispatcher::makeListener() wrapped every non-wildcard, non-queued listener in EventListenerTracer::trace(), including the package's own instrumentation listeners (DatabaseQueryListener, and the queue listener added next). That is wrong for two reasons: - it records noise `listener.LaravelTrace\LaravelTrace\Tracing\...` spans around internal plumbing and reparents the real span (`database.query`) under that wrapper instead of the request span; - the wrapper snapshots the trace context before the listener runs and re-applies it on close, so an instrumentation listener that deliberately mutates the ambient context (restoring/clearing it) has that change silently undone. Skip wrapping when the listener class lives in this namespace. Factor the listener-class resolution out of isQueuedListener() into listenerClass() so both guards share it.
1 parent ead9e5e commit ba52c96

1 file changed

Lines changed: 36 additions & 6 deletions

File tree

src/Tracing/TracingEventDispatcher.php

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ public function makeListener(
2929
$wildcard,
3030
);
3131

32-
if ($wildcard || $this->isQueuedListener($listener)) {
32+
if (
33+
$wildcard
34+
|| $this->isQueuedListener($listener)
35+
|| $this->isInternalListener($listener)
36+
) {
3337
return $callable;
3438
}
3539

@@ -49,11 +53,7 @@ public function makeListener(
4953
*/
5054
private function isQueuedListener(mixed $listener): bool
5155
{
52-
$class = match (true) {
53-
is_string($listener) => $this->parseClassCallable($listener)[0],
54-
is_array($listener) => $listener[0],
55-
default => null,
56-
};
56+
$class = $this->listenerClass($listener);
5757

5858
if ($class === null) {
5959
return false;
@@ -62,6 +62,36 @@ private function isQueuedListener(mixed $listener): bool
6262
return $this->handlerShouldBeQueued($class);
6363
}
6464

65+
/**
66+
* The package's own queue/database instrumentation listeners manage the
67+
* trace context themselves. Wrapping them in a listener span would make
68+
* the wrapper re-apply the context snapshot it captured before the
69+
* listener ran, undoing the context the listener deliberately cleared
70+
* and leaking queue execution context after every job.
71+
*
72+
* @param Closure|string|array{class-string, string} $listener
73+
*/
74+
private function isInternalListener(mixed $listener): bool
75+
{
76+
$class = $this->listenerClass($listener);
77+
78+
return $class !== null
79+
&& str_starts_with($class, __NAMESPACE__.'\\');
80+
}
81+
82+
/**
83+
* @param Closure|string|array{class-string, string} $listener
84+
* @return class-string|null
85+
*/
86+
private function listenerClass(mixed $listener): ?string
87+
{
88+
return match (true) {
89+
is_string($listener) => $this->parseClassCallable($listener)[0],
90+
is_array($listener) => $listener[0],
91+
default => null,
92+
};
93+
}
94+
6595
private function listenerName(mixed $listener): string
6696
{
6797
if (is_string($listener)) {

0 commit comments

Comments
 (0)