From bb4e8586cc993fa037476272b19e440a48247f94 Mon Sep 17 00:00:00 2001 From: PR Replica Date: Sat, 1 Jan 2000 00:00:00 +0000 Subject: [PATCH] Remove utopia-php/logger and report errors through spans Source PR: https://github.com/appwrite/appwrite/pull/13457 Source head: fd3a1d17daa5053a16e0886f16719cedbdee1194 --- .env | 1 - app/cli.php | 52 +--- app/config/variables.php | 11 +- app/controllers/general.php | 226 +++--------------- app/http.php | 79 ++---- app/init/registers.php | 109 --------- app/init/resources.php | 8 +- app/init/resources/request.php | 11 +- app/init/span.php | 62 ++++- app/init/worker/message.php | 59 +---- app/realtime.php | 62 ++--- app/worker.php | 38 +-- composer.json | 1 - composer.lock | 57 +---- docker-compose.yml | 5 - src/Appwrite/Execution/Store.php | 35 +-- src/Appwrite/Locking/Lock.php | 38 +-- .../Databases/Http/Embeddings/Text/Create.php | 34 +-- .../Modules/Databases/Workers/Databases.php | 11 +- .../Modules/Functions/Workers/Builds.php | 13 +- .../Platform/Modules/Proxy/Action.php | 17 +- .../Modules/Proxy/Http/Rules/API/Create.php | 5 +- .../Proxy/Http/Rules/Function/Create.php | 5 +- .../Proxy/Http/Rules/Redirect/Create.php | 5 +- .../Modules/Proxy/Http/Rules/Site/Create.php | 5 +- .../Proxy/Http/Rules/Status/Update.php | 5 +- src/Appwrite/Platform/Tasks/Doctor.php | 3 +- .../Platform/Workers/Certificates.php | 24 +- src/Appwrite/Platform/Workers/Deletes.php | 9 +- src/Appwrite/Platform/Workers/Functions.php | 16 +- src/Appwrite/Platform/Workers/Mails.php | 7 +- src/Appwrite/Platform/Workers/Messaging.php | 8 +- .../Platform/Workers/Notifications.php | 29 +-- src/Appwrite/Platform/Workers/Webhooks.php | 8 +- tests/unit/Execution/StoreTest.php | 40 ++-- tests/unit/Locking/LockTest.php | 91 ++----- tests/unit/Platform/Workers/DatabasesTest.php | 2 - tests/unit/Platform/Workers/MailsTest.php | 3 - .../Platform/Workers/NotificationsTest.php | 78 +++--- 39 files changed, 303 insertions(+), 969 deletions(-) diff --git a/.env b/.env index d015689eabe..a132383554e 100644 --- a/.env +++ b/.env @@ -169,7 +169,6 @@ _APP_GRAPHQL_MAX_COMPLEXITY=250 _APP_GRAPHQL_MAX_DEPTH=4 _APP_ASSISTANT_OPENAI_API_KEY= _APP_LOGGING_CONFIG= -_APP_LOGGING_CONFIG_REALTIME= _APP_LOGGING_FORMAT=pretty _APP_MIGRATION_HOST=appwrite _APP_MIGRATIONS_FIREBASE_CLIENT_ID= diff --git a/app/cli.php b/app/cli.php index 9e471618d3d..e3227f4693a 100644 --- a/app/cli.php +++ b/app/cli.php @@ -20,11 +20,10 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\Logger\Log; use Utopia\Platform\Service; use Utopia\Pools\Group; use Utopia\Registry\Registry; -use Utopia\System\System; +use Utopia\Span\Span; use function Swoole\Coroutine\run; @@ -178,8 +177,8 @@ $container->set('usage', function () { return new UsageContext(); }, []); -$container->set('logError', function (Registry $register) { - return function (Throwable $error, string $namespace, string $action) use ($register) { +$container->set('logError', function () { + return function (Throwable $error, string $namespace, string $action) { Console::error('[Error] Timestamp: ' . date('c', time())); Console::error('[Error] Type: ' . get_class($error)); Console::error('[Error] Message: ' . $error->getMessage()); @@ -187,48 +186,11 @@ Console::error('[Error] Line: ' . $error->getLine()); Console::error('[Error] Trace: ' . $error->getTraceAsString()); - $logger = $register->get('logger'); - - if ($logger) { - $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - - $log = new Log(); - $log->setNamespace($namespace); - $log->setServer(System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($error->getMessage()); - - $log->addTag('code', $error->getCode()); - $log->addTag('verboseType', get_class($error)); - - $log->addExtra('file', $error->getFile()); - $log->addExtra('line', $error->getLine()); - $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('detailedTrace', $error->getTrace()); - - if ($error->getPrevious() !== null) { - if ($error->getPrevious()->getMessage() != $error->getMessage()) { - $log->addExtra('previousMessage', $error->getPrevious()->getMessage()); - } - $log->addExtra('previousFile', $error->getPrevious()->getFile()); - $log->addExtra('previousLine', $error->getPrevious()->getLine()); - } - - $log->setAction($action); - - $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - - try { - $responseCode = $logger->addLog($log); - Console::info('Error log pushed with status code: ' . $responseCode); - } catch (Throwable $th) { - Console::error('Error pushing log: ' . $th->getMessage()); - } - } + // Tasks run outside a request span; open one so the failure reaches the exporters. + $span = Span::current() ?? Span::init($action); + $span->finish(error: $error); }; -}, ['register']); +}, []); $container->set('bus', function (Registry $register) use ($container) { return $register->get('bus')->setResolver(fn (string $name) => $container->get($name)); diff --git a/app/config/variables.php b/app/config/variables.php index 9b758e39f48..aed2f57f54d 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -403,18 +403,9 @@ 'question' => 'Enter an email that will be used when registering for SSL certificates', 'filter' => '' ], - [ - 'name' => '_APP_LOGGING_PROVIDER', - 'description' => 'Deprecated since 1.6.0, use `_APP_LOGGING_CONFIG` with DSN value instead. This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, set the value to one of \'sentry\', \'raygun\', \'appSignal\', \'logOwl\' to enable the logger.', - 'introduction' => '0.12.0', - 'default' => '', - 'required' => false, - 'question' => '', - 'filter' => '' - ], [ 'name' => '_APP_LOGGING_CONFIG', - 'description' => 'This variable allows you to enable logging errors to third party providers. This value is empty by default, set a DSN value to one of the following `sentry://PROJECT_ID:SENTRY_API_KEY@SENTRY_HOST/`, , `logowl://SERVICE_TICKET@SERIVCE_HOST/` `raygun://RAYGUN_API_KEY/`, `appSignal://API_KEY/` to enable the logger.\n\nFor versions prior `1.5.6` you can use the old syntax.\n\nOld syntax: If using Sentry, this should be \'SENTRY_API_KEY;SENTRY_APP_ID\'. If using Raygun, this should be Raygun API key. If using AppSignal, this should be AppSignal API key. If using LogOwl, this should be LogOwl Service Ticket.', + 'description' => 'This variable allows you to report server errors to Sentry. This value is empty by default, set a DSN value `sentry://PROJECT_ID:SENTRY_API_KEY@SENTRY_HOST/` to enable it. Errors are exported as spans, so every attribute added with `Span::add()` during the request or job is attached to the Sentry event.', 'introduction' => '0.12.0', 'default' => '', 'required' => false, diff --git a/app/controllers/general.php b/app/controllers/general.php index b35b06eded8..83c19c359b0 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -70,10 +70,6 @@ use Utopia\DSN\DSN; use Utopia\Http\Http; use Utopia\Locale\Locale; -use Utopia\Logger\Adapter\Sentry; -use Utopia\Logger\Log; -use Utopia\Logger\Log\User; -use Utopia\Logger\Logger; use Utopia\Platform\Service; use Utopia\Span\Span; use Utopia\System\System; @@ -82,7 +78,7 @@ Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE); -function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Event $queueForEvents, Bus $bus, Executor $executor, Geo $geo, callable $getIsResourceBlocked, array $platform, string $previewHostname, Authorization $authorization, ?Key $apiKey, DeletePublisher $publisherForDeletes, int $executionsRetentionCount, Lock $lock) +function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, Bus $bus, Executor $executor, Geo $geo, callable $getIsResourceBlocked, array $platform, string $previewHostname, Authorization $authorization, ?Key $apiKey, DeletePublisher $publisherForDeletes, int $executionsRetentionCount, Lock $lock) { $host = $request->getHostname(); if (!empty($previewHostname)) { @@ -168,9 +164,9 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S } /** - * Set projectId to update the Error hook logger, since x-appwrite-project is not available when executing custom domain function + * Set project.id on the span, since x-appwrite-project is not available when executing custom domain function */ - $log->addTag('projectId', $project->getId()); + Span::add('project.id', $project->getId()); } if (array_key_exists('proxy', $project->getAttribute('services', []))) { @@ -889,7 +885,6 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S ->inject('swooleRequest') ->inject('request') ->inject('response') - ->inject('log') ->inject('project') ->inject('dbForPlatform') ->inject('getProjectDB') @@ -910,7 +905,7 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S ->inject('executionsRetentionCount') ->inject('lock') ->inject('params') - ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Geo $geo, Event $queueForEvents, Bus $bus, Executor $executor, array $platform, callable $getIsResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeletePublisher $publisherForDeletes, int $executionsRetentionCount, Lock $lock, array $params) { + ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Geo $geo, Event $queueForEvents, Bus $bus, Executor $executor, array $platform, callable $getIsResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeletePublisher $publisherForDeletes, int $executionsRetentionCount, Lock $lock, array $params) { /* * Appwrite Router */ @@ -918,7 +913,7 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S $platformHostnames = $platform['hostnames'] ?? []; // Only run Router when external domain if (!\in_array($hostname, $platformHostnames) || !empty($previewHostname)) { - if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $bus, $executor, $geo, $getIsResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $publisherForDeletes, $executionsRetentionCount, $lock)) { + if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $bus, $executor, $geo, $getIsResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $publisherForDeletes, $executionsRetentionCount, $lock)) { $utopia->match($request)?->route->label('router', true); } } @@ -1212,7 +1207,6 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S ->inject('swooleRequest') ->inject('request') ->inject('response') - ->inject('log') ->inject('dbForPlatform') ->inject('getProjectDB') ->inject('queueForEvents') @@ -1230,14 +1224,14 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S ->inject('publisherForDeletes') ->inject('executionsRetentionCount') ->inject('lock') - ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Bus $bus, Executor $executor, Geo $geo, callable $getIsResourceBlocked, array $platform, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeletePublisher $publisherForDeletes, int $executionsRetentionCount, Lock $lock) { + ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Bus $bus, Executor $executor, Geo $geo, callable $getIsResourceBlocked, array $platform, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeletePublisher $publisherForDeletes, int $executionsRetentionCount, Lock $lock) { /* * Appwrite Router */ $platformHostnames = $platform['hostnames'] ?? []; // Only run Router when external domain if (!in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) { - if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $bus, $executor, $geo, $getIsResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $publisherForDeletes, $executionsRetentionCount, $lock)) { + if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $bus, $executor, $geo, $getIsResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $publisherForDeletes, $executionsRetentionCount, $lock)) { $utopia->match($request)?->route->label('router', true); } } @@ -1266,12 +1260,10 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S ->inject('request') ->inject('response') ->inject('project') - ->inject('logger') - ->inject('log') ->inject('bus') ->inject('devKey') ->inject('authorization') - ->action(function (Throwable $error, Http $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Bus $bus, Document $devKey, Authorization $authorization) { + ->action(function (Throwable $error, Http $utopia, Request $request, Response $response, Document $project, Bus $bus, Document $devKey, Authorization $authorization) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); $route = $utopia->match($request)?->route; $class = \get_class($error); @@ -1312,31 +1304,6 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S $publish = $error->getCode() === 0 || $error->getCode() >= 500; } - $providerConfig = System::getEnv('_APP_EXPERIMENT_LOGGING_CONFIG', ''); - if (!empty($providerConfig) && $error->getCode() >= 400 && $error->getCode() < 500) { - // Register error logger - try { - $loggingProvider = new DSN($providerConfig); - $providerName = $loggingProvider->getScheme(); - - if (!empty($providerName) && $providerName === 'sentry') { - $key = $loggingProvider->getPassword(); - $projectId = $loggingProvider->getUser() ?? ''; - $host = 'https://' . $loggingProvider->getHost(); - $sampleRate = $loggingProvider->getParam('sample', 0.01); - - $adapter = new Sentry($projectId, $key, $host); - $logger = new Logger($adapter); - $logger->setSample($sampleRate); - $publish = true; - } else { - throw new \Exception('Invalid experimental logging provider'); - } - } catch (\Throwable $th) { - Console::warning('Failed to initialize logging provider: ' . $th->getMessage()); - } - } - /** * If not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php */ @@ -1359,158 +1326,27 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S } } - if ($logger && $publish) { - try { - /** @var Utopia\Database\Document $user */ - $user = $utopia->context()->get('user'); - } catch (\Throwable) { - // All good, user is optional information for logger - } - - if (isset($user) && !$user->isEmpty()) { - $log->setUser(new User($user->getId())); - } else { - $log->setUser(new User('guest-' . hash('sha256', $request->getIP()))); - } - - try { - $dsn = new DSN($project->getAttribute('database', 'console')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database', 'console')); - } - - $log->setNamespace("http"); - $log->setServer(System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($error->getMessage()); - - $log->addTag('database', $dsn->getHost()); - $log->addTag('method', \implode(',', $route?->getMethods() ?? [$request->getMethod()])); - $log->addTag('url', $request->getURI()); - $log->addTag('verboseType', get_class($error)); - $log->addTag('code', $error->getCode()); - - $tags = $log->getTags(); - if (!isset($tags['projectId'])) { - $log->addTag('projectId', $project->getId()); - } - - $log->addTag('hostname', $request->getHostname()); - $log->addTag('locale', (string)$request->getParam('locale', $request->getHeaderLine('x-appwrite-locale', ''))); - - $log->addExtra('file', $error->getFile()); - $log->addExtra('line', $error->getLine()); - $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('roles', $authorization->getRoles()); - - try { - /* add queries to log */ - $queries = $request->getParam('queries', []); - if (!empty($queries) && is_array($queries)) { - $parsedQueries = Query::parseQueries($queries); - - // format query by removing sensitive values - $formatQuery = function (array $queryArray) use (&$formatQuery): ?array { - $method = $queryArray['method'] ?? ''; - $values = $queryArray['values'] ?? []; - $attribute = $queryArray['attribute'] ?? ''; - - if (!is_string($method) || $method === '') { - return null; - } - - // logical queries - recursively format nested queries - if (in_array($method, [Query::TYPE_AND, Query::TYPE_OR], true)) { - $nested = []; - foreach ($values as $nestedArray) { - if (is_array($nestedArray)) { - $formatted = $formatQuery($nestedArray); - if ($formatted !== null) { - $nested[] = $formatted; - } - } - } - return empty($nested) ? null : [$method => $nested]; - } - - // select - show selected attributes - if ($method === Query::TYPE_SELECT) { - $attributes = array_values(array_filter($values, 'is_string')); - return [$method => $attributes]; - } - - // pagination - if (in_array($method, [ - Query::TYPE_LIMIT, - Query::TYPE_OFFSET, - Query::TYPE_CURSOR_AFTER, - Query::TYPE_CURSOR_BEFORE - ], true)) { - return [$method => []]; - } - - // orders - if (in_array($method, [ - Query::TYPE_ORDER_DESC, - Query::TYPE_ORDER_ASC, - Query::TYPE_ORDER_RANDOM - ], true)) { - return [$method => !empty($attribute) ? [$attribute] : []]; - } - - // filter - if (!empty($attribute)) { - return [$method => [$attribute]]; - } - - // fallback - return [$method => []]; - }; - - $formattedQueries = []; - foreach ($parsedQueries as $query) { - $formatted = $formatQuery($query->toArray()); - if ($formatted !== null) { - $formattedQueries[] = $formatted; - } - } - - if (!empty($formattedQueries)) { - $log->addExtra('queries', $formattedQueries); - } - } - } catch (Throwable $_) { - // don't fail the error handler - } - - $sdk = $route?->getLabel("sdk", false); - $action = 'UNKNOWN_NAMESPACE.UNKNOWN.METHOD'; - if (!empty($sdk)) { - if (\is_array($sdk)) { - $sdk = $sdk[0]; - } - /** @var \Appwrite\SDK\Method $sdk */ - $action = $sdk->getNamespace() . '.' . $sdk->getMethodName(); - } elseif ($route === null) { - $path = ltrim(parse_url($request->getURI(), PHP_URL_PATH) ?? '/', '/') ?: 'root'; - $action = 'http.' . $request->getMethod() . '.' . $path; - } - - $log->setAction($action); - $log->addTag('service', $action); + Span::add('http.hostname', $request->getHostname()); + Span::add('http.locale', (string)$request->getParam('locale', $request->getHeaderLine('x-appwrite-locale', ''))); + if (Span::current()?->get('project.id') === null) { + Span::add('project.id', $project->getId()); + } - $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); + try { + /** @var Utopia\Database\Document $user */ + $user = $utopia->context()->get('user'); + Span::add('user.id', $user->isEmpty() ? 'guest-' . hash('sha256', $request->getIP()) : $user->getId()); + } catch (\Throwable) { + // User resource may not be available in error context + } - try { - $responseCode = $logger->addLog($log); - Console::info('Error log pushed with status code: ' . $responseCode); - } catch (Throwable $th) { - Console::error('Error pushing log: ' . $th->getMessage()); - } + try { + $dsn = new DSN($project->getAttribute('database', 'console')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database', 'console')); } + Span::add('database.host', $dsn->getHost()); /** Wrap all exceptions inside Appwrite\Extend\Exception */ if (!($error instanceof AppwriteException)) { @@ -1619,7 +1455,6 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S ->inject('swooleRequest') ->inject('request') ->inject('response') - ->inject('log') ->inject('dbForPlatform') ->inject('getProjectDB') ->inject('queueForEvents') @@ -1634,13 +1469,13 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S ->inject('publisherForDeletes') ->inject('executionsRetentionCount') ->inject('lock') - ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Bus $bus, Executor $executor, Geo $geo, callable $getIsResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization, DeletePublisher $publisherForDeletes, int $executionsRetentionCount, Lock $lock) { + ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Bus $bus, Executor $executor, Geo $geo, callable $getIsResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization, DeletePublisher $publisherForDeletes, int $executionsRetentionCount, Lock $lock) { $platformHostnames = $platform['hostnames'] ?? []; if (in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) { $template = new View(__DIR__ . '/../views/general/robots.phtml'); $response->text($template->render(false)); } else { - if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $bus, $executor, $geo, $getIsResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $publisherForDeletes, $executionsRetentionCount, $lock)) { + if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $bus, $executor, $geo, $getIsResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $publisherForDeletes, $executionsRetentionCount, $lock)) { $utopia->match($request)?->route->label('router', true); } } @@ -1654,7 +1489,6 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S ->inject('swooleRequest') ->inject('request') ->inject('response') - ->inject('log') ->inject('dbForPlatform') ->inject('getProjectDB') ->inject('queueForEvents') @@ -1669,13 +1503,13 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S ->inject('publisherForDeletes') ->inject('executionsRetentionCount') ->inject('lock') - ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Bus $bus, Executor $executor, Geo $geo, callable $getIsResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization, DeletePublisher $publisherForDeletes, int $executionsRetentionCount, Lock $lock) { + ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Bus $bus, Executor $executor, Geo $geo, callable $getIsResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization, DeletePublisher $publisherForDeletes, int $executionsRetentionCount, Lock $lock) { $platformHostnames = $platform['hostnames'] ?? []; if (in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) { $template = new View(__DIR__ . '/../views/general/humans.phtml'); $response->text($template->render(false)); } else { - if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $bus, $executor, $geo, $getIsResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $publisherForDeletes, $executionsRetentionCount, $lock)) { + if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $bus, $executor, $geo, $getIsResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $publisherForDeletes, $executionsRetentionCount, $lock)) { $utopia->match($request)?->route->label('router', true); } } diff --git a/app/http.php b/app/http.php index f104905e347..9fef8e3f9b6 100644 --- a/app/http.php +++ b/app/http.php @@ -26,8 +26,6 @@ use Utopia\Http\Adapter\Swoole\Server; use Utopia\Http\Files; use Utopia\Http\Http; -use Utopia\Logger\Log; -use Utopia\Logger\Log\User; use Utopia\Span\Span; use Utopia\System\System; @@ -495,70 +493,25 @@ function createDatabase(Container $resources, string $resourceKey, string $dbNam $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - $logger = $app->context()->get("logger"); - if ($logger) { - try { - /** @var Utopia\Database\Document $user */ - $user = $app->context()->get('user'); - } catch (\Throwable $_th) { - // All good, user is optional information for logger - } - - $route = $app->match($request)?->route; - - $log = $app->context()->get("log"); - - if (isset($user) && !$user->isEmpty()) { - $log->setUser(new User($user->getId())); - } else { - $log->setUser(new User('guest-' . hash('sha256', $request->getIP()))); - } - - $log->setNamespace("http"); - $log->setServer(System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($th->getMessage()); - - $log->addTag('method', \implode(',', $route?->getMethods() ?? [$request->getMethod()])); - $log->addTag('url', $route?->getPath() ?? $request->getURI()); - $log->addTag('verboseType', get_class($th)); - $log->addTag('code', $th->getCode()); - // $log->addTag('projectId', $project->getId()); // TODO: Figure out how to get ProjectID, if it becomes relevant - $log->addTag('hostname', $request->getHostname()); - $log->addTag('locale', (string)$request->getParam('locale', $request->getHeaderLine('x-appwrite-locale', ''))); - - $log->addExtra('file', $th->getFile()); - $log->addExtra('line', $th->getLine()); - $log->addExtra('trace', $th->getTraceAsString()); - $log->addExtra('roles', isset($authorization) ? $authorization->getRoles() : []); - - $sdk = $route?->getLabel("sdk", false); - - $action = 'UNKNOWN_NAMESPACE.UNKNOWN.METHOD'; - if (!empty($sdk)) { - if (\is_array($sdk)) { - $sdk = $sdk[0]; - } - /** @var Appwrite\SDK\Method $sdk */ - $action = $sdk->getNamespace() . '.' . $sdk->getMethodName(); - } elseif ($route === null) { - $path = ltrim(parse_url($request->getURI(), PHP_URL_PATH) ?? '/', '/') ?: 'root'; - $action = 'http.' . $request->getMethod() . '.' . $path; - } - - $log->setAction($action); - $log->addTag('service', $action); + $route = $app->match($request)?->route; - $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); + try { + /** @var Utopia\Database\Document $user */ + $user = $app->context()->get('user'); + Span::add('user.id', $user->isEmpty() ? 'guest-' . hash('sha256', $request->getIP()) : $user->getId()); + } catch (\Throwable $_th) { + // All good, user is optional information + } + Span::add('http.hostname', $request->getHostname()); + Span::add('http.locale', (string)$request->getParam('locale', $request->getHeaderLine('x-appwrite-locale', ''))); - try { - $responseCode = $logger->addLog($log); - Console::info('Error log pushed with status code: ' . $responseCode); - } catch (Throwable $th) { - Console::error('Error pushing log: ' . $th->getMessage()); + $sdk = $route?->getLabel("sdk", false); + if (!empty($sdk)) { + if (\is_array($sdk)) { + $sdk = $sdk[0]; } + /** @var Appwrite\SDK\Method $sdk */ + Span::add('http.service', $sdk->getNamespace() . '.' . $sdk->getMethodName()); } $swooleResponse = $utopiaResponse->getSwooleResponse(); diff --git a/app/init/registers.php b/app/init/registers.php index 4fabf11458e..7c5e5ea9d65 100644 --- a/app/init/registers.php +++ b/app/init/registers.php @@ -7,7 +7,6 @@ use Appwrite\URL\URL as AppwriteURL; use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Config\Config; -use Utopia\Console; use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Adapter\Mongo; use Utopia\Database\Adapter\MySQL; @@ -17,11 +16,6 @@ use Utopia\Domains\Validator\PublicDomain; use Utopia\DSN\DSN; use Utopia\Http\Http; -use Utopia\Logger\Adapter\AppSignal; -use Utopia\Logger\Adapter\LogOwl; -use Utopia\Logger\Adapter\Raygun; -use Utopia\Logger\Adapter\Sentry; -use Utopia\Logger\Logger; use Utopia\Messaging\Adapter\Email\SMTP; use Utopia\Mongo\Client as MongoClient; use Utopia\Pools\Adapter\Stack as StackPool; @@ -44,109 +38,6 @@ PublicDomain::allow(['request-catcher-webhook']); } -$register->set('logger', function () { - // Register error logger - $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); - $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); - - if (empty($providerConfig)) { - return; - } - - try { - $loggingProvider = new DSN($providerConfig); - - $providerName = $loggingProvider->getScheme(); - $providerConfig = match ($providerName) { - 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()], - 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], - default => ['key' => $loggingProvider->getHost()], - }; - } catch (Throwable $th) { - // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables - Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage()); - $configChunks = \explode(";", $providerConfig); - - $providerConfig = match ($providerName) { - 'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',], - 'logowl' => ['ticket' => $configChunks[0], 'host' => ''], - default => ['key' => $providerConfig], - }; - } - - if (empty($providerName)) { - return; - } - - if (!Logger::hasProvider($providerName)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); - } - - try { - $adapter = match ($providerName) { - 'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']), - 'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']), - 'raygun' => new Raygun($providerConfig['key']), - 'appsignal' => new AppSignal($providerConfig['key']), - default => null - }; - } catch (Throwable $th) { - $adapter = null; - } - - if ($adapter === null) { - Console::error("Logging provider not supported. Logging is disabled"); - return; - } - - return new Logger($adapter); -}); - -$register->set('realtimeLogger', function () { - // Register error logger for realtime, falls back to default logging config - $providerConfig = System::getEnv('_APP_LOGGING_CONFIG_REALTIME', '') - ?: System::getEnv('_APP_LOGGING_CONFIG', ''); - - if (empty($providerConfig)) { - return; - } - - $loggingProvider = new DSN($providerConfig); - $providerName = $loggingProvider->getScheme(); - $providerConfig = match ($providerName) { - 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()], - 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], - default => ['key' => $loggingProvider->getHost()], - }; - - if (empty($providerName)) { - return; - } - - if (!Logger::hasProvider($providerName)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); - } - - try { - $adapter = match ($providerName) { - 'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']), - 'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']), - 'raygun' => new Raygun($providerConfig['key']), - 'appsignal' => new AppSignal($providerConfig['key']), - default => null - }; - } catch (Throwable $th) { - $adapter = null; - } - - if ($adapter === null) { - Console::error("Logging provider not supported. Logging is disabled"); - return; - } - - return new Logger($adapter); -}); - $register->set('pools', function () { $group = new Group(); diff --git a/app/init/resources.php b/app/init/resources.php index bdd8069bb49..4897062b005 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -43,7 +43,6 @@ use Utopia\DI\Container; use Utopia\DSN\DSN; use Utopia\Lock\Distributed; -use Utopia\Logger\Logger; use Utopia\Pools\Adapter\Swoole as SwoolePoolAdapter; use Utopia\Pools\Group; use Utopia\Pools\Pool as Connections; @@ -70,8 +69,6 @@ $container->set('register', fn () => $register); -$container->set('logger', fn ($register) => $register->get('logger'), ['register']); - $container->set('hooks', fn ($register) => $register->get('hooks'), ['register']); $container->set('console', fn () => new Document(Config::getParam('console')), []); @@ -182,7 +179,7 @@ ); }, []); -$container->set('executionStore', function (?Logger $logger) { +$container->set('executionStore', function () { $client = new HttpClientPool(new Connections( new SwoolePoolAdapter(), 'executions', @@ -208,9 +205,8 @@ dsn: $connection, client: $client, retention: (int) System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600), - logger: $logger, ); -}, ['logger']); +}, []); $container->set('publisherForBuilds', fn (Publisher $publisher) => new BuildPublisher( $publisher, diff --git a/app/init/resources/request.php b/app/init/resources/request.php index 1ebfd8a69b9..d161832a57a 100644 --- a/app/init/resources/request.php +++ b/app/init/resources/request.php @@ -52,8 +52,6 @@ use Utopia\Http\Http; use Utopia\Locale\Locale; use Utopia\Lock\Distributed as DistributedLock; -use Utopia\Logger\Log; -use Utopia\Logger\Logger; use Utopia\Pools\Group; use Utopia\Queue\Publisher\Synchronous as Publisher; use Utopia\Queue\Queue; @@ -72,20 +70,15 @@ return function (Container $context): void { $context->set('utopia:graphql', fn ($utopia) => $utopia, ['utopia']); - $context->set('log', fn () => new Log(), []); - - $context->set('logger', fn ($register) => $register->get('logger'), ['register']); - - $context->set('lock', function (Group $pools, Telemetry $telemetry, ?Logger $logger, Document $project): Lock { + $context->set('lock', function (Group $pools, Telemetry $telemetry, Document $project): Lock { return new Lock( fn (string $key, int $ttl, Closure $callback): mixed => $pools->get('lock')->use( fn (\Redis $redis): mixed => $callback(new DistributedLock($redis, $key, $ttl)) ), $telemetry, - $logger, $project ); - }, ['pools', 'telemetry', 'logger', 'project']); + }, ['pools', 'telemetry', 'project']); $context->set('authorization', fn () => new Authorization(), []); diff --git a/app/init/span.php b/app/init/span.php index a358e9bb431..8d442c83713 100644 --- a/app/init/span.php +++ b/app/init/span.php @@ -1,6 +1,10 @@ getScheme() !== 'sentry') { + throw new \InvalidArgumentException('Only the sentry:// scheme is supported'); + } + + $exporters[] = new Sentry( + sampler: static function (Span $span): bool { + $error = $span->getError(); + if ($error instanceof AppwriteException) { + return $error->isPublishable(); + } + + return $error === null || $error->getCode() === 0 || $error->getCode() >= 500; + }, + dsn: 'https://' . $dsn->getPassword() . '@' . $dsn->getHost() . '/' . $dsn->getUser(), + environment: System::getEnv('_APP_ENV', 'development') === 'production' ? 'production' : 'staging', + release: System::getEnv('_APP_VERSION', 'UNKNOWN'), + serverName: System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname() ?: null), + classifier: static fn (string $key): SentryField => match ($key) { + 'project.id', + 'user.id', + 'http.method', + 'http.path', + 'http.hostname', + 'http.locale', + 'http.service', + 'error.action', + 'type', + 'domain', + 'function.id', + 'deployment.id', + 'database.id', + 'channel', + 'lock.target' => SentryField::Tag, + default => SentryField::Context, + }, + ); + } catch (\Throwable $th) { + \error_log('Invalid _APP_LOGGING_CONFIG, error reporting is disabled: ' . $th->getMessage()); + } +} + +Span::setExporters(...$exporters); diff --git a/app/init/worker/message.php b/app/init/worker/message.php index 033a2c8cc7a..47881c29277 100644 --- a/app/init/worker/message.php +++ b/app/init/worker/message.php @@ -19,11 +19,9 @@ use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; use Utopia\DI\Container; -use Utopia\Logger\Log; use Utopia\Pools\Group; use Utopia\Queue\Publisher\Synchronous as Publisher; use Utopia\Queue\Queue; -use Utopia\Registry\Registry; use Utopia\Span\Span; use Utopia\Storage\Device\Telemetry as TelemetryDevice; use Utopia\System\System; @@ -35,8 +33,6 @@ * must be fresh for each worker job. */ return function (Container $container): void { - $container->set('log', fn () => new Log(), []); - $container->set('usage', fn () => new Context(), []); $container->set('authorization', function () { @@ -202,50 +198,17 @@ return new Deployments($jobs, $dbForProject, $project, $platform); }, ['jobs', 'dbForProject', 'project', 'platform']); - $container->set('logError', function (Registry $register, Document $project) { - return function (Throwable $error, string $namespace, string $action, ?array $extras = null) use ($register, $project) { - $logger = $register->get('logger'); - - if ($logger) { - $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - - $log = new Log(); - $log->setNamespace($namespace); - $log->setServer(System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($error->getMessage()); - - $log->addTag('code', $error->getCode()); - $log->addTag('verboseType', \get_class($error)); - $log->addTag('projectId', $project->getId()); - - $log->addExtra('file', $error->getFile()); - $log->addExtra('line', $error->getLine()); - $log->addExtra('trace', $error->getTraceAsString()); - - if ($error->getPrevious() !== null) { - if ($error->getPrevious()->getMessage() != $error->getMessage()) { - $log->addExtra('previousMessage', $error->getPrevious()->getMessage()); - } - $log->addExtra('previousFile', $error->getPrevious()->getFile()); - $log->addExtra('previousLine', $error->getPrevious()->getLine()); - } - + $container->set('logError', function (Document $project) { + return function (Throwable $error, string $namespace, string $action, ?array $extras = null) use ($project) { + $span = Span::current(); + if ($span !== null) { + $span->setError($error); + $span->set('project.id', $project->getId()); + $span->set('error.action', $action); foreach (($extras ?? []) as $key => $value) { - $log->addExtra($key, $value); - } - - $log->setAction($action); - - $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - - try { - $responseCode = $logger->addLog($log); - Console::info('Error log pushed with status code: ' . $responseCode); - } catch (Throwable $th) { - Console::error('Error pushing log: ' . $th->getMessage()); + if (\is_scalar($value) || $value === null) { + $span->set('error.' . $key, $value); + } } } @@ -259,7 +222,7 @@ Console::warning("Previous File: {$error->getPrevious()->getFile()} Line: {$error->getPrevious()->getLine()}"); } }; - }, ['register', 'project']); + }, ['project']); $container->set('getAudit', function (Database $dbForPlatform, callable $getProjectDB) { return function (Document $project) use ($dbForPlatform, $getProjectDB) { diff --git a/app/realtime.php b/app/realtime.php index 1c759eb3b57..ccbe39637da 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -47,7 +47,6 @@ use Utopia\Database\Validator\Authorization; use Utopia\DI\Container; use Utopia\DSN\DSN; -use Utopia\Logger\Log; use Utopia\Pools\Group; use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Queue; @@ -412,55 +411,22 @@ function checkForProjectUsage(Document $project): void if (!function_exists('logError')) { function logError(Throwable $error, string $action, array $tags = [], ?Document $project = null, ?Document $user = null, ?Authorization $authorization = null): void { - global $register; - - $logger = $register->get('realtimeLogger'); - - // Match HTTP semantics (app/controllers/general.php): AppwriteException uses its - // configured publish flag; everything else publishes only for code 0 or >= 500. - // Without this, expected client errors (e.g. Utopia DB Authorization) hit Sentry. - if ($error instanceof AppwriteException) { - $publish = $error->isPublishable(); - } else { - $publish = $error->getCode() === 0 || $error->getCode() >= 500; + // Server callbacks (pub/sub, stats) run outside a connection span; open one + // so the failure still reaches the exporters. + $span = Span::current(); + $owned = $span === null; + $span ??= Span::init($action); + + $span->setError($error); + $span->set('error.action', $action); + $span->set('project.id', $project?->getId() ?: 'n/a'); + $span->set('user.id', $user?->getId() ?: 'n/a'); + foreach ($tags as $key => $value) { + $span->set($key, \is_scalar($value) || $value === null ? $value : \json_encode($value)); } - if ($logger && $publish) { - $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - - $log = new Log(); - $log->setNamespace("realtime"); - $log->setServer(System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($error->getMessage()); - - $log->addTag('code', $error->getCode()); - $log->addTag('verboseType', get_class($error)); - $log->addTag('projectId', $project?->getId() ?: 'n/a'); - $log->addTag('userId', $user?->getId() ?: 'n/a'); - - foreach ($tags as $key => $value) { - $log->addTag($key, $value ?: 'n/a'); - } - - $log->addExtra('file', $error->getFile()); - $log->addExtra('line', $error->getLine()); - $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('detailedTrace', $error->getTrace()); - $log->addExtra('roles', $authorization?->getRoles() ?? []); - - $log->setAction($action); - - $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - - try { - $responseCode = $logger->addLog($log); - Console::info('Error log pushed with status code: ' . $responseCode); - } catch (Throwable $th) { - Console::error('Error pushing log: ' . $th->getMessage()); - } + if ($owned) { + $span->finish(error: $error); } Console::error('[Error] Type: ' . get_class($error)); diff --git a/app/worker.php b/app/worker.php index da9c42dca93..6dd7c509d1a 100644 --- a/app/worker.php +++ b/app/worker.php @@ -11,8 +11,6 @@ use Utopia\Console; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\Logger\Log; -use Utopia\Logger\Logger; use Utopia\Platform\Service; use Utopia\Queue\Adapter\Swoole; use Utopia\Queue\Broker\Pool as BrokerPool; @@ -37,8 +35,6 @@ $container->set('project', fn () => new Document([]), []); -$container->set('log', fn () => new Log(), []); - $container->set('certificates', function () { $email = System::getEnv('_APP_EMAIL_CERTIFICATES', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')); if (empty($email)) { @@ -154,40 +150,10 @@ $worker ->error() ->inject('error') - ->inject('logger') - ->inject('log') ->inject('project') - ->inject('authorization') - ->action(function (Throwable $error, ?Logger $logger, Log $log, Document $project, Authorization $authorization) { - $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - + ->action(function (Throwable $error, Document $project) { Span::current()?->setError($error); - - if ($logger) { - $log->setNamespace('appwrite-worker'); - $log->setServer(System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($error->getMessage()); - $log->setAction('appwrite-queue-worker'); - $log->addTag('verboseType', get_class($error)); - $log->addTag('code', $error->getCode()); - $log->addTag('projectId', $project->getId()); - $log->addExtra('file', $error->getFile()); - $log->addExtra('line', $error->getLine()); - $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('roles', $authorization->getRoles()); - - $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - - try { - $responseCode = $logger->addLog($log); - Console::info('Error log pushed with status code: ' . $responseCode); - } catch (Throwable $th) { - Console::error('Error pushing log: ' . $th->getMessage()); - } - } + Span::add('project.id', $project->getId()); Console::error('[Error] Type: ' . get_class($error)); Console::error('[Error] Message: ' . $error->getMessage()); diff --git a/composer.json b/composer.json index 7e25dae6052..f6da65513ce 100644 --- a/composer.json +++ b/composer.json @@ -91,7 +91,6 @@ "utopia-php/image": "0.8.*", "utopia-php/locale": "0.8.*", "utopia-php/lock": "0.3.*", - "utopia-php/logger": "0.8.*", "utopia-php/messaging": "^2.1", "utopia-php/migration": "^2.0.0", "utopia-php/platform": "1.0.0-rc20", diff --git a/composer.lock b/composer.lock index 8cbc3cf1c7c..7d360ac9a0b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5b23ea5ac37704e78ab49bfc83818f42", + "content-hash": "35704e3e15b6146538aed2bdeab97ca3", "packages": [ { "name": "adhocore/jwt", @@ -4249,61 +4249,6 @@ }, "time": "2026-08-21T15:12:28+00:00" }, - { - "name": "utopia-php/logger", - "version": "0.8.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/logger.git", - "reference": "132236c42222cd614cb882938a48f8729ef3118b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/132236c42222cd614cb882938a48f8729ef3118b", - "reference": "132236c42222cd614cb882938a48f8729ef3118b", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "utopia-php/fetch": "^1.1" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.9.x-dev", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Logger\\": "src/Logger" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", - "keywords": [ - "appsignal", - "errors", - "framework", - "logger", - "logging", - "logs", - "php", - "raygun", - "sentry", - "upf", - "utopia", - "warnings" - ], - "support": { - "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.8.0" - }, - "time": "2026-05-05T06:04:27+00:00" - }, { "name": "utopia-php/messaging", "version": "2.1.0", diff --git a/docker-compose.yml b/docker-compose.yml index a1371d03669..6d9be6bb0a4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -246,8 +246,6 @@ services: - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET - _APP_ASSISTANT_OPENAI_API_KEY - _APP_CONSOLE_COUNTRIES_DENYLIST - - _APP_EXPERIMENT_LOGGING_PROVIDER - - _APP_EXPERIMENT_LOGGING_CONFIG - _APP_DATABASE_SHARED_TABLES - _APP_DATABASE_SHARED_NAMESPACE - _APP_FUNCTIONS_CREATION_ABUSE_LIMIT @@ -350,7 +348,6 @@ services: - _APP_CONNECTIONS_DATABASE_VECTORSDB - _APP_LOGGING_CONFIG - _APP_LOGGING_FORMAT - - _APP_LOGGING_CONFIG_REALTIME - _APP_DATABASE_SHARED_TABLES - _APP_LIMIT_DATABASE_BATCH - _APP_DOCUMENTSDB @@ -431,7 +428,6 @@ services: - _APP_REDIS_PASS - _APP_LOGGING_CONFIG - _APP_LOGGING_FORMAT - - _APP_LOGGING_PROVIDER - _APP_QUEUE_NAME - _APP_DATABASE_SHARED_TABLES - _APP_CONSOLE_URL_SCHEME=root @@ -1161,7 +1157,6 @@ services: - _APP_DOCKER_HUB_PASSWORD - _APP_LOGGING_CONFIG - _APP_LOGGING_FORMAT - - _APP_LOGGING_PROVIDER - _APP_DATABASE_SHARED_TABLES appwrite-worker-mails: profiles: diff --git a/src/Appwrite/Execution/Store.php b/src/Appwrite/Execution/Store.php index 0868c145b72..6342bbac591 100644 --- a/src/Appwrite/Execution/Store.php +++ b/src/Appwrite/Execution/Store.php @@ -10,14 +10,12 @@ use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Query; use Utopia\DSN\DSN; -use Utopia\Logger\Log; -use Utopia\Logger\Logger; use Utopia\Psr7\Method; use Utopia\Psr7\Request\Factory as RequestFactory; use Utopia\Query\Builder\ClickHouse as ClickHouseBuilder; use Utopia\Query\Builder\ClickHouse\Format; use Utopia\Query\Builder\Statement; -use Utopia\System\System; +use Utopia\Span\Span; /** * ClickHouse persistence for function and site executions. @@ -117,7 +115,6 @@ public function __construct( private readonly string $dsn, private readonly ?ClientInterface $client, private readonly int $retention = 1_209_600, - private readonly ?Logger $logger = null, ) { $this->requestFactory = new RequestFactory(); } @@ -899,7 +896,8 @@ private function mirror(string $operation, callable $callback): void private function report(string $operation, Throwable $th): void { - if ($this->logger === null) { + // Nothing to record on: skip the rate-limit bookkeeping as well. + if (Span::current() === null) { return; } @@ -909,27 +907,12 @@ private function report(string $operation, Throwable $th): void } self::$lastReports[$operation] = $now; - $log = new Log(); - $log->setNamespace('executions'); - $log->setServer(System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $log->setVersion(System::getEnv('_APP_VERSION', 'UNKNOWN')); - $log->setType(Log::TYPE_ERROR); - $log->setMessage("ClickHouse execution mirror {$operation} failed: {$th->getMessage()}"); - $log->setAction("executions.mirror.{$operation}"); - $log->setEnvironment(System::getEnv('_APP_ENV', 'development') === 'production' - ? Log::ENVIRONMENT_PRODUCTION - : Log::ENVIRONMENT_STAGING); - $log->addTag('operation', $operation); - $log->addTag('exception', $th::class); - $log->addTag('code', $th->getCode()); - $log->addExtra('file', $th->getFile()); - $log->addExtra('line', $th->getLine()); - $log->addExtra('trace', $th->getTraceAsString()); - - try { - $this->logger->addLog($log); - } catch (Throwable) { - } + // The mirror is best effort, so the failure is recorded on the request + // span without failing it. + Span::add('executions.mirror.operation', $operation); + Span::add('executions.mirror.error', $th->getMessage()); + Span::add('executions.mirror.exception', $th::class); + Span::add('executions.mirror.code', $th->getCode()); } /** @param array $params */ diff --git a/src/Appwrite/Locking/Lock.php b/src/Appwrite/Locking/Lock.php index f808ce404c7..40f161d1a79 100644 --- a/src/Appwrite/Locking/Lock.php +++ b/src/Appwrite/Locking/Lock.php @@ -8,8 +8,7 @@ use Utopia\Console; use Utopia\Database\Document; use Utopia\Lock\Lock as UtopiaLock; -use Utopia\Logger\Log; -use Utopia\Logger\Logger; +use Utopia\Span\Span; use Utopia\System\System; use Utopia\Telemetry\Adapter as Telemetry; @@ -52,7 +51,6 @@ final class Lock public function __construct( Closure $useLock, Telemetry $telemetry, - private readonly ?Logger $logger, Document $project, ) { $this->useLock = $useLock; @@ -188,16 +186,12 @@ private function executeWithLock( } /** - * Rate-limit backend/release reports so outages don't flood Sentry. + * Rate-limit backend/release reports so outages don't flood the span exporters. */ private function reportError(string $action, string $key, string $target, Throwable $e): void { Console::warning("Lock {$action} for {$key}: {$e->getMessage()}"); - if ($this->logger === null) { - return; - } - $bucket = $action.':'.$target; $now = time(); if ((self::$lastReportAt[$bucket] ?? 0) + self::REPORT_RATE_LIMIT_SECONDS > $now) { @@ -205,28 +199,12 @@ private function reportError(string $action, string $key, string $target, Throwa } self::$lastReportAt[$bucket] = $now; - $log = new Log(); - $log->setNamespace('http'); - $log->setServer(System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $log->setVersion(APP_VERSION_STABLE); - $log->setType(Log::TYPE_WARNING); - $log->setMessage('Distributed lock '.$action.': '.$e->getMessage()); - $log->setAction("lock.{$action}"); - $log->setEnvironment(System::getEnv('_APP_ENV', 'development') === 'production' - ? Log::ENVIRONMENT_PRODUCTION - : Log::ENVIRONMENT_STAGING); - $log->addTag('lock.target', $target); - $log->addTag('lock.project', $this->projectInternalId); + Span::add('lock.action', $action); + Span::add('lock.target', $target); + Span::add('lock.project', $this->projectInternalId); // Strip trailing document ID to keep aggregator cardinality bounded. - $log->addTag('lock.key_pattern', preg_replace('/:[^:]+$/', ':*', $key)); - $log->addTag('code', $e->getCode()); - $log->addExtra('file', $e->getFile()); - $log->addExtra('line', $e->getLine()); - $log->addExtra('trace', $e->getTraceAsString()); - - try { - $this->logger->addLog($log); - } catch (Throwable) { - } + Span::add('lock.key_pattern', preg_replace('/:[^:]+$/', ':*', $key)); + Span::add('lock.error', $e->getMessage()); + Span::add('lock.error_code', $e->getCode()); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Embeddings/Text/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Embeddings/Text/Create.php index edbe0463c29..ef2068a1e59 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Embeddings/Text/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Embeddings/Text/Create.php @@ -14,10 +14,8 @@ use Utopia\Agents\Agent; use Utopia\Database\Document; use Utopia\Http\Adapter\Swoole\Response as SwooleResponse; -use Utopia\Logger\Log; -use Utopia\Logger\Logger; use Utopia\Platform\Enum; -use Utopia\System\System; +use Utopia\Span\Span; use Utopia\Validator\ArrayList; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; @@ -81,12 +79,10 @@ public function __construct() ->inject('project') ->inject('embeddingAgent') ->inject('usage') - ->inject('log') - ->inject('logger') ->callback($this->action(...)); } - public function action(array $texts, string $model, UtopiaResponse $response, Document $project, Agent $embeddingAgent, Context $usage, Log $log, ?Logger $logger): void + public function action(array $texts, string $model, UtopiaResponse $response, Document $project, Agent $embeddingAgent, Context $usage): void { $adapter = $embeddingAgent->getAdapter(); $adapter->setModel($model); @@ -116,7 +112,7 @@ public function action(array $texts, string $model, UtopiaResponse $response, Do } } catch (\Exception $e) { $totalErrors += \count($batch); - $this->logError($e, $model, $project, $log, $logger); + $this->logError($e, $model); // One error result per text in the failed batch. foreach ($batch as $ignored) { @@ -160,26 +156,10 @@ private function embeddingResult(string $model, int $dimension, array $embedding ]); } - private function logError(\Throwable $e, string $model, Document $project, Log $log, ?Logger $logger): void + private function logError(\Throwable $e, string $model): void { - if ($logger === null) { - return; - } - - $log->setNamespace('http'); - $log->setServer(System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname())); - $log->setVersion(System::getEnv('_APP_VERSION', 'UNKNOWN')); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($e->getMessage()); - - $log->addTag('embeddingModel', $model); - $log->addTag('code', $e->getCode()); - $log->addTag('projectId', $project->getId()); - - $log->addExtra('file', $e->getFile()); - $log->addExtra('line', $e->getLine()); - $log->addExtra('trace', $e->getTraceAsString()); - - $logger->addLog($log); + Span::add('embedding.model', $model); + Span::add('embedding.error', $e->getMessage()); + Span::add('embedding.error_code', $e->getCode()); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php index 6dae0b15ae8..a5a5f97a23d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php +++ b/src/Appwrite/Platform/Modules/Databases/Workers/Databases.php @@ -14,7 +14,6 @@ use Utopia\Database\Exception\Restricted; use Utopia\Database\Exception\Structure; use Utopia\Database\Query; -use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; use Utopia\Span\Span; @@ -39,7 +38,6 @@ public function __construct() ->inject('dbForProject') ->inject('getDatabasesDB') ->inject('queueForRealtime') - ->inject('log') ->callback($this->action(...)); } @@ -49,11 +47,10 @@ public function __construct() * @param Database $dbForPlatform * @param Database $dbForProject * @param Realtime $queueForRealtime - * @param Log $log * @return void * @throws \Exception */ - public function action(Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, callable $getDatabasesDB, Realtime $queueForRealtime, Log $log): void + public function action(Message $message, Document $project, Database $dbForPlatform, Database $dbForProject, callable $getDatabasesDB, Realtime $queueForRealtime): void { $payload = $message->getPayload(); @@ -81,14 +78,14 @@ public function action(Message $message, Document $project, Database $dbForPlatf * @var Database $dbForDatabases */ $dbForDatabases = $getDatabasesDB($database); - $log->addTag('projectId', $project->getId()); - $log->addTag('type', $type); + Span::add('project.id', $project->getId()); + Span::add('type', $type); if ($database->isEmpty()) { throw new Exception('Missing database'); } - $log->addTag('databaseId', $database->getId()); + Span::add('database.id', $database->getId()); match (\strval($type)) { DATABASE_TYPE_DELETE_DATABASE => $this->deleteDatabase($database, $dbForProject, $dbForDatabases), diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index 16fe9f66be2..eee89f1a5db 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -20,7 +20,6 @@ use Utopia\Database\Exception\Structure; use Utopia\Database\Exception\Transaction as TransactionException; use Utopia\Database\Query; -use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; use Utopia\Span\Span; @@ -71,7 +70,6 @@ public function __construct() ->inject('vcsFactory') ->inject('dbForProject') ->inject('getIsResourceBlocked') - ->inject('log') ->inject('deployments') ->callback($this->action(...)); } @@ -89,7 +87,6 @@ public function action( VcsFactory $vcsFactory, Database $dbForProject, callable $getIsResourceBlocked, - Log $log, Deployments $deployments, ): void { $payload = $message->getPayload(); @@ -106,8 +103,8 @@ public function action( $template = new Document($payload['template'] ?? []); $platform = $payload['platform'] ?? Config::getParam('platform', []); - $log->addTag('projectId', $project->getId()); - $log->addTag('type', $type); + Span::add('project.id', $project->getId()); + Span::add('type', $type); switch ($type) { case BUILD_TYPE_DEPLOYMENT: @@ -126,7 +123,6 @@ public function action( $deployment, $template, $getIsResourceBlocked, - $log, $deployments, $platform, (int) ($payload['timeout'] ?? System::getEnv('_APP_COMPUTE_BUILD_TIMEOUT', 900)) @@ -155,7 +151,6 @@ protected function buildDeployment( Document $deployment, Document $template, callable $getIsResourceBlocked, - Log $log, Deployments $deployments, array $platform, int $timeout @@ -176,7 +171,7 @@ protected function buildDeployment( default => throw new \Exception('Invalid resource type') }; - $log->addTag($resourceKey, $resource->getId()); + Span::add($resourceKey, $resource->getId()); $resource = $dbForProject->getDocument($resource->getCollection(), $resource->getId()); if ($resource->isEmpty()) { @@ -187,7 +182,7 @@ protected function buildDeployment( throw new BuildException('Resource is blocked'); } - $log->addTag('deploymentId', $deployment->getId()); + Span::add('deployment.id', $deployment->getId()); $deployment = $dbForProject->getDocument('deployments', $deployment->getId()); if ($deployment->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Proxy/Action.php b/src/Appwrite/Platform/Modules/Proxy/Action.php index ac9b894f46d..fca0c9e6004 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Action.php +++ b/src/Appwrite/Platform/Modules/Proxy/Action.php @@ -15,7 +15,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\DNS\Message\Record; use Utopia\Domains\Domain; -use Utopia\Logger\Log; +use Utopia\Span\Span; use Utopia\System\System; use Utopia\Validator\AnyOf; use Utopia\Validator\Domain as ValidatorDomain; @@ -159,10 +159,9 @@ protected function validateDomainRestrictions(string $domain, array $platform): * Verify or re-verify a rule * * @param Document $rule Rule to verify - * @param Log|null $log Log instance to add timings to * @return void */ - protected function verifyRule(Document $rule, ?Log $log = null): void + protected function verifyRule(Document $rule): void { $dnsValidatorClass = $this->dnsValidatorClass; $dnsEnv = System::getEnv('_APP_DNS', '8.8.8.8'); @@ -185,10 +184,8 @@ protected function verifyRule(Document $rule, ?Log $log = null): void $validationStart = \microtime(true); $validator = new $dnsValidatorClass($caaTarget, Record::TYPE_CAA, $dnsServers); if (!$validator->isValid($domain->get())) { - if (!\is_null($log)) { - $log->addExtra('dnsTimingCaa', \strval(\microtime(true) - $validationStart)); - $log->addTag('dnsDomain', $domain->get()); - } + Span::add('dns.timing_caa', \microtime(true) - $validationStart); + Span::add('dns.domain', $domain->get()); throw new Exception(Exception::RULE_VERIFICATION_FAILED, $validator->getDescription()); } } @@ -268,10 +265,8 @@ protected function verifyRule(Document $rule, ?Log $log = null): void $validationStart = \microtime(true); if (!$validator->isValid($domain->get())) { - if (!\is_null($log)) { - $log->addExtra('dnsTiming', \strval(\microtime(true) - $validationStart)); - $log->addTag('dnsDomain', $domain->get()); - } + Span::add('dns.timing', \microtime(true) - $validationStart); + Span::add('dns.domain', $domain->get()); throw new Exception(Exception::RULE_VERIFICATION_FAILED, $mainValidator->getDescription()); } } diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php index 00f47b955ef..8b3f5137d6d 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php @@ -15,7 +15,6 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; -use Utopia\Logger\Log; use Utopia\Platform\Scope\HTTP; use Utopia\System\System; use Utopia\Validator\Domain as ValidatorDomain; @@ -69,7 +68,6 @@ public function __construct(...$params) ->inject('queueForEvents') ->inject('dbForPlatform') ->inject('platform') - ->inject('log') ->inject('authorization') ->inject('bus') ->callback($this->action(...)); @@ -83,7 +81,6 @@ public function action( Event $queueForEvents, Database $dbForPlatform, array $platform, - Log $log, Authorization $authorization, Bus $bus, ) { @@ -120,7 +117,7 @@ public function action( if ($rule->getAttribute('status', '') === RULE_STATUS_CREATED) { try { - $this->verifyRule($rule, $log); + $this->verifyRule($rule); $rule->setAttribute('status', RULE_STATUS_CERTIFICATE_GENERATING); } catch (Exception $err) { $rule->setAttribute('logs', $err->getMessage()); diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php index 7239aa4a300..c2313b8825a 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php @@ -17,7 +17,6 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Logger\Log; use Utopia\Platform\Scope\HTTP; use Utopia\System\System; use Utopia\Validator\Domain as ValidatorDomain; @@ -76,7 +75,6 @@ public function __construct(...$params) ->inject('dbForPlatform') ->inject('dbForProject') ->inject('platform') - ->inject('log') ->inject('authorization') ->inject('bus') ->callback($this->action(...)); @@ -94,7 +92,6 @@ public function action( Database $dbForPlatform, Database $dbForProject, array $platform, - Log $log, Authorization $authorization, Bus $bus, ) { @@ -145,7 +142,7 @@ public function action( if ($rule->getAttribute('status', '') === RULE_STATUS_CREATED) { try { - $this->verifyRule($rule, $log); + $this->verifyRule($rule); $rule->setAttribute('status', RULE_STATUS_CERTIFICATE_GENERATING); } catch (Exception $err) { $rule->setAttribute('logs', $err->getMessage()); diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php index 5e3dd643189..e44308e912e 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php @@ -16,7 +16,6 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Logger\Log; use Utopia\Platform\Enum; use Utopia\Platform\Scope\HTTP; use Utopia\System\System; @@ -92,7 +91,6 @@ public function __construct(...$params) ->inject('dbForPlatform') ->inject('dbForProject') ->inject('platform') - ->inject('log') ->inject('authorization') ->inject('bus') ->callback($this->action(...)); @@ -111,7 +109,6 @@ public function action( Database $dbForPlatform, Database $dbForProject, array $platform, - Log $log, Authorization $authorization, Bus $bus, ) { @@ -164,7 +161,7 @@ public function action( if ($rule->getAttribute('status', '') === RULE_STATUS_CREATED) { try { - $this->verifyRule($rule, $log); + $this->verifyRule($rule); $rule->setAttribute('status', RULE_STATUS_CERTIFICATE_GENERATING); } catch (Exception $err) { $rule->setAttribute('logs', $err->getMessage()); diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php index bc3533adf6f..5d768a3f22d 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php @@ -17,7 +17,6 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Logger\Log; use Utopia\Platform\Scope\HTTP; use Utopia\System\System; use Utopia\Validator\Domain as ValidatorDomain; @@ -76,7 +75,6 @@ public function __construct(...$params) ->inject('dbForPlatform') ->inject('dbForProject') ->inject('platform') - ->inject('log') ->inject('authorization') ->inject('bus') ->callback($this->action(...)); @@ -94,7 +92,6 @@ public function action( Database $dbForPlatform, Database $dbForProject, array $platform, - Log $log, Authorization $authorization, Bus $bus, ) { @@ -145,7 +142,7 @@ public function action( if ($rule->getAttribute('status', '') === RULE_STATUS_CREATED) { try { - $this->verifyRule($rule, $log); + $this->verifyRule($rule); $rule->setAttribute('status', RULE_STATUS_CERTIFICATE_GENERATING); } catch (Exception $err) { $rule->setAttribute('logs', $err->getMessage()); diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Status/Update.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Status/Update.php index 6f799fd1b5f..b6631071cf0 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Status/Update.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Status/Update.php @@ -17,7 +17,6 @@ use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; -use Utopia\Logger\Log; use Utopia\Platform\Scope\HTTP; class Update extends Action @@ -64,7 +63,6 @@ public function __construct(...$params) ->inject('queueForEvents') ->inject('project') ->inject('dbForPlatform') - ->inject('log') ->inject('authorization') ->inject('bus') ->callback($this->action(...)); @@ -77,7 +75,6 @@ public function action( Event $queueForEvents, Document $project, Database $dbForPlatform, - Log $log, Authorization $authorization, Bus $bus, ) { @@ -96,7 +93,7 @@ public function action( } try { - $this->verifyRule($rule, $log); + $this->verifyRule($rule); // Reset logs and status for the rule $rule = $authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([ 'logs' => '', diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index d5e2c29f246..c1663b614f9 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -13,7 +13,6 @@ use Utopia\Domains\Domain; use Utopia\DSN\DSN; use Utopia\Http\Http; -use Utopia\Logger\Logger; use Utopia\Platform\Action; use Utopia\Pools\Group; use Utopia\Queue\Broker\Pool as BrokerPool; @@ -130,7 +129,7 @@ public function action(Registry $register, Authorization $authorization): void $providerName = $loggingProvider->getScheme(); - if (empty($providerName) || !Logger::hasProvider($providerName)) { + if ($providerName !== 'sentry') { Console::log('🔴 Logging adapter is disabled'); } else { Console::log('🟢 Logging adapter is enabled (' . $providerName . ')'); diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 0c1e317e274..9132be7e8a7 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -32,8 +32,8 @@ use Utopia\Database\Validator\Authorization as ValidatorAuthorization; use Utopia\Domains\Domain; use Utopia\Locale\Locale; -use Utopia\Logger\Log; use Utopia\Queue\Message; +use Utopia\Span\Span; use Utopia\System\System; class Certificates extends Action @@ -60,7 +60,6 @@ public function __construct(...$params) ->inject('publisherForFunctions') ->inject('queueForRealtime') ->inject('publisherForCertificates') - ->inject('log') ->inject('certificates') ->inject('plan') ->inject('authorization') @@ -77,7 +76,6 @@ public function __construct(...$params) * @param FunctionPublisher $publisherForFunctions * @param Realtime $queueForRealtime * @param Certificate $publisherForCertificates - * @param Log $log * @param Provider $certificates * @param array $plan * @param ValidatorAuthorization $authorization @@ -94,7 +92,6 @@ public function action( FunctionPublisher $publisherForFunctions, Realtime $queueForRealtime, Certificate $publisherForCertificates, - Log $log, Provider $certificates, array $plan, ValidatorAuthorization $authorization, @@ -114,15 +111,15 @@ public function action( $validationDomain = $certificateMessage->validationDomain; $action = $certificateMessage->action; - $log->addTag('domain', $domain->get()); + Span::add('domain', $domain->get()); switch ($action) { case \Appwrite\Event\Certificate::ACTION_DOMAIN_VERIFICATION: - $this->handleDomainVerificationAction($domain, $dbForPlatform, $queueForEvents, $queueForWebhooks, $publisherForFunctions, $queueForRealtime, $publisherForCertificates, $log, $authorization, $bus, $validationDomain); + $this->handleDomainVerificationAction($domain, $dbForPlatform, $queueForEvents, $queueForWebhooks, $publisherForFunctions, $queueForRealtime, $publisherForCertificates, $authorization, $bus, $validationDomain); break; case \Appwrite\Event\Certificate::ACTION_GENERATION: - $this->handleCertificateGenerationAction($domain, $domainType, $dbForPlatform, $publisherForMails, $queueForEvents, $queueForWebhooks, $publisherForFunctions, $queueForRealtime, $log, $certificates, $authorization, $bus, $skipRenewCheck, $plan, $validationDomain); + $this->handleCertificateGenerationAction($domain, $domainType, $dbForPlatform, $publisherForMails, $queueForEvents, $queueForWebhooks, $publisherForFunctions, $queueForRealtime, $certificates, $authorization, $bus, $skipRenewCheck, $plan, $validationDomain); break; default: @@ -138,7 +135,6 @@ public function action( * @param FunctionPublisher $publisherForFunctions * @param Realtime $queueForRealtime * @param Certificate $publisherForCertificates - * @param Log $log * @param ValidatorAuthorization $authorization * @param string|null $validationDomain * @return void @@ -154,7 +150,6 @@ private function handleDomainVerificationAction( FunctionPublisher $publisherForFunctions, Realtime $queueForRealtime, Certificate $publisherForCertificates, - Log $log, ValidatorAuthorization $authorization, Bus $bus, ?string $validationDomain = null @@ -177,7 +172,7 @@ private function handleDomainVerificationAction( try { // Verify DNS records - $this->validateDomain($rule, $domain, $log, $validationDomain); + $this->validateDomain($rule, $domain, $validationDomain); // Reset logs and status for the rule $rule->setAttribute('logs', ''); $rule->setAttribute('status', RULE_STATUS_CERTIFICATE_GENERATING); @@ -221,7 +216,6 @@ private function handleDomainVerificationAction( * @param Webhook $queueForWebhooks * @param FunctionPublisher $publisherForFunctions * @param Realtime $queueForRealtime - * @param Log $log * @param Provider $certificates * @param ValidatorAuthorization $authorization * @param bool $skipRenewCheck @@ -245,7 +239,6 @@ private function handleCertificateGenerationAction( Webhook $queueForWebhooks, FunctionPublisher $publisherForFunctions, Realtime $queueForRealtime, - Log $log, Provider $certificates, ValidatorAuthorization $authorization, Bus $bus, @@ -319,7 +312,7 @@ private function handleCertificateGenerationAction( // Validate domain and DNS records. Skip if job is forced if (!$skipRenewCheck) { - $this->validateDomain($rule, $domain, $log, $validationDomain); + $this->validateDomain($rule, $domain, $validationDomain); // If certificate exists already, double-check expiry date. Skip if job is forced if (!$certificates->isRenewRequired($domain->get(), $domainType)) { @@ -493,13 +486,12 @@ protected function updateRuleAndSendEvents( * * @param Document $rule Rule to validate * @param Domain $domain Domain to validate - * @param Log $log Logger for adding metrics * @param string|null $validationDomain Override for main domain check * * @return void * @throws Exception */ - private function validateDomain(Document $rule, Domain $domain, Log $log, ?string $validationDomain = null): void + private function validateDomain(Document $rule, Domain $domain, ?string $validationDomain = null): void { $mainDomain = $validationDomain ?? $this->getMainDomain(); $isMainDomain = !isset($mainDomain) || $domain->get() === $mainDomain; @@ -515,7 +507,7 @@ private function validateDomain(Document $rule, Domain $domain, Log $log, ?strin } try { - $this->verifyRule($rule, $log); + $this->verifyRule($rule); } catch (AppwriteException $err) { $msg = $err->getMessage() . "\n"; $msg .= "Verify your DNS records are correctly configured and try again.\n"; diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 8e926e76885..438b1e2fa17 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -32,7 +32,6 @@ use Utopia\Database\Exception\Structure; use Utopia\Database\Query; use Utopia\DSN\DSN; -use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; use Utopia\Span\Span; @@ -74,7 +73,6 @@ public function __construct() ->inject('executor') ->inject('executionRetention') ->inject('executionsRetentionCount') - ->inject('log') ->inject('publisherForDeletes') ->inject('publisherForUsage') ->inject('bus') @@ -106,7 +104,6 @@ public function actionWithUsage( Executor $executor, string $executionRetention, int $executionsRetentionCount, - Log $log, DeletePublisher $publisherForDeletes, UsagePublisher $publisherForUsage, Bus $bus, @@ -141,7 +138,6 @@ public function actionWithUsage( $executor, $executionRetention, $executionsRetentionCount, - $log, $publisherForDeletes, $publisherForUsage, $bus, @@ -236,7 +232,6 @@ public function action( Executor $executor, string $executionRetention, int $executionsRetentionCount, - Log $log, DeletePublisher $publisherForDeletes, UsagePublisher $publisherForUsage, Bus $bus, @@ -256,8 +251,8 @@ public function action( $resourceType = $deleteMessage->resourceType; $document = $deleteMessage->document ?? new Document(); - $log->addTag('projectId', $project->getId()); - $log->addTag('type', $type); + Span::add('project.id', $project->getId()); + Span::add('type', $type); switch (\strval($type)) { case DELETE_TYPE_DOCUMENT: diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 409844ff8c2..ff437cc47b6 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -24,7 +24,6 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; -use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; use Utopia\Span\Span; @@ -57,7 +56,6 @@ public function __construct() ->inject('queueForRealtime') ->inject('queueForEvents') ->inject('bus') - ->inject('log') ->inject('executor') ->inject('getIsResourceBlocked') ->inject('locks') @@ -74,7 +72,6 @@ public function action( Realtime $queueForRealtime, Event $queueForEvents, Bus $bus, - Log $log, Executor $executor, callable $getIsResourceBlocked, callable $locks @@ -155,9 +152,9 @@ functionId: $functionId, $function = $dbForProject->getDocument('functions', $functionId); } - $log->addTag('functionId', $function->getId()); - $log->addTag('projectId', $project->getId()); - $log->addTag('type', $type); + Span::add('function.id', $function->getId()); + Span::add('project.id', $project->getId()); + Span::add('type', $type); if (empty($events) && !$function->isEmpty()) { Span::add('function.id', $function->getId()); @@ -199,7 +196,6 @@ functionId: $functionId, Console::success('Iterating function: ' . $function->getAttribute('name')); $this->execute( - log: $log, dbForProject: $dbForProject, queueForWebhooks: $queueForWebhooks, publisherForFunctions: $publisherForFunctions, @@ -243,7 +239,6 @@ function: $function, $execution = new Document($payload['execution'] ?? []); $user = new Document($payload['user'] ?? []); $this->execute( - log: $log, dbForProject: $dbForProject, queueForWebhooks: $queueForWebhooks, publisherForFunctions: $publisherForFunctions, @@ -279,7 +274,6 @@ function: $function, } $this->execute( - log: $log, dbForProject: $dbForProject, queueForWebhooks: $queueForWebhooks, publisherForFunctions: $publisherForFunctions, @@ -474,7 +468,6 @@ private function fail( } /** - * @param Log $log * @param Database $dbForProject * @param FunctionPublisher $publisherForFunctions * @param Realtime $queueForRealtime @@ -495,7 +488,6 @@ private function fail( * @return void */ private function execute( - Log $log, Database $dbForProject, Webhook $queueForWebhooks, FunctionPublisher $publisherForFunctions, @@ -526,7 +518,7 @@ private function execute( Span::add('deployment.id', $deploymentId); Span::add('execution.trigger', $trigger); - $log->addTag('deploymentId', $deploymentId); + Span::add('deployment.id', $deploymentId); /** Check if deployment exists */ $deployment = $dbForProject->getDocument('deployments', $deploymentId); diff --git a/src/Appwrite/Platform/Workers/Mails.php b/src/Appwrite/Platform/Workers/Mails.php index 29cb7b642f8..d10675218a2 100644 --- a/src/Appwrite/Platform/Workers/Mails.php +++ b/src/Appwrite/Platform/Workers/Mails.php @@ -5,7 +5,6 @@ use Appwrite\Template\Template; use Exception; use Utopia\Database\Document; -use Utopia\Logger\Log; use Utopia\Messaging\Adapter\Email as EmailAdapter; use Utopia\Messaging\Adapter\Email\SMTP; use Utopia\Messaging\Messages\Email as EmailMessage; @@ -39,7 +38,6 @@ public function __construct() ->inject('message') ->inject('project') ->inject('register') - ->inject('log') ->inject('telemetry') ->callback($this->action(...)); } @@ -56,12 +54,11 @@ public function __construct() * @param Message $message * @param Document $project * @param Registry $register - * @param Log $log * @param Telemetry $telemetry * @return void * @throws Exception */ - public function action(Message $message, Document $project, Registry $register, Log $log, Telemetry $telemetry): void + public function action(Message $message, Document $project, Registry $register, Telemetry $telemetry): void { $payload = $message->getPayload(); @@ -76,7 +73,7 @@ public function action(Message $message, Document $project, Registry $register, } $type = empty($smtp) ? 'cloud' : 'smtp'; - $log->addTag('type', $type); + Span::add('type', $type); $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; $hostname = System::getEnv('_APP_CONSOLE_DOMAIN'); diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 9730c3ca60a..106be49dd4f 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -14,7 +14,6 @@ use Utopia\Database\Query; use Utopia\DSN\DSN; use Utopia\Lock\Semaphore; -use Utopia\Logger\Log; use Utopia\Messaging\Adapter\Email as EmailAdapter; use Utopia\Messaging\Adapter\Email\Mailgun; use Utopia\Messaging\Adapter\Email\Resend; @@ -72,7 +71,6 @@ public function __construct() ->desc('Messaging worker') ->inject('message') ->inject('project') - ->inject('log') ->inject('dbForProject') ->inject('deviceForFiles') ->inject('publisherForUsage') @@ -83,7 +81,6 @@ public function __construct() /** * @param Message $message * @param Document $project - * @param Log $log * @param Database $dbForProject * @param Device $deviceForFiles * @param UsagePublisher $publisherForUsage @@ -94,7 +91,6 @@ public function __construct() public function action( Message $message, Document $project, - Log $log, Database $dbForProject, Device $deviceForFiles, UsagePublisher $publisherForUsage, @@ -116,7 +112,7 @@ public function action( $message = new Document($payload['message'] ?? []); $recipients = $payload['recipients'] ?? []; - $this->sendInternalSMSMessage($message, $project, $recipients, $log); + $this->sendInternalSMSMessage($message, $project, $recipients); break; case MESSAGE_SEND_TYPE_EXTERNAL: $messageId = $payload['messageId']; @@ -822,7 +818,7 @@ private function buildMessage( return $data; } - private function sendInternalSMSMessage(Document $message, Document $project, array $recipients, Log $log): void + private function sendInternalSMSMessage(Document $message, Document $project, array $recipients): void { if ($this->adapter === null) { $this->adapter = $this->createInternalSMSAdapter(); diff --git a/src/Appwrite/Platform/Workers/Notifications.php b/src/Appwrite/Platform/Workers/Notifications.php index f882ed6be44..caca7bb0601 100644 --- a/src/Appwrite/Platform/Workers/Notifications.php +++ b/src/Appwrite/Platform/Workers/Notifications.php @@ -16,7 +16,6 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\UID; -use Utopia\Logger\Log; use Utopia\Messaging\Adapter\Email as EmailAdapter; use Utopia\Messaging\Adapter\Email\SMTP; use Utopia\Messaging\Messages\Email as EmailMessage; @@ -24,6 +23,7 @@ use Utopia\Platform\Action; use Utopia\Queue\Message; use Utopia\Registry\Registry; +use Utopia\Span\Span; use Utopia\System\System; class Notifications extends Action @@ -52,11 +52,10 @@ public function __construct() ->inject('project') ->inject('register') ->inject('dbForPlatform') - ->inject('log') ->callback($this->action(...)); } - public function action(Message $message, Document $project, Registry $register, Database $dbForPlatform, Log $log): void + public function action(Message $message, Document $project, Registry $register, Database $dbForPlatform): void { $payload = $message->getPayload(); @@ -85,19 +84,19 @@ public function action(Message $message, Document $project, Registry $register, } if ($messageId !== '' && $this->alreadyDelivered($dbForPlatform, self::buildAlertId($messageId, $recipient))) { - $log->addTag('dedup', 'hit'); - $log->addTag('channel', $channel); + Span::add('dedup', 'hit'); + Span::add('channel', $channel); continue; } try { - $alertId = $this->dispatch($recipient, $messageId, $payload, $project, $register, $dbForPlatform, $log); + $alertId = $this->dispatch($recipient, $messageId, $payload, $project, $register, $dbForPlatform); if ($messageId !== '' && $channel === NOTIFICATION_TYPE_WEBHOOK && $alertId === null) { $this->persistAlert($dbForPlatform, $messageId, $recipient, $payload, $project); } } catch (Throwable $error) { - $log->addTag('channel', $channel); - $log->addTag('error', $error->getMessage()); + Span::add('channel', $channel); + Span::add('channel.error', $error->getMessage()); $failure ??= $error; } } @@ -174,14 +173,13 @@ protected function dispatch( Document $project, Registry $register, Database $dbForPlatform, - Log $log, ): ?string { $channel = $recipient['channel']; return match ($channel) { - NOTIFICATION_TYPE_EMAIL => $this->dispatchEmail($recipient, $messageId, $payload, $project, $register, $dbForPlatform, $log), + NOTIFICATION_TYPE_EMAIL => $this->dispatchEmail($recipient, $messageId, $payload, $project, $register, $dbForPlatform), NOTIFICATION_TYPE_CONSOLE => $this->dispatchConsole($recipient, $messageId, $payload, $project, $dbForPlatform), - NOTIFICATION_TYPE_WEBHOOK => $this->dispatchWebhook($recipient, $payload, $log), + NOTIFICATION_TYPE_WEBHOOK => $this->dispatchWebhook($recipient, $payload), default => throw new Exception('Unsupported notification channel: ' . $channel), }; } @@ -196,18 +194,17 @@ protected function dispatchEmail( Document $project, Registry $register, Database $dbForPlatform, - Log $log, ): ?string { $address = $recipient['address']; $smtp = $this->resolveSmtpConfig($project, $payload); if (empty($smtp) && empty(System::getEnv('_APP_SMTP_HOST'))) { - $log->addTag('email_skipped', 'no_smtp'); + Span::add('email.skipped', 'no_smtp'); throw new Exception('Skipped mail processing. No SMTP configuration has been set.'); } $type = empty($smtp) ? 'cloud' : 'smtp'; - $log->addTag('type', $type); + Span::add('type', $type); $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'disabled' ? 'http' : 'https'; $consoleHostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', 'localhost')); @@ -410,7 +407,7 @@ protected function dispatchConsole(array $recipient, string $messageId, array $p /** * @param array{address: string, channel: string, signatureKey?: string, resourceType: string, resourceId: string, resourceInternalId: string, parentResourceType: string, parentResourceId: string, parentResourceInternalId: string} $recipient */ - protected function dispatchWebhook(array $recipient, array $payload, Log $log): ?string + protected function dispatchWebhook(array $recipient, array $payload): ?string { $address = $recipient['address']; $signatureKey = $recipient['signatureKey'] ?? null; @@ -426,7 +423,7 @@ protected function dispatchWebhook(array $recipient, array $payload, Log $log): ]; if ($signatureKey === null || $signatureKey === '') { - $log->addTag('webhook_signed', 'false'); + Span::add('webhook.signed', 'false'); } $message = new WebhookMessage( diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index 3da5eab64e3..76667cc3506 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -15,13 +15,13 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; -use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Psr7\ContentType; use Utopia\Psr7\Header; use Utopia\Psr7\Method; use Utopia\Psr7\Request\Factory as RequestFactory; use Utopia\Queue\Message; +use Utopia\Span\Span; use Utopia\System\System; class Webhooks extends Action @@ -45,7 +45,6 @@ public function __construct() ->inject('dbForPlatform') ->inject('publisherForNotifications') ->inject('publisherForUsage') - ->inject('log') ->inject('plan') ->callback($this->action(...)); } @@ -56,12 +55,11 @@ public function __construct() * @param Database $dbForPlatform * @param NotificationPublisher $publisherForNotifications * @param UsagePublisher $publisherForUsage - * @param Log $log * @param array $plan * @return void * @throws Exception */ - public function action(Message $message, Document $project, Database $dbForPlatform, NotificationPublisher $publisherForNotifications, UsagePublisher $publisherForUsage, Log $log, array $plan): void + public function action(Message $message, Document $project, Database $dbForPlatform, NotificationPublisher $publisherForNotifications, UsagePublisher $publisherForUsage, array $plan): void { $payload = $message->getPayload(); @@ -73,7 +71,7 @@ public function action(Message $message, Document $project, Database $dbForPlatf $webhookPayload = json_encode($payload['payload']); $user = new Document($payload['user'] ?? []); - $log->addTag('projectId', $project->getId()); + Span::add('project.id', $project->getId()); $errors = []; foreach ($project->getAttribute('webhooks', []) as $webhook) { diff --git a/tests/unit/Execution/StoreTest.php b/tests/unit/Execution/StoreTest.php index 6c77f3bc861..a04e34802cd 100644 --- a/tests/unit/Execution/StoreTest.php +++ b/tests/unit/Execution/StoreTest.php @@ -11,10 +11,10 @@ use Psr\Http\Message\ResponseInterface; use Utopia\Database\Document; use Utopia\Database\Query; -use Utopia\Logger\Log; -use Utopia\Logger\Logger; use Utopia\Psr7\Response; use Utopia\Psr7\Stream; +use Utopia\Span\Span; +use Utopia\Span\Storage\Memory; final class StoreTest extends TestCase { @@ -269,32 +269,32 @@ public function testSetupFailuresRemainVisible(): void $this->store(new FailingClient())->setup(); } - public function testMirrorFailuresAreReportedToConfiguredLogger(): void + public function testMirrorFailuresAreRecordedOnTheCurrentSpan(): void { - $logger = $this->createMock(Logger::class); - $logger->expects($this->once()) - ->method('addLog') - ->with($this->callback(function (Log $log): bool { - $this->assertSame(Log::TYPE_ERROR, $log->getType()); - $this->assertSame('executions.mirror.upsert', $log->getAction()); - $this->assertStringContainsString('ClickHouse unavailable', $log->getMessage()); - return true; - })); - - $this->store(new FailingClient(), $logger)->create('project', new Document([ - '$id' => 'execution', - '$createdAt' => '2026-08-25T10:00:00.000+00:00', - 'status' => 'completed', - ])); + Span::setStorage(new Memory()); + $span = Span::init('test.executions'); + + try { + $this->store(new FailingClient())->create('project', new Document([ + '$id' => 'execution', + '$createdAt' => '2026-08-25T10:00:00.000+00:00', + 'status' => 'completed', + ])); + + $this->assertSame('upsert', $span->get('executions.mirror.operation')); + $this->assertStringContainsString('ClickHouse unavailable', (string) $span->get('executions.mirror.error')); + $this->assertNotInstanceOf(\Throwable::class, $span->getError(), 'a failed mirror must not fail the request span'); + } finally { + Span::setStorage(null); + } } - private function store(ClientInterface $client, ?Logger $logger = null): Store + private function store(ClientInterface $client): Store { return new Store( enabled: true, dsn: 'http://appwrite:secret@clickhouse:8123/appwrite', client: $client, - logger: $logger, ); } diff --git a/tests/unit/Locking/LockTest.php b/tests/unit/Locking/LockTest.php index d4e9a9b5121..dbfb7687b0b 100644 --- a/tests/unit/Locking/LockTest.php +++ b/tests/unit/Locking/LockTest.php @@ -11,9 +11,8 @@ use Utopia\Database\Document; use Utopia\Lock\Exception\Contention as LockContention; use Utopia\Lock\Lock as UtopiaLock; -use Utopia\Logger\Adapter as LoggerAdapter; -use Utopia\Logger\Log; -use Utopia\Logger\Logger; +use Utopia\Span\Span; +use Utopia\Span\Storage\Memory; use Utopia\Telemetry\Adapter\None as NoTelemetry; if (! \class_exists(\RedisException::class)) { @@ -53,7 +52,6 @@ private function makeLock(): Lock return new Lock( $this->withLock(), new NoTelemetry(), - null, $this->project, ); } @@ -89,7 +87,6 @@ function (string $key, int $lockTtl, \Closure $callback) use (&$ttl, &$timeout): })); }, new NoTelemetry(), - null, $this->project, ); @@ -129,7 +126,6 @@ function (string $key, int $lockTtl, \Closure $callback) use (&$ttl, &$timeout): })); }, new NoTelemetry(), - null, $this->project, ); @@ -144,7 +140,6 @@ public function testBestEffortBackendErrorRunsCallbackUnlocked(): void $lock = new Lock( fn (string $key, int $ttl, \Closure $callback): mixed => $callback(new ThrowingAcquireLock(new \RedisException('redis unavailable'))), new NoTelemetry(), - null, $this->project, ); @@ -156,25 +151,28 @@ public function testBestEffortBackendErrorRunsCallbackUnlocked(): void $this->assertTrue($called); } - public function testBackendErrorReportDoesNotMutateRequestLog(): void + public function testBackendErrorIsRecordedOnCurrentSpan(): void { - $requestLog = new Log(); - $adapter = new RecordingLoggerAdapter(); - $logger = new Logger($adapter); + Span::setStorage(new Memory()); + $span = Span::init('test.lock'); - $lock = new Lock( - fn (string $key, int $ttl, \Closure $callback): mixed => $callback(new ThrowingAcquireLock(new \RedisException('redis unavailable'))), - new NoTelemetry(), - $logger, - $this->project, - ); - - $lock->tryWithKey(self::KEY_PREFIX.'keys:k1', fn () => null, target: 'keys'); - - $this->assertCount(1, $adapter->logs); - $this->assertNotSame($requestLog, $adapter->logs[0]); - $this->assertSame([], $requestLog->getTags()); - $this->assertSame([], $requestLog->getExtra()); + try { + $lock = new Lock( + fn (string $key, int $ttl, \Closure $callback): mixed => $callback(new ThrowingAcquireLock(new \RedisException('redis unavailable'))), + new NoTelemetry(), + $this->project, + ); + + // A target no other test reports on, so the per-target rate limit cannot swallow this report. + $lock->tryWithKey(self::KEY_PREFIX.'keys:k1', fn () => null, target: 'span-report'); + + $this->assertSame('span-report', $span->get('lock.target')); + $this->assertSame(self::KEY_PREFIX.'keys:*', $span->get('lock.key_pattern')); + $this->assertSame('redis unavailable', $span->get('lock.error')); + $this->assertNotInstanceOf(\Throwable::class, $span->getError(), 'a lock backend failure must not fail the request span'); + } finally { + Span::setStorage(null); + } } public function testWithKeyBackendErrorRunsCallbackUnlocked(): void @@ -182,7 +180,6 @@ public function testWithKeyBackendErrorRunsCallbackUnlocked(): void $lock = new Lock( fn (string $key, int $ttl, \Closure $callback): mixed => $callback(new ThrowingAcquireLock(new \RedisException('redis unavailable'))), new NoTelemetry(), - null, $this->project, ); @@ -199,7 +196,6 @@ public function testBestEffortPoolCheckoutErrorRunsCallbackUnlocked(): void $lock = new Lock( fn (string $key, int $ttl, \Closure $callback): mixed => throw new \RedisException('pool unavailable'), new NoTelemetry(), - null, $this->project, ); @@ -224,7 +220,6 @@ public function testReleaseErrorAfterCallbackIsLoggedButNotThrown(): void $lock = new Lock( fn (string $key, int $ttl, \Closure $callback): mixed => $callback(new ThrowingReleaseLock(new MemoryLock($key, $this->heldLocks), new \RedisException('release failed'))), new NoTelemetry(), - null, $this->project, ); @@ -319,7 +314,6 @@ public function testPoolCheckoutExceptionRunsCallbackUnlocked(): void $lock = new Lock( fn (string $key, int $ttl, \Closure $callback): mixed => throw new \Exception('Pool \'lock\' is empty'), new NoTelemetry(), - null, $this->project, ); @@ -336,7 +330,6 @@ public function testNonPoolExceptionIsNotSwallowed(): void $lock = new Lock( fn (string $key, int $ttl, \Closure $callback): mixed => throw new \RuntimeException('unexpected'), new NoTelemetry(), - null, $this->project, ); @@ -507,43 +500,3 @@ public function withLock(callable $callback, float $timeout = 0.0): mixed final class LockingRedisException extends \Exception { } - -final class RecordingLoggerAdapter extends LoggerAdapter -{ - /** - * @var list - */ - public array $logs = []; - - public static function getName(): string - { - return 'recording'; - } - - public function push(Log $log): int - { - $this->logs[] = $log; - - return 200; - } - - public function getSupportedTypes(): array - { - return [ - Log::TYPE_WARNING, - ]; - } - - public function getSupportedEnvironments(): array - { - return [ - Log::ENVIRONMENT_PRODUCTION, - Log::ENVIRONMENT_STAGING, - ]; - } - - public function getSupportedBreadcrumbTypes(): array - { - return []; - } -} diff --git a/tests/unit/Platform/Workers/DatabasesTest.php b/tests/unit/Platform/Workers/DatabasesTest.php index 4449393fcf8..b624533b320 100644 --- a/tests/unit/Platform/Workers/DatabasesTest.php +++ b/tests/unit/Platform/Workers/DatabasesTest.php @@ -9,7 +9,6 @@ use PHPUnit\Framework\TestCase; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Logger\Log; use Utopia\Queue\Message; require_once __DIR__ . '/../../../../app/init.php'; @@ -85,7 +84,6 @@ protected function trigger( $dbForProject, static fn () => $dbForDatabases, $this->createStub(Realtime::class), - $this->createStub(Log::class), ); } } diff --git a/tests/unit/Platform/Workers/MailsTest.php b/tests/unit/Platform/Workers/MailsTest.php index 469fce17b4d..946884edbad 100644 --- a/tests/unit/Platform/Workers/MailsTest.php +++ b/tests/unit/Platform/Workers/MailsTest.php @@ -7,7 +7,6 @@ use Appwrite\Platform\Workers\Mails; use PHPUnit\Framework\TestCase; use Utopia\Database\Document; -use Utopia\Logger\Log; use Utopia\Messaging\Adapter\Email as EmailAdapter; use Utopia\Messaging\Messages\Email as EmailMessage; use Utopia\Queue\Message; @@ -102,7 +101,6 @@ public function testLegacyMailPayloadIsSentByMailsWorker(): void ]), new Document(['$id' => 'project-x']), $registry, - new Log(), new None(), ); } finally { @@ -171,7 +169,6 @@ private function assertMailWorkerThrows(SpyMailAdapter $adapter, string $expecte ]), new Document(['$id' => 'project-x']), $registry, - new Log(), new None(), ); } finally { diff --git a/tests/unit/Platform/Workers/NotificationsTest.php b/tests/unit/Platform/Workers/NotificationsTest.php index dcfc83b2b27..dc04a34bcd7 100644 --- a/tests/unit/Platform/Workers/NotificationsTest.php +++ b/tests/unit/Platform/Workers/NotificationsTest.php @@ -16,11 +16,12 @@ use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\Logger\Log; use Utopia\Messaging\Adapter\Email as EmailAdapter; use Utopia\Messaging\Messages\Email as EmailMessage; use Utopia\Queue\Message; use Utopia\Registry\Registry; +use Utopia\Span\Span; +use Utopia\Span\Storage\Memory as SpanMemory; require_once __DIR__ . '/../../../../app/init.php'; @@ -44,7 +45,6 @@ protected function dispatch( Document $project, Registry $register, Database $dbForPlatform, - Log $log, ): ?string { $channel = $recipient['channel']; $this->dispatched[] = [ @@ -157,7 +157,7 @@ final class NotificationsTest extends TestCase private Authorization $authorization; private Registry $registry; private Document $project; - private Log $log; + private Span $span; protected function setUp(): void { @@ -210,11 +210,13 @@ protected function setUp(): void $this->registry = new Registry(); $this->project = new Document(['$id' => 'project-x', '$sequence' => 'project-internal-x']); - $this->log = new Log(); + Span::setStorage(new SpanMemory()); + $this->span = Span::init('test.notifications'); } protected function tearDown(): void { + Span::setStorage(null); $this->authorization->cleanRoles(); $this->authorization->addRole(Role::any()->toString()); } @@ -289,7 +291,7 @@ public function testDispatchesPerChannelToCorrectAdapter(): void 'deduplicationKey' => 'event-1', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $this->assertCount(3, $worker->dispatched); $channels = \array_map(static fn ($d) => $d['channel'], $worker->dispatched); @@ -311,7 +313,7 @@ public function testPersistsOneAlertPerRecipientChannel(): void 'permissions' => [Permission::read(Role::any())], ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $rows = $this->database->find('notifications'); $this->assertCount(2, $rows); @@ -343,11 +345,11 @@ public function testDedupHitShortCircuitsBeforeDispatch(): void 'deduplicationKey' => 'dup-key', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $this->assertCount(1, $worker->dispatched); $worker->dispatched = []; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $this->assertCount(0, $worker->dispatched, 'second invocation must short-circuit on dedup hit'); } @@ -361,8 +363,7 @@ public function testMissingRecipientsAndAddressThrows(): void $this->buildMessage(['project' => ['$id' => 'project-x'], 'subject' => '', 'body' => '']), $this->project, $this->registry, - $this->database, - $this->log, + $this->database ); } @@ -376,7 +377,7 @@ public function testFallbackToLegacyRecipient(): void 'body' => 'Y', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $this->assertCount(1, $worker->dispatched); $this->assertSame('legacy@example.test', $worker->dispatched[0]['address']); @@ -408,7 +409,7 @@ public function testLegacyMailPayloadOptionsAreAppliedByEmailChannel(): void ], ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); } finally { \putenv($previousSmtpHost === false ? '_APP_SMTP_HOST' : '_APP_SMTP_HOST=' . $previousSmtpHost); } @@ -442,7 +443,7 @@ public function testLegacyMailPayloadThrowsWhenSmtpIsNotConfigured(): void $this->expectException(\Exception::class); $this->expectExceptionMessage('Skipped mail processing. No SMTP configuration has been set.'); - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); } finally { \putenv($previousSmtpHost === false ? '_APP_SMTP_HOST' : '_APP_SMTP_HOST=' . $previousSmtpHost); } @@ -468,7 +469,7 @@ public function testWebhookRecipientForwardsSignatureKey(): void 'body' => 'b', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $this->assertCount(2, $worker->dispatched); $this->assertSame('tenant-secret', $worker->dispatched[0]['signatureKey']); @@ -489,15 +490,14 @@ public function testDispatchErrorTagsLogAndPropagates(): void ]; try { - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $this->fail('expected exception to propagate'); } catch (\Throwable $error) { $this->assertSame('boom', $error->getMessage()); } - $tags = $this->log->getTags(); - $this->assertSame(NOTIFICATION_TYPE_WEBHOOK, $tags['channel'] ?? null); - $this->assertSame('boom', $tags['error'] ?? null); + $this->assertSame(NOTIFICATION_TYPE_WEBHOOK, $this->span->get('channel')); + $this->assertSame('boom', $this->span->get('channel.error')); $rows = $this->database->find('notifications'); $this->assertCount(0, $rows, 'failed dispatch must not persist alert'); @@ -521,7 +521,7 @@ public function testDedupSkipsOnlyDeliveredRecipientAfterPartialFanoutFailure(): ]; try { - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $this->fail('expected webhook failure to propagate'); } catch (\RuntimeException $error) { $this->assertSame('webhook down', $error->getMessage()); @@ -534,7 +534,7 @@ public function testDedupSkipsOnlyDeliveredRecipientAfterPartialFanoutFailure(): $this->assertSame(NOTIFICATION_TYPE_CONSOLE, $rows[0]->getAttribute('channel')); $retry = new SpyNotifications(); - $retry->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $retry->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $this->assertCount(1, $retry->dispatched, 'retry should dispatch only the previously undelivered webhook'); $this->assertSame(NOTIFICATION_TYPE_WEBHOOK, $retry->dispatched[0]['channel']); @@ -559,7 +559,7 @@ public function testConsoleChannelSkipsPersistAlert(): void 'deduplicationKey' => 'console-skip', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); // The Console adapter wrote exactly one alert; the action loop // must NOT have called persistAlert (otherwise we'd see 2 rows or @@ -586,7 +586,7 @@ public function testConsoleZeroDeliveryThrows(): void ]; try { - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $this->fail('expected console zero-delivery to throw'); } catch (\Throwable $error) { $this->assertStringContainsString('Console alert delivery failed', $error->getMessage()); @@ -610,7 +610,7 @@ public function testMultiRecipientFanoutNoCollision(): void 'deduplicationKey' => 'fanout', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $rows = $this->database->find('notifications'); $this->assertCount(2, $rows, 'two recipients must produce two distinct alert rows'); @@ -647,7 +647,7 @@ public function testRecipientStructRoundtripsResourceFields(): void 'deduplicationKey' => 'roundtrip', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $rows = $this->database->find('notifications'); $this->assertCount(1, $rows); @@ -688,7 +688,7 @@ public function testTrackingLogoInjectedIntoEmailHtml(): void 'deduplicationKey' => 'logo-key', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); } finally { \putenv($previousSmtpHost === false ? '_APP_SMTP_HOST' : '_APP_SMTP_HOST=' . $previousSmtpHost); \putenv($previousTrackingSecret === false ? '_APP_NOTIFICATIONS_TRACKING_SECRET' : '_APP_NOTIFICATIONS_TRACKING_SECRET=' . $previousTrackingSecret); @@ -760,7 +760,7 @@ public function testTrackingLogoDoesNotUseOpenSslKeyFallback(): void 'deduplicationKey' => 'logo-key-no-tracking-secret', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); } finally { \putenv($previousSmtpHost === false ? '_APP_SMTP_HOST' : '_APP_SMTP_HOST=' . $previousSmtpHost); \putenv($previousTrackingSecret === false ? '_APP_NOTIFICATIONS_TRACKING_SECRET' : '_APP_NOTIFICATIONS_TRACKING_SECRET=' . $previousTrackingSecret); @@ -792,7 +792,7 @@ public function testPersistAlertReturnsAlertIdAndStoresResource(): void 'deduplicationKey' => 'persist-email', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); } finally { \putenv($previousSmtpHost === false ? '_APP_SMTP_HOST' : '_APP_SMTP_HOST=' . $previousSmtpHost); } @@ -845,7 +845,7 @@ public function testEmailSendFailureDoesNotPersistAlert(): void $threw = false; try { - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); } catch (\Throwable $error) { $threw = true; $this->assertStringContainsString('SMTP unavailable', $error->getMessage()); @@ -866,7 +866,7 @@ public function testEmailSendFailureDoesNotPersistAlert(): void $this->registry->set('smtp', static fn () => $working); $retryWorker = new Notifications(); - $retryWorker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $retryWorker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $this->assertSame(1, $working->sendCount, 'retry must invoke the working adapter'); @@ -908,7 +908,7 @@ public function testEmailFailureDoesNotBlockConsoleRecipient(): void $threw = false; try { - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); } catch (\Throwable $error) { $threw = true; $this->assertStringContainsString('SMTP unavailable', $error->getMessage()); @@ -933,7 +933,7 @@ public function testEmailFailureDoesNotBlockConsoleRecipient(): void $this->registry->set('smtp', static fn () => $working); $retryWorker = new Notifications(); - $retryWorker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $retryWorker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $this->assertSame(1, $working->sendCount, 'retry must still deliver the email recipient'); $rows = $this->database->find('notifications', [ @@ -976,7 +976,7 @@ public function testEmailChannelHappyPath(): void 'deduplicationKey' => 'happy-email', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); } finally { \putenv($previousSmtpHost === false ? '_APP_SMTP_HOST' : '_APP_SMTP_HOST=' . $previousSmtpHost); \putenv($previousTrackingSecret === false ? '_APP_NOTIFICATIONS_TRACKING_SECRET' : '_APP_NOTIFICATIONS_TRACKING_SECRET=' . $previousTrackingSecret); @@ -1045,7 +1045,7 @@ public function testConsoleChannelHappyPath(): void 'deduplicationKey' => 'happy-console', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $rows = $this->database->find('notifications', [ Query::equal('channel', ['console']), @@ -1088,7 +1088,7 @@ public function testConsoleChannelUsesPreviewBodyInsteadOfRenderedEmailHtml(): v 'deduplicationKey' => 'console-preview', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $rows = $this->database->find('notifications'); $this->assertCount(1, $rows); @@ -1116,7 +1116,7 @@ public function testEmailChannelThrowsWhenSmtpIsNotConfigured(): void ]; try { - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); } catch (\Exception $error) { $this->assertStringContainsString('No SMTP configuration has been set', $error->getMessage()); $threw = true; @@ -1128,7 +1128,7 @@ public function testEmailChannelThrowsWhenSmtpIsNotConfigured(): void $this->assertTrue($threw, 'missing SMTP must fail email delivery so the queue can retry'); $this->assertSame(0, $worker->persistAlertCalls); $this->assertCount(0, $this->database->find('notifications')); - $this->assertSame('no_smtp', $this->log->getTags()['email_skipped'] ?? null); + $this->assertSame('no_smtp', $this->span->get('email.skipped')); } public function testConsoleChannelRejectsInvalidImplicitUserId(): void @@ -1148,7 +1148,7 @@ public function testConsoleChannelRejectsInvalidImplicitUserId(): void $this->expectException(\Exception::class); $this->expectExceptionMessage('Invalid console alert resourceId'); - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); } /** @@ -1173,7 +1173,7 @@ public function __construct(public array &$captured) parent::__construct(); } - protected function dispatchWebhook(array $recipient, array $payload, Log $log): ?string + protected function dispatchWebhook(array $recipient, array $payload): ?string { $adapter = new \Tests\Unit\Utopia\Messaging\Adapter\CapturingWebhook(); $message = new \Appwrite\Utopia\Messaging\Messages\Webhook( @@ -1220,7 +1220,7 @@ protected function dispatchWebhook(array $recipient, array $payload, Log $log): 'deduplicationKey' => 'happy-webhook', ]; - $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database, $this->log); + $worker->action($this->buildMessage($payload), $this->project, $this->registry, $this->database); $this->assertCount(1, $worker->captured, 'adapter must POST exactly once'); $request = $worker->captured[0];