diff --git a/.env b/.env index dfa53ec54c..9a8613f685 100644 --- a/.env +++ b/.env @@ -134,4 +134,5 @@ _APP_PROJECT_REGIONS=default _APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000 _APP_STATS_USAGE_DUAL_WRITING_DBS=database_db_main _APP_TRUSTED_HEADERS=x-forwarded-for -_APP_POOL_ADAPTER=stack \ No newline at end of file +_APP_POOL_ADAPTER=stack +_APP_WORKER_SCREENSHOTS_ROUTER=http://appwrite diff --git a/app/cli.php b/app/cli.php index 14ebea6e1c..0f8426afd9 100644 --- a/app/cli.php +++ b/app/cli.php @@ -30,9 +30,6 @@ use Utopia\Pools\Group; use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Publisher; use Utopia\Registry\Registry; -use Utopia\Span\Exporter; -use Utopia\Span\Span; -use Utopia\Span\Storage; use Utopia\System\System; use Utopia\Telemetry\Adapter\None as NoTelemetry; @@ -340,6 +337,5 @@ $cli $cli->shutdown()->action(fn () => Timer::clearAll()); Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -Span::setStorage(new Storage\Coroutine()); -Span::addExporter(new Exporter\Stdout()); +require_once __DIR__ . '/init/span.php'; run($cli->run(...)); diff --git a/app/controllers/general.php b/app/controllers/general.php index a4f364485e..2d3bde69d6 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -52,6 +52,7 @@ 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; use Utopia\Validator; use Utopia\Validator\Text; @@ -1245,17 +1246,7 @@ Http::error() $trace = $error->getTrace(); if (php_sapi_name() === 'cli') { - Console::error('[Error] Timestamp: ' . date('c', time())); - - if ($route) { - Console::error('[Error] Method: ' . $route->getMethod()); - Console::error('[Error] URL: ' . $route->getPath()); - } - - Console::error('[Error] Type: ' . get_class($error)); - Console::error('[Error] Message: ' . $message); - Console::error('[Error] File: ' . $file); - Console::error('[Error] Line: ' . $line); + Span::error($error); } switch ($class) { diff --git a/app/http.php b/app/http.php index bf71787fdf..9771568797 100644 --- a/app/http.php +++ b/app/http.php @@ -1,6 +1,7 @@ on(Constant::EVENT_WORKER_START, function ($server, $workerId) use (&$fil $files = new Files(); $files->load(__DIR__ . '/../public'); } - Console::success('Worker ' . ++$workerId . ' started successfully'); }); $http->on(Constant::EVENT_WORKER_STOP, function ($server, $workerId) { @@ -207,7 +208,8 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c } } - Console::success("[Setup] - $dbName database init started..."); + Span::init("database.setup"); + Span::add('database.name', $dbName); $attempts = 0; while (true) { @@ -218,6 +220,7 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c break; // exit loop on success } catch (\Exception $e) { if ($e instanceof DuplicateException) { + Span::add('database.exists', true); Console::info(" └── Skip: metadata table already exists"); break; } @@ -232,6 +235,7 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c } // Process collections + $collectionsCreated = 0; foreach ($collections as $key => $collection) { if (($collection['$collection'] ?? '') !== Database::METADATA) { continue; @@ -241,8 +245,6 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c continue; } - Console::info(" └── Creating collection: {$collection['$id']}..."); - $attributes = array_map(fn ($attr) => new Document([ '$id' => ID::custom($attr['$id']), 'type' => $attr['type'], @@ -264,14 +266,19 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c ]), $collection['indexes']); $database->createCollection($key, $attributes, $indexes); + $collectionsCreated++; } + Span::add('database.collections_created', $collectionsCreated); + if ($extraSetup) { $extraSetup($database); } + + Span::current()?->finish(); } -$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) { +$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $totalWorkers, $register) { $app = new Http('UTC'); go(function () use ($register, $app) { @@ -296,7 +303,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg } if ($dbForPlatform->getDocument('buckets', 'default')->isEmpty()) { - Console::info(" └── Creating default bucket..."); $dbForPlatform->createDocument('buckets', new Document([ '$id' => ID::custom('default'), '$collection' => ID::custom('buckets'), @@ -319,7 +325,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $bucket = $dbForPlatform->getDocument('buckets', 'default'); - Console::info(" └── Creating files collection for default bucket..."); $files = $collections['buckets']['files'] ?? []; if (empty($files)) { throw new Exception('Files collection is not configured.'); @@ -349,7 +354,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg } if ($authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) { - Console::info(" └── Creating screenshots bucket..."); $authorization->skip(fn () => $dbForPlatform->createDocument('buckets', new Document([ '$id' => ID::custom('screenshots'), '$collection' => ID::custom('buckets'), @@ -367,7 +371,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $bucket = $authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')); - Console::info(" └── Creating files collection for screenshots bucket..."); $files = $collections['buckets']['files'] ?? []; if (empty($files)) { throw new Exception('Files collection is not configured.'); @@ -405,6 +408,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $cache = $app->getResource('cache'); foreach ($sharedTablesV2 as $hostname) { + Span::init('database.setup'); + Span::add('database.hostname', $hostname); + $adapter = new DatabasePool($pools->get($hostname)); $dbForProject = (new Database($adapter, $cache)) ->setDatabase('appwrite') @@ -422,6 +428,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $dbForProject->create(); break; // exit loop on success } catch (DuplicateException) { + Span::add('database.exists', true); Console::success('[Setup] - Skip: metadata table already exists'); break; } catch (\Throwable $e) { @@ -439,6 +446,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $audit->setup(); } + $collectionsCreated = 0; foreach ($projectCollections as $key => $collection) { if (($collection['$collection'] ?? '') !== Database::METADATA) { continue; @@ -450,17 +458,21 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']); $indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']); - Console::success('[Setup] - Creating project collection: ' . $collection['$id'] . '...'); - $dbForProject->createCollection($key, $attributes, $indexes); + $collectionsCreated++; } - } - Console::success('[Setup] - Server database init completed...'); + Span::add('database.collections_created', $collectionsCreated); + Span::current()?->finish(); + } }); - Console::success('Server started successfully (max payload is ' . number_format($payloadSize) . ' bytes)'); - Console::info("Master pid {$http->master_pid}, manager pid {$http->manager_pid}"); + Span::init('http.server.start'); + Span::add('server.workers', $totalWorkers); + Span::add('server.payload_size', $payloadSize); + Span::add('server.master_pid', $http->master_pid); + Span::add('server.manager_pid', $http->manager_pid); + Span::current()?->finish(); // Start the task that starts fetching custom domains $http->task([], 0); @@ -473,12 +485,16 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg }); $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register, &$files) { + Span::init('http.request'); + Http::setResource('swooleRequest', fn () => $swooleRequest); Http::setResource('swooleResponse', fn () => $swooleResponse); $request = new Request($swooleRequest); $response = new Response($swooleResponse); + Span::add('http.method', $request->getMethod()); + if ($files instanceof Files && $files->isFileLoaded($request->getURI())) { $time = (60 * 60 * 24 * 45); // 45 days cache @@ -507,7 +523,12 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool $authorization->addRole(Role::any()->toString()); $app->run($request, $response); + + $route = $app->getRoute(); + Span::add('http.path', $route?->getPath() ?? 'unknown'); } catch (\Throwable $th) { + Span::error($th); + $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); $logger = $app->getResource("logger"); @@ -570,12 +591,6 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool } } - Console::error('[Error] Type: ' . get_class($th)); - Console::error('[Error] Message: ' . $th->getMessage()); - Console::error('[Error] File: ' . $th->getFile()); - Console::error('[Error] Line: ' . $th->getLine()); - Console::error('[Error] Trace: ' . $th->getTraceAsString()); - $swooleResponse->setStatusCode(500); $output = ((Http::isDevelopment())) ? [ @@ -592,6 +607,9 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool ]; $swooleResponse->end(\json_encode($output)); + } finally { + Span::add('http.response.code', $response->getStatusCode()); + Span::current()?->finish(); } }); diff --git a/app/init/span.php b/app/init/span.php new file mode 100644 index 0000000000..76f37f5300 --- /dev/null +++ b/app/init/span.php @@ -0,0 +1,8 @@ + $register); diff --git a/composer.json b/composer.json index ced9a65219..008ef14fee 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,7 @@ "utopia-php/detector": "0.2.*", "utopia-php/domains": "1.*", "utopia-php/emails": "0.6.*", - "utopia-php/dns": "1.5.*", + "utopia-php/dns": "1.6.*", "utopia-php/dsn": "0.2.1", "utopia-php/framework": "0.33.*", "utopia-php/fetch": "0.5.*", @@ -70,6 +70,7 @@ "utopia-php/migration": "1.5.*", "utopia-php/platform": "0.7.*", "utopia-php/pools": "1.*", + "utopia-php/span": "1.1.*", "utopia-php/preloader": "0.2.*", "utopia-php/queue": "0.15.*", "utopia-php/registry": "0.5.*", diff --git a/composer.lock b/composer.lock index c6991086e9..6cffb53d62 100644 --- a/composer.lock +++ b/composer.lock @@ -3948,22 +3948,22 @@ }, { "name": "utopia-php/dns", - "version": "1.5.4", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/utopia-php/dns.git", - "reference": "ee831a6f2ceb28babb042ea65539c26ea4530bf6" + "reference": "98c70520213a41e2fe1867e5b110273c06bf1cab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dns/zipball/ee831a6f2ceb28babb042ea65539c26ea4530bf6", - "reference": "ee831a6f2ceb28babb042ea65539c26ea4530bf6", + "url": "https://api.github.com/repos/utopia-php/dns/zipball/98c70520213a41e2fe1867e5b110273c06bf1cab", + "reference": "98c70520213a41e2fe1867e5b110273c06bf1cab", "shasum": "" }, "require": { "php": ">=8.3", "utopia-php/domains": "1.0.*", - "utopia-php/span": "1.0.*", + "utopia-php/span": "1.1.*", "utopia-php/telemetry": "*", "utopia-php/validators": "0.*" }, @@ -3999,9 +3999,9 @@ ], "support": { "issues": "https://github.com/utopia-php/dns/issues", - "source": "https://github.com/utopia-php/dns/tree/1.5.4" + "source": "https://github.com/utopia-php/dns/tree/1.6.2" }, - "time": "2026-02-02T10:40:38+00:00" + "time": "2026-02-13T12:29:08+00:00" }, { "name": "utopia-php/domains", @@ -4909,25 +4909,26 @@ }, { "name": "utopia-php/span", - "version": "1.0.0", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/utopia-php/span.git", - "reference": "f2f6c499ded3a776e8019902e83d140ff0f89693" + "reference": "49d04aa588a2cdbbc9381ee7a1c129469e0f905c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/span/zipball/f2f6c499ded3a776e8019902e83d140ff0f89693", - "reference": "f2f6c499ded3a776e8019902e83d140ff0f89693", + "url": "https://api.github.com/repos/utopia-php/span/zipball/49d04aa588a2cdbbc9381ee7a1c129469e0f905c", + "reference": "49d04aa588a2cdbbc9381ee7a1c129469e0f905c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { "laravel/pint": "^1.0", "phpstan/phpstan": "^2.0", "phpunit/phpunit": "^10.0", + "rector/rector": "^2.3", "swoole/ide-helper": "^5.0" }, "suggest": { @@ -4946,9 +4947,9 @@ "description": "Simple span tracing library for PHP with coroutine support", "support": { "issues": "https://github.com/utopia-php/span/issues", - "source": "https://github.com/utopia-php/span/tree/1.0.0" + "source": "https://github.com/utopia-php/span/tree/1.1.4" }, - "time": "2026-01-12T20:05:10+00:00" + "time": "2026-02-13T10:58:12+00:00" }, { "name": "utopia-php/storage", diff --git a/docker-compose.yml b/docker-compose.yml index 859a769349..6c4e9f0231 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -572,6 +572,7 @@ services: environment: # Specific - _APP_BROWSER_HOST + - _APP_WORKER_SCREENSHOTS_ROUTER # Basic - _APP_ENV - _APP_WORKER_PER_CORE diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php index 4fd8d321fb..1a4cc45081 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php @@ -301,7 +301,7 @@ class Create extends Base if ($async) { if (is_null($scheduledAt)) { - if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check + if ($project->getId() != '6862e6a6000cce69f9da') { $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); } $queueForFunctions @@ -344,7 +344,7 @@ class Create extends Base ->setAttribute('scheduleInternalId', $schedule->getSequence()) ->setAttribute('scheduledAt', $scheduledAt); - if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check + if ($project->getId() != '6862e6a6000cce69f9da') { $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); } } @@ -505,7 +505,7 @@ class Create extends Base ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) ; - if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check + if ($project->getId() != '6862e6a6000cce69f9da') { $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); } } diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php b/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php index ff0fa812cf..f5f7a7974b 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php @@ -111,15 +111,16 @@ class Screenshots extends Action throw new \Exception('Bucket not found'); } + $routerHost = System::getEnv('_APP_WORKER_SCREENSHOTS_ROUTER', 'http://appwrite'); $configs = [ 'screenshotLight' => [ 'headers' => [ 'x-appwrite-hostname' => $rule->getAttribute('domain') ], - 'url' => 'http://appwrite/?appwrite-preview=1&appwrite-theme=light', + 'url' => $routerHost . '/?appwrite-preview=1&appwrite-theme=light', 'theme' => 'light' ], 'screenshotDark' => [ 'headers' => [ 'x-appwrite-hostname' => $rule->getAttribute('domain') ], - 'url' => 'http://appwrite/?appwrite-preview=1&appwrite-theme=dark', + 'url' => $routerHost . '/?appwrite-preview=1&appwrite-theme=dark', 'theme' => 'dark' ], ]; diff --git a/src/Appwrite/Platform/Workers/Executions.php b/src/Appwrite/Platform/Workers/Executions.php index 300a84162c..d874e26267 100644 --- a/src/Appwrite/Platform/Workers/Executions.php +++ b/src/Appwrite/Platform/Workers/Executions.php @@ -7,7 +7,6 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Platform\Action; use Utopia\Queue\Message; -use Utopia\System\System; class Executions extends Action { @@ -45,7 +44,8 @@ class Executions extends Action throw new Exception('Missing execution'); } - if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check + $project = new Document($payload['project'] ?? []); + if ($project->getId() != '6862e6a6000cce69f9da') { $dbForProject->upsertDocument('executions', $execution); } }