From db551415352cebce6cbeb79308604df97931b847 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 30 Apr 2026 13:02:42 +0530 Subject: [PATCH] Use initialized spans for isolated error export --- app/cli.php | 54 +++++++++-------- app/init/worker/message.php | 60 ++++++++++--------- app/realtime.php | 54 +++++++++-------- app/worker.php | 44 ++++++++------ .../Http/VectorsDB/Embeddings/Text/Create.php | 43 +++++++------ 5 files changed, 143 insertions(+), 112 deletions(-) diff --git a/app/cli.php b/app/cli.php index 888c5f0bdb..576cf5c4fd 100644 --- a/app/cli.php +++ b/app/cli.php @@ -35,6 +35,7 @@ use Utopia\Queue\Publisher; use Utopia\Queue\Queue; use Utopia\Registry\Registry; use Utopia\Span\Span; +use Utopia\Span\Storage\Coroutine as SpanCoroutineStorage; use Utopia\System\System; use Utopia\Telemetry\Adapter\None as NoTelemetry; @@ -276,34 +277,39 @@ $container->set('logError', function () { Console::error('[Error] Line: ' . $error->getLine()); Console::error('[Error] Trace: ' . $error->getTraceAsString()); - // Export a standalone error span without mutating the active CLI task span. - $span = new Span($action); + // Export through Span::init(), then restore the active CLI task span. + $activeSpan = Span::current(); + $span = Span::init($action); - $span->set('level', 'error'); - $span->set('logger', $namespace); - $span->set('server.name', System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $span->set('release', System::getEnv('_APP_VERSION', 'UNKNOWN')); - $span->set('environment', System::getEnv('_APP_ENV', 'development') === 'production' ? 'production' : 'staging'); - $span->set('appwrite.error.publish', true); - $span->set('appwrite.error.action', $action); - $span->set('error.message', $error->getMessage()); - $span->set('verboseType', get_class($error)); - $span->set('error.code', $error->getCode()); - $span->set('error.file', $error->getFile()); - $span->set('error.line', $error->getLine()); - $span->set('error.trace', $error->getTraceAsString()); - $span->set('error.detailedTrace', \json_encode($error->getTrace()) ?: null); + try { + $span->set('level', 'error'); + $span->set('logger', $namespace); + $span->set('server.name', System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); + $span->set('release', System::getEnv('_APP_VERSION', 'UNKNOWN')); + $span->set('environment', System::getEnv('_APP_ENV', 'development') === 'production' ? 'production' : 'staging'); + $span->set('appwrite.error.publish', true); + $span->set('appwrite.error.action', $action); + $span->set('error.message', $error->getMessage()); + $span->set('verboseType', get_class($error)); + $span->set('error.code', $error->getCode()); + $span->set('error.file', $error->getFile()); + $span->set('error.line', $error->getLine()); + $span->set('error.trace', $error->getTraceAsString()); + $span->set('error.detailedTrace', \json_encode($error->getTrace()) ?: null); - if ($error->getPrevious() !== null) { - if ($error->getPrevious()->getMessage() != $error->getMessage()) { - $span->set('error.previous.message', $error->getPrevious()->getMessage()); + if ($error->getPrevious() !== null) { + if ($error->getPrevious()->getMessage() != $error->getMessage()) { + $span->set('error.previous.message', $error->getPrevious()->getMessage()); + } + $span->set('error.previous.file', $error->getPrevious()->getFile()); + $span->set('error.previous.line', $error->getPrevious()->getLine()); } - $span->set('error.previous.file', $error->getPrevious()->getFile()); - $span->set('error.previous.line', $error->getPrevious()->getLine()); - } - $span->setError($error); - $span->finish(); + $span->setError($error); + $span->finish(); + } finally { + (new SpanCoroutineStorage())->set($activeSpan); + } }; }, []); diff --git a/app/init/worker/message.php b/app/init/worker/message.php index 250a32ec0c..c92a668850 100644 --- a/app/init/worker/message.php +++ b/app/init/worker/message.php @@ -26,6 +26,7 @@ use Utopia\Pools\Group; use Utopia\Queue\Publisher; use Utopia\Registry\Registry; use Utopia\Span\Span; +use Utopia\Span\Storage\Coroutine as SpanCoroutineStorage; use Utopia\Storage\Device\Telemetry as TelemetryDevice; use Utopia\System\System; use Utopia\Telemetry\Adapter as Telemetry; @@ -352,38 +353,43 @@ return function (Container $container): void { $container->set('logError', function (Document $project) { return function (Throwable $error, string $namespace, string $action, ?array $extras = null) use ($project) { - // Export a standalone error span without mutating the active job span. - $span = new Span($action); + // Export through Span::init(), then restore the active job span. + $activeSpan = Span::current(); + $span = Span::init($action); - $span->set('level', 'error'); - $span->set('logger', $namespace); - $span->set('server.name', System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $span->set('release', System::getEnv('_APP_VERSION', 'UNKNOWN')); - $span->set('environment', System::getEnv('_APP_ENV', 'development') === 'production' ? 'production' : 'staging'); - $span->set('appwrite.error.publish', true); - $span->set('appwrite.error.action', $action); - $span->set('code', $error->getCode()); - $span->set('verboseType', \get_class($error)); - $span->set('projectId', $project->getId()); - $span->set('error.message', $error->getMessage()); - $span->set('error.file', $error->getFile()); - $span->set('error.line', $error->getLine()); - $span->set('error.trace', $error->getTraceAsString()); + try { + $span->set('level', 'error'); + $span->set('logger', $namespace); + $span->set('server.name', System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); + $span->set('release', System::getEnv('_APP_VERSION', 'UNKNOWN')); + $span->set('environment', System::getEnv('_APP_ENV', 'development') === 'production' ? 'production' : 'staging'); + $span->set('appwrite.error.publish', true); + $span->set('appwrite.error.action', $action); + $span->set('code', $error->getCode()); + $span->set('verboseType', \get_class($error)); + $span->set('projectId', $project->getId()); + $span->set('error.message', $error->getMessage()); + $span->set('error.file', $error->getFile()); + $span->set('error.line', $error->getLine()); + $span->set('error.trace', $error->getTraceAsString()); - if ($error->getPrevious() !== null) { - if ($error->getPrevious()->getMessage() != $error->getMessage()) { - $span->set('error.previous.message', $error->getPrevious()->getMessage()); + if ($error->getPrevious() !== null) { + if ($error->getPrevious()->getMessage() != $error->getMessage()) { + $span->set('error.previous.message', $error->getPrevious()->getMessage()); + } + $span->set('error.previous.file', $error->getPrevious()->getFile()); + $span->set('error.previous.line', $error->getPrevious()->getLine()); } - $span->set('error.previous.file', $error->getPrevious()->getFile()); - $span->set('error.previous.line', $error->getPrevious()->getLine()); - } - foreach (($extras ?? []) as $key => $value) { - $span->set($key, \is_scalar($value) || $value === null ? $value : (\json_encode($value) ?: null)); - } + foreach (($extras ?? []) as $key => $value) { + $span->set($key, \is_scalar($value) || $value === null ? $value : (\json_encode($value) ?: null)); + } - $span->setError($error); - $span->finish(); + $span->setError($error); + $span->finish(); + } finally { + (new SpanCoroutineStorage())->set($activeSpan); + } Console::warning("Failed: {$error->getMessage()}"); Console::warning($error->getTraceAsString()); diff --git a/app/realtime.php b/app/realtime.php index 8bb285e26d..c6f4f91a78 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -38,6 +38,7 @@ use Utopia\DSN\DSN; use Utopia\Pools\Group; use Utopia\Registry\Registry; use Utopia\Span\Span; +use Utopia\Span\Storage\Coroutine as SpanCoroutineStorage; use Utopia\System\System; use Utopia\Telemetry\Adapter\None as NoTelemetry; use Utopia\WebSocket\Adapter; @@ -281,34 +282,39 @@ $server = new Server($adapter); if (!function_exists('logError')) { function logError(Throwable $error, string $action, array $tags = [], ?Document $project = null, ?Document $user = null, ?Authorization $authorization = null): void { - // Export a standalone error span without mutating the active connection span. - $span = new Span($action); + // Export through Span::init(), then restore the active connection span. + $activeSpan = Span::current(); + $span = Span::init($action); - $span->set('level', 'error'); - $span->set('logger', 'realtime'); - $span->set('server.name', System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $span->set('release', System::getEnv('_APP_VERSION', 'UNKNOWN')); - $span->set('environment', System::getEnv('_APP_ENV', 'development') === 'production' ? 'production' : 'staging'); - $span->set('appwrite.error.publish', true); - $span->set('appwrite.error.action', $action); - $span->set('code', $error->getCode()); - $span->set('verboseType', get_class($error)); - $span->set('projectId', $project?->getId() ?: 'n/a'); - $span->set('userId', $user?->getId() ?: 'n/a'); + try { + $span->set('level', 'error'); + $span->set('logger', 'realtime'); + $span->set('server.name', System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); + $span->set('release', System::getEnv('_APP_VERSION', 'UNKNOWN')); + $span->set('environment', System::getEnv('_APP_ENV', 'development') === 'production' ? 'production' : 'staging'); + $span->set('appwrite.error.publish', true); + $span->set('appwrite.error.action', $action); + $span->set('code', $error->getCode()); + $span->set('verboseType', get_class($error)); + $span->set('projectId', $project?->getId() ?: 'n/a'); + $span->set('userId', $user?->getId() ?: 'n/a'); - foreach ($tags as $key => $value) { - $span->set($key, \is_scalar($value) ? ($value ?: 'n/a') : (\json_encode($value) ?: 'n/a')); + foreach ($tags as $key => $value) { + $span->set($key, \is_scalar($value) ? ($value ?: 'n/a') : (\json_encode($value) ?: 'n/a')); + } + + $span->set('error.message', $error->getMessage()); + $span->set('error.file', $error->getFile()); + $span->set('error.line', $error->getLine()); + $span->set('error.trace', $error->getTraceAsString()); + $span->set('error.detailedTrace', \json_encode($error->getTrace()) ?: null); + $span->set('roles', \json_encode($authorization?->getRoles() ?? []) ?: null); + $span->setError($error); + $span->finish(); + } finally { + (new SpanCoroutineStorage())->set($activeSpan); } - $span->set('error.message', $error->getMessage()); - $span->set('error.file', $error->getFile()); - $span->set('error.line', $error->getLine()); - $span->set('error.trace', $error->getTraceAsString()); - $span->set('error.detailedTrace', \json_encode($error->getTrace()) ?: null); - $span->set('roles', \json_encode($authorization?->getRoles() ?? []) ?: null); - $span->setError($error); - $span->finish(); - Console::error('[Error] Type: ' . get_class($error)); Console::error('[Error] Message: ' . $error->getMessage()); Console::error('[Error] File: ' . $error->getFile()); diff --git a/app/worker.php b/app/worker.php index f65bf044e2..291af4b153 100644 --- a/app/worker.php +++ b/app/worker.php @@ -15,6 +15,7 @@ use Utopia\Queue\Adapter\Swoole; use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Server; use Utopia\Span\Span; +use Utopia\Span\Storage\Coroutine as SpanCoroutineStorage; use Utopia\System\System; Runtime::enableCoroutine(); @@ -115,26 +116,31 @@ $worker ->action(function (Throwable $error, Document $project, Authorization $authorization) use ($queueName) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); $action = 'appwrite-queue-' . $queueName; - // Export a standalone error span without mutating the active worker span. - $span = new Span($action); + // Export through Span::init(), then restore the active worker span. + $activeSpan = Span::current(); + $span = Span::init($action); - $span->set('level', 'error'); - $span->set('logger', 'appwrite-worker'); - $span->set('server.name', System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $span->set('release', $version); - $span->set('environment', System::getEnv('_APP_ENV', 'development') === 'production' ? 'production' : 'staging'); - $span->set('appwrite.error.publish', true); - $span->set('appwrite.error.action', $action); - $span->set('verboseType', get_class($error)); - $span->set('code', $error->getCode()); - $span->set('projectId', $project->getId()); - $span->set('error.message', $error->getMessage()); - $span->set('error.file', $error->getFile()); - $span->set('error.line', $error->getLine()); - $span->set('error.trace', $error->getTraceAsString()); - $span->set('roles', \json_encode($authorization->getRoles()) ?: null); - $span->setError($error); - $span->finish(); + try { + $span->set('level', 'error'); + $span->set('logger', 'appwrite-worker'); + $span->set('server.name', System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); + $span->set('release', $version); + $span->set('environment', System::getEnv('_APP_ENV', 'development') === 'production' ? 'production' : 'staging'); + $span->set('appwrite.error.publish', true); + $span->set('appwrite.error.action', $action); + $span->set('verboseType', get_class($error)); + $span->set('code', $error->getCode()); + $span->set('projectId', $project->getId()); + $span->set('error.message', $error->getMessage()); + $span->set('error.file', $error->getFile()); + $span->set('error.line', $error->getLine()); + $span->set('error.trace', $error->getTraceAsString()); + $span->set('roles', \json_encode($authorization->getRoles()) ?: null); + $span->setError($error); + $span->finish(); + } finally { + (new SpanCoroutineStorage())->set($activeSpan); + } Console::error('[Error] Type: ' . get_class($error)); Console::error('[Error] Message: ' . $error->getMessage()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Embeddings/Text/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Embeddings/Text/Create.php index d5eb099e72..d0c397d04f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Embeddings/Text/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/VectorsDB/Embeddings/Text/Create.php @@ -15,6 +15,7 @@ use Utopia\Agents\Agent; use Utopia\Database\Document; use Utopia\Http\Adapter\Swoole\Response as SwooleResponse; use Utopia\Span\Span; +use Utopia\Span\Storage\Coroutine as SpanCoroutineStorage; use Utopia\System\System; use Utopia\Validator\ArrayList; use Utopia\Validator\Text; @@ -102,24 +103,30 @@ class Create extends CreateDocumentAction $error = 'Error while generating embedding'; $totalErrors += 1; - // Export each failed text independently without overwriting the request span. - $span = new Span('vectorsDB.createTextEmbeddings'); - $span->set('level', 'error'); - $span->set('logger', 'http'); - $span->set('server.name', System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $span->set('release', System::getEnv('_APP_VERSION', 'UNKNOWN')); - $span->set('environment', System::getEnv('_APP_ENV', 'development') === 'production' ? 'production' : 'staging'); - $span->set('appwrite.error.publish', true); - $span->set('appwrite.error.action', 'vectorsDB.createTextEmbeddings'); - $span->set('embeddingModel', $model); - $span->set('code', $e->getCode()); - $span->set('projectId', $project->getId()); - $span->set('error.message', $e->getMessage()); - $span->set('error.file', $e->getFile()); - $span->set('error.line', $e->getLine()); - $span->set('error.trace', $e->getTraceAsString()); - $span->setError($e); - $span->finish(); + // Export through Span::init(), then restore the active request span. + $activeSpan = Span::current(); + $span = Span::init('vectorsDB.createTextEmbeddings'); + + try { + $span->set('level', 'error'); + $span->set('logger', 'http'); + $span->set('server.name', System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); + $span->set('release', System::getEnv('_APP_VERSION', 'UNKNOWN')); + $span->set('environment', System::getEnv('_APP_ENV', 'development') === 'production' ? 'production' : 'staging'); + $span->set('appwrite.error.publish', true); + $span->set('appwrite.error.action', 'vectorsDB.createTextEmbeddings'); + $span->set('embeddingModel', $model); + $span->set('code', $e->getCode()); + $span->set('projectId', $project->getId()); + $span->set('error.message', $e->getMessage()); + $span->set('error.file', $e->getFile()); + $span->set('error.line', $e->getLine()); + $span->set('error.trace', $e->getTraceAsString()); + $span->setError($e); + $span->finish(); + } finally { + (new SpanCoroutineStorage())->set($activeSpan); + } } $results[] = new Document([