Hello, maybe you'd be able to guide us, if it is possible to handle it correctly, but:
- we are using Laravel application with the service provider which registers the tracing like this
$resource = ResourceInfo::create(
Attributes::create([
'service.name' => config('otel.service_name'),
'deployment.environment' => config('app.deployed_env'),
])
);
$transport = (new OtlpHttpTransportFactory)->create(
config('otel.endpoint'),
Protocols::contentType(config('otel.protocol')),
['X-SF-Token' => config('otel.sf_token')],
);
$tracerProvider = TracerProvider::builder()
->setResource($resource)
->addSpanProcessor(new BatchSpanProcessor(new SpanExporter($transport), SystemClock::create()))
->build();
Sdk::builder()
->setTracerProvider($tracerProvider)
->setAutoShutdown(true)
->buildAndRegisterGlobal();
$this->app->singleton(\OpenTelemetry\API\Trace\TracerInterface::class, fn () => $tracerProvider->getTracer(config('app.name')));
then we have the middleware which is like this:
{
public function handle(Request $request, Closure $next): Response
{
if (isFeatureFlagEnabled('backoffice.disable-otel-splunk-telemetry')) {
return $next($request);
}
$propagator = TraceContextPropagator::getInstance();
$parentContext = $propagator->extract(
$request->headers->all(),
\OpenTelemetry\Context\Propagation\ArrayAccessGetterSetter::getInstance(),
);
$tracer = Globals::tracerProvider()->getTracer(config('app.name'));
$route = $request->route()?->uri() ?? $request->path();
$span = $tracer
->spanBuilder($request->method().' '.$route)
->setParent($parentContext)
->setSpanKind(SpanKind::KIND_SERVER)
->startSpan();
$scope = $span->activate();
// ====== SEMANTIC CONVENTIONS (HTTP SERVER) ======
$span->setAttribute('http.method', $request->method());
$span->setAttribute('http.route', $route);
$span->setAttribute('http.target', $request->getRequestUri());
$span->setAttribute('http.scheme', $request->getScheme());
$span->setAttribute('http.host', $request->getHost());
$span->setAttribute('http.user_agent', $request->userAgent() ?? '');
$span->setAttribute('net.peer.ip', $request->ip());
try {
$response = $next($request);
$statusCode = $response->getStatusCode();
$span->setAttribute('http.status_code', $statusCode);
if ($statusCode >= 500) {
$span->setStatus(StatusCode::STATUS_ERROR);
}
// ====== Inject trace headers do response ======
$propagationSetter = new class implements \OpenTelemetry\Context\Propagation\PropagationSetterInterface
{
public function set(&$carrier, string $key, string $value): void
{
$carrier->headers->set($key, $value, false);
}
};
$propagator->inject($response, $propagationSetter, OtelContext::getCurrent());
return $response;
} catch (\Throwable $e) {
$span->recordException($e);
$span->setStatus(StatusCode::STATUS_ERROR);
throw $e;
} finally {
$span->end();
$scope->detach();
}
}
}
Everything seem to work correctly when the full request lifecycle ends, because the finally clause will be called and detach the scope. But if during the lifecycle I'll call something like dd() which is an function which prints something and calls exit(1) then the finally seem to run after the DebugScope destructor will be called and thus, since the scope has not been detached the error Scope: missing call to Scope::detach() for scope # will be returned along with the echoed/dumped data.
"message": "Scope: missing call to Scope::detach() for scope #2051, created \n\tat OpenTelemetry.Context.Context.activate(Context.php:82)\n\tat OpenTelemetry.API.Trace.Span.activate(Span.php:56)\n\tat App.Http.Middleware.OtelPropagator.handle(OtelPropagator.php:39)\n\tat Illuminate.Pipeline.Pipeline.{closure:{closure:Illuminate.Pipeline.Pipeline::carry():194}:195}(Pipeline.php:219)\n\tat Illuminate.Http.Middleware.HandleCors.handle(HandleCors.php:61)\n\tat Illuminate.Pipeline.Pipeline.{closure:{closure:Illuminate.Pipeline.Pipeline::carry():194}:195}(Pipeline.php:219)\n\tat Illuminate.Http.Middleware.TrustProxies.handle(TrustProxies.php:58)\n\tat Illuminate.Pipeline.Pipeline.{closure:{closure:Illuminate.Pipeline.Pipeline::carry():194}:195}(Pipeline.php:219)\n\tat Illuminate.Pipeline.Pipeline.then(Pipeline.php:137)\n\tat Illuminate.Foundation.Http.Kernel.sendRequestThroughRouter(Kernel.php:175)\n\tat Illuminate.Foundation.Http.Kernel.handle(Kernel.php:144)\n\tat require(index.php:52)\n\tat {main}(server.php:139)\n",
"exception": "ErrorException",
"file": "D:\\vhosts\\fifa.gop.backoffice-backend\\vendor\\open-telemetry\\context\\DebugScope.php",
It is not a breaker, but I was wondering what could be the issue here. It looks like the setAutoShutdown should handle it, but it looks like it is not, I mean the destructor is called even before the callback in register_shutdown_function is called so it does not pass the
if (self::$finalShutdownPhase && $this->fiberId !== self::currentFiberId()) {
return;
}
``` condition.
Hello, maybe you'd be able to guide us, if it is possible to handle it correctly, but:
then we have the middleware which is like this:
Everything seem to work correctly when the full request lifecycle ends, because the
finallyclause will be called and detach the scope. But if during the lifecycle I'll call something likedd()which is an function which prints something and callsexit(1)then thefinallyseem to run after theDebugScopedestructor will be called and thus, since the scope has not been detached the errorScope: missing call to Scope::detach() for scope #will be returned along with the echoed/dumped data.It is not a breaker, but I was wondering what could be the issue here. It looks like the
setAutoShutdownshould handle it, but it looks like it is not, I mean the destructor is called even before the callback inregister_shutdown_functionis called so it does not pass the