1
0
Fork 0
mirror of synced 2024-05-09 15:22:33 +12:00

Merge pull request #2432 from appwrite/feat-improve-error-logging

Implement utopia-php/logger
This commit is contained in:
Eldad A. Fux 2021-12-31 16:55:29 +02:00 committed by GitHub
commit 5140d3328e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 471 additions and 42 deletions

2
.env
View file

@ -43,3 +43,5 @@ _APP_MAINTENANCE_RETENTION_EXECUTION=1209600
_APP_MAINTENANCE_RETENTION_ABUSE=86400
_APP_MAINTENANCE_RETENTION_AUDIT=1209600
_APP_USAGE_STATS=enabled
_APP_LOGGING_PROVIDER=
_APP_LOGGING_CONFIG=

View file

@ -65,6 +65,7 @@ script:
exit 1
fi
- docker-compose logs appwrite
- docker-compose logs appwrite-realtime
- docker-compose logs mariadb
- docker-compose logs appwrite-worker-functions
- docker-compose exec appwrite doctor

View file

@ -181,7 +181,9 @@ ENV _APP_SERVER=swoole \
_APP_MAINTENANCE_RETENTION_AUDIT=1209600 \
# 1 Day = 86400 s
_APP_MAINTENANCE_RETENTION_ABUSE=86400 \
_APP_MAINTENANCE_INTERVAL=86400
_APP_MAINTENANCE_INTERVAL=86400 \
_APP_LOGGING_PROVIDER= \
_APP_LOGGING_CONFIG=
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

View file

@ -150,6 +150,24 @@ return [
'question' => '',
'filter' => ''
],
[
'name' => '_APP_LOGGING_PROVIDER',
'description' => 'This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, to enable the logger set the value to one of \'sentry\', \'raygun\', \'appsignal\'',
'introduction' => '0.12.0',
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_LOGGING_CONFIG',
'description' => 'This variable configures authentication to 3rd party error logging providers. 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.',
'introduction' => '0.12.0',
'default' => '',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_USAGE_AGGREGATION_INTERVAL',
'description' => 'Interval value containing the number of seconds that the Appwrite usage process should wait before aggregating stats and syncing it to mariadb from InfluxDB. The default value is 30 seconds.',

View file

@ -3,6 +3,8 @@
require_once __DIR__.'/../init.php';
use Utopia\App;
use Utopia\Logger\Log;
use Utopia\Logger\Log\User;
use Utopia\Swoole\Request;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\View;
@ -298,19 +300,72 @@ App::options(function ($request, $response) {
->noContent();
}, ['request', 'response']);
App::error(function ($error, $utopia, $request, $response, $layout, $project) {
App::error(function ($error, $utopia, $request, $response, $layout, $project, $logger, $loggerBreadcrumbs) {
/** @var Exception $error */
/** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Utopia\View $layout */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Logger\Logger $logger */
/** @var Utopia\Logger\Log\Breadcrumb[] $loggerBreadcrumbs */
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
$route = $utopia->match($request);
if($logger) {
if($error->getCode() >= 500 || $error->getCode() === 0) {
try {
$user = $utopia->getResource('user');
/** @var Appwrite\Database\Document $user */
} catch(\Throwable $th) {
// All good, user is optional information for logger
}
$log = new Utopia\Logger\Log();
if(isset($user) && !$user->isEmpty()) {
$log->setUser(new User($user->getId()));
}
$log->setNamespace("http");
$log->setServer(\gethostname());
$log->setVersion($version);
$log->setType(Log::TYPE_ERROR);
$log->setMessage($error->getMessage());
$log->addTag('method', $route->getMethod());
$log->addTag('url', $route->getPath());
$log->addTag('verboseType', get_class($error));
$log->addTag('code', $error->getCode());
$log->addTag('projectId', $project->getId());
$log->addTag('hostname', $request->getHostname());
$log->addTag('locale', (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', '')));
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('roles', Authorization::$roles);
$action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD");
$log->setAction($action);
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
foreach($loggerBreadcrumbs as $loggerBreadcrumb) {
$log->addBreadcrumb($loggerBreadcrumb);
}
$responseCode = $logger->addLog($log);
Console::info('Log pushed with status code: '.$responseCode);
}
}
if ($error instanceof PDOException) {
throw $error;
}
$route = $utopia->match($request);
$template = ($route) ? $route->getLabel('error', null) : null;
if (php_sapi_name() === 'cli') {
@ -327,8 +382,6 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) {
Console::error('[Error] Line: '.$error->getLine());
}
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
switch ($error->getCode()) { // Don't show 500 errors!
case 400: // Error allowed publicly
case 401: // Error allowed publicly
@ -395,7 +448,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) {
$response->dynamic(new Document($output),
$utopia->isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR);
}, ['error', 'utopia', 'request', 'response', 'layout', 'project']);
}, ['error', 'utopia', 'request', 'response', 'layout', 'project', 'logger', 'loggerBreadcrumbs']);
App::get('/manifest.json')
->desc('Progressive app manifest file')

View file

@ -16,6 +16,8 @@ use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Database\Document;
use Utopia\Swoole\Files;
use Utopia\Swoole\Request;
use Utopia\Logger\Log;
use Utopia\Logger\Log\User;
$http = new Server("0.0.0.0", App::getEnv('PORT', 80));
@ -186,6 +188,59 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
$app->run($request, $response);
} catch (\Throwable $th) {
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
$logger = $app->getResource("logger");
if($logger) {
try {
$user = $app->getResource('user');
/** @var Appwrite\Database\Document $user */
} catch(\Throwable $_th) {
// All good, user is optional information for logger
}
$loggerBreadcrumbs = $app->getResource("loggerBreadcrumbs");
$route = $app->match($request);
$log = new Utopia\Logger\Log();
if(isset($user) && !$user->isEmpty()) {
$log->setUser(new User($user->getId()));
}
$log->setNamespace("http");
$log->setServer(\gethostname());
$log->setVersion($version);
$log->setType(Log::TYPE_ERROR);
$log->setMessage($th->getMessage());
$log->addTag('method', $route->getMethod());
$log->addTag('url', $route->getPath());
$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->getHeader('x-appwrite-locale', '')));
$log->addExtra('file', $th->getFile());
$log->addExtra('line', $th->getLine());
$log->addExtra('trace', $th->getTraceAsString());
$log->addExtra('roles', Authorization::$roles);
$action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD");
$log->setAction($action);
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
foreach($loggerBreadcrumbs as $loggerBreadcrumb) {
$log->addBreadcrumb($loggerBreadcrumb);
}
$responseCode = $logger->addLog($log);
Console::info('Log pushed with status code: '.$responseCode);
}
Console::error('[Error] Type: '.get_class($th));
Console::error('[Error] Message: '.$th->getMessage());
Console::error('[Error] File: '.$th->getFile());
@ -200,12 +255,20 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
$swooleResponse->setStatusCode(500);
if(App::isDevelopment()) {
$swooleResponse->end('error: '.$th->getMessage());
}
else {
$swooleResponse->end('500: Server Error');
}
$output = ((App::isDevelopment())) ? [
'message' => 'Error: '. $th->getMessage(),
'code' => 500,
'file' => $th->getFile(),
'line' => $th->getLine(),
'trace' => $th->getTrace(),
'version' => $version,
] : [
'message' => 'Error: Server Error',
'code' => 500,
'version' => $version,
];
$swooleResponse->end(\json_encode($output));
} finally {
/** @var PDOPool $dbPool */
$dbPool = $register->get('dbPool');

View file

@ -30,6 +30,7 @@ use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Stats\Stats;
use Appwrite\Utopia\View;
use Utopia\App;
use Utopia\Logger\Logger;
use Utopia\Config\Config;
use Utopia\Locale\Locale;
use Utopia\Registry\Registry;
@ -144,7 +145,7 @@ Config::load('locale-continents', __DIR__.'/config/locale/continents.php');
Config::load('storage-logos', __DIR__.'/config/storage/logos.php');
Config::load('storage-mimes', __DIR__.'/config/storage/mimes.php');
Config::load('storage-inputs', __DIR__.'/config/storage/inputs.php');
Config::load('storage-outputs', __DIR__.'/config/storage/outputs.php');
Config::load('storage-outputs', __DIR__.'/config/storage/outputs.php');
$user = App::getEnv('_APP_REDIS_USER','');
$pass = App::getEnv('_APP_REDIS_PASS','');
@ -375,6 +376,22 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function($attribute) {
/*
* Registry
*/
$register->set('logger', function () { // Register error logger
$providerName = App::getEnv('_APP_LOGGING_PROVIDER', '');
$providerConfig = App::getEnv('_APP_LOGGING_CONFIG', '');
if(empty($providerName) || empty($providerConfig)) {
return null;
}
if(!Logger::hasProvider($providerName)) {
throw new Exception("Logging provider not supported. Logging disabled.");
}
$classname = '\\Utopia\\Logger\\Adapter\\'.\ucfirst($providerName);
$adapter = new $classname($providerConfig);
return new Logger($adapter);
});
$register->set('dbPool', function () { // Register DB connection
$dbHost = App::getEnv('_APP_DB_HOST', '');
$dbPort = App::getEnv('_APP_DB_PORT', '');
@ -581,6 +598,14 @@ Locale::setLanguageFromJSON('zh-tw', __DIR__.'/config/locale/translations/zh-tw.
]);
// Runtime Execution
App::setResource('logger', function($register) {
return $register->get('logger');
}, ['register']);
App::setResource('loggerBreadcrumbs', function() {
return [];
});
App::setResource('register', fn() => $register);
App::setResource('layout', function($locale) {

View file

@ -14,6 +14,8 @@ use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Logger\Log;
use Utopia\Database\Database;
use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\Cache\Cache;
@ -51,6 +53,43 @@ $adapter->setPackageMaxLength(64000); // Default maximum Package Size (64kb)
$server = new Server($adapter);
$logError = function(Throwable $error, string $action) use ($register) {
$logger = $register->get('logger');
if($logger) {
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
$log = new Log();
$log->setNamespace("realtime");
$log->setServer(\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->setAction($action);
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('Realtime log pushed with status code: '.$responseCode);
}
Console::error('[Error] Type: ' . get_class($error));
Console::error('[Error] Message: ' . $error->getMessage());
Console::error('[Error] File: ' . $error->getFile());
Console::error('[Error] Line: ' . $error->getLine());
};
$server->error($logError);
function getDatabase(Registry &$register, string $namespace)
{
$db = $register->get('dbPool')->get();
@ -70,13 +109,13 @@ function getDatabase(Registry &$register, string $namespace)
];
};
$server->onStart(function () use ($stats, $register, $containerId, &$statsDocument) {
$server->onStart(function () use ($stats, $register, $containerId, &$statsDocument, $logError) {
Console::success('Server started succefully');
/**
* Create document for this worker to share stats across Containers.
*/
go(function () use ($register, $containerId, &$statsDocument) {
go(function () use ($register, $containerId, &$statsDocument, $logError) {
try {
[$database, $returnDatabase] = getDatabase($register, '_project_console');
$document = new Document([
@ -90,10 +129,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
]);
$statsDocument = Authorization::skip(fn() => $database->createDocument('realtime', $document));
} catch (\Throwable $th) {
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());
call_user_func($logError, $th, "createWorkerDocument");
} finally {
call_user_func($returnDatabase);
}
@ -102,7 +138,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
/**
* Save current connections to the Database every 5 seconds.
*/
Timer::tick(5000, function () use ($register, $stats, $containerId, &$statsDocument) {
Timer::tick(5000, function () use ($register, $stats, $containerId, &$statsDocument, $logError) {
/** @var Document $statsDocument */
foreach ($stats as $projectId => $value) {
$connections = $stats->get($projectId, 'connections') ?? 0;
@ -142,23 +178,20 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
Authorization::skip(fn() => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
} catch (\Throwable $th) {
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());
call_user_func($logError, $th, "updateWorkerDocument");
} finally {
call_user_func($returnDatabase);
}
});
});
$server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime) {
$server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime, $logError) {
Console::success('Worker ' . $workerId . ' started succefully');
$attempts = 0;
$start = time();
Timer::tick(5000, function () use ($server, $register, $realtime, $stats) {
Timer::tick(5000, function () use ($server, $register, $realtime, $stats, $logError) {
/**
* Sending current connections to project channels on the console project every 5 seconds.
*/
@ -300,6 +333,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
}
});
} catch (\Throwable $th) {
call_user_func($logError, $th, "pubSubConnection");
Console::error('Pub/sub error: ' . $th->getMessage());
$register->get('redisPool')->put($redis);
$attempts++;
@ -312,7 +347,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
Console::error('Failed to restart pub/sub...');
});
$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime) {
$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime, $logError) {
$app = new App('UTC');
$request = new Request($request);
$response = new Response(new SwooleResponse());
@ -409,6 +444,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$stats->incr($project->getId(), 'connections');
$stats->incr($project->getId(), 'connectionsTotal');
} catch (\Throwable $th) {
call_user_func($logError, $th, "initServer");
$response = [
'type' => 'error',
'data' => [

View file

@ -3,6 +3,7 @@
global $cli;
use Appwrite\ClamAV\Network;
use Utopia\Logger\Logger;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
use Utopia\App;
@ -82,6 +83,16 @@ $cli
Console::log('🟢 HTTPS force option is enabled');
}
$providerName = App::getEnv('_APP_LOGGING_PROVIDER', '');
$providerConfig = App::getEnv('_APP_LOGGING_CONFIG', '');
if(empty($providerName) || empty($providerConfig) || !Logger::hasProvider($providerName)) {
Console::log('🔴 Logging adapter is disabled');
} else {
Console::log('🟢 Logging adapter is enabled (' . $providerName . ')');
}
\sleep(0.2);
try {

View file

@ -106,6 +106,8 @@ services:
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_RUNTIMES
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_STATSD_HOST
- _APP_STATSD_PORT
- _APP_MAINTENANCE_INTERVAL

View file

@ -11,6 +11,10 @@ Console::success(APP_NAME . ' audits worker v1 has started');
class AuditsV1 extends Worker
{
public function getName(): string {
return "audits";
}
public function init(): void
{
}

View file

@ -16,6 +16,10 @@ Console::success(APP_NAME . ' certificates worker v1 has started');
class CertificatesV1 extends Worker
{
public function getName(): string {
return "certificates";
}
public function init(): void
{
}

View file

@ -23,6 +23,10 @@ class DeletesV1 extends Worker
*/
protected $consoleDB = null;
public function getName(): string {
return "deletes";
}
public function init(): void
{
}

View file

@ -100,6 +100,10 @@ class FunctionsV1 extends Worker
public array $allowed = [];
public function getName(): string {
return "functions";
}
public function init(): void
{
}

View file

@ -13,6 +13,10 @@ Console::success(APP_NAME . ' mails worker v1 has started' . "\n");
class MailsV1 extends Worker
{
public function getName(): string {
return "mails";
}
public function init(): void
{
}

View file

@ -11,6 +11,10 @@ Console::success(APP_NAME . ' webhooks worker v1 has started');
class WebhooksV1 extends Worker
{
public function getName(): string {
return "webhooks";
}
public function init(): void
{
}

View file

@ -39,6 +39,7 @@
"appwrite/php-runtimes": "0.6.*",
"utopia-php/framework": "0.19.*",
"utopia-php/logger": "0.1.*",
"utopia-php/abuse": "0.7.*",
"utopia-php/analytics": "0.2.*",
"utopia-php/audit": "0.8.*",
@ -53,7 +54,7 @@
"utopia-php/domains": "1.1.*",
"utopia-php/swoole": "0.3.*",
"utopia-php/storage": "0.5.*",
"utopia-php/websocket": "0.0.*",
"utopia-php/websocket": "0.1.0",
"utopia-php/image": "0.5.*",
"resque/php-resque": "1.3.6",

82
composer.lock generated
View file

@ -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": "2b1ed15e618832ee86b69cff2dcdd6ac",
"content-hash": "e0243d2a276d074c4af4ac21f521c953",
"packages": [
{
"name": "adhocore/jwt",
@ -2405,6 +2405,72 @@
},
"time": "2021-07-24T11:35:55+00:00"
},
{
"name": "utopia-php/logger",
"version": "0.1.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/logger.git",
"reference": "a7d626e349e8736e46d4d75f5ba686b40e73c097"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/logger/zipball/a7d626e349e8736e46d4d75f5ba686b40e73c097",
"reference": "a7d626e349e8736e46d4d75f5ba686b40e73c097",
"shasum": ""
},
"require": {
"php": ">=8.0"
},
"require-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"
],
"authors": [
{
"name": "Eldad Fux",
"email": "eldad@appwrite.io"
},
{
"name": "Matej Bačo",
"email": "matej@appwrite.io"
},
{
"name": "Christy Jacob",
"email": "christy@appwrite.io"
}
],
"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.1.0"
},
"time": "2021-12-20T06:57:26+00:00"
},
{
"name": "utopia-php/orchestration",
"version": "0.2.1",
@ -2730,16 +2796,16 @@
},
{
"name": "utopia-php/websocket",
"version": "0.0.1",
"version": "0.1.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/websocket.git",
"reference": "808317ef4ea0683c2c82dee5d543b1c8378e2e1b"
"reference": "51fcb86171400d8aa40d76c54593481fd273dab5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/websocket/zipball/808317ef4ea0683c2c82dee5d543b1c8378e2e1b",
"reference": "808317ef4ea0683c2c82dee5d543b1c8378e2e1b",
"url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5",
"reference": "51fcb86171400d8aa40d76c54593481fd273dab5",
"shasum": ""
},
"require": {
@ -2782,9 +2848,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/websocket/issues",
"source": "https://github.com/utopia-php/websocket/tree/0.0.1"
"source": "https://github.com/utopia-php/websocket/tree/0.1.0"
},
"time": "2021-07-11T13:09:44+00:00"
"time": "2021-12-20T10:50:09+00:00"
},
{
"name": "webmozart/assert",
@ -6520,5 +6586,5 @@
"platform-overrides": {
"php": "8.0"
},
"plugin-api-version": "2.0.0"
"plugin-api-version": "2.1.0"
}

View file

@ -125,6 +125,8 @@ services:
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_RUNTIMES
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-realtime:
entrypoint: realtime
@ -154,6 +156,7 @@ services:
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
# - ./vendor:/usr/src/code/vendor
depends_on:
- redis
environment:
@ -168,6 +171,8 @@ services:
- _APP_DB_USER
- _APP_DB_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-audits:
entrypoint: worker-audits
@ -193,6 +198,8 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-webhooks:
entrypoint: worker-webhooks
@ -215,6 +222,8 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-deletes:
entrypoint: worker-deletes
@ -244,6 +253,8 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-database:
entrypoint: worker-database
@ -270,6 +281,8 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-certificates:
entrypoint: worker-certificates
@ -299,6 +312,8 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-functions:
entrypoint: worker-functions
@ -339,6 +354,8 @@ services:
- _APP_STATSD_PORT
- DOCKERHUB_PULL_USERNAME
- DOCKERHUB_PULL_PASSWORD
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-mails:
entrypoint: worker-mails
@ -367,6 +384,8 @@ services:
- _APP_SMTP_SECURE
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-maintenance:
entrypoint: maintenance

View file

@ -9,15 +9,66 @@ use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Adapter\MariaDB;
use Exception;
abstract class Worker
{
/**
* Callbacks that will be executed when an error occurs
*
* @var array
*/
static protected array $errorCallbacks = [];
/**
* Associative array holding all information passed into the worker
*
* @return array
*/
public array $args = [];
abstract public function init(): void;
/**
* Function for identifying the worker needs to be set to unique name
*
* @return string
* @throws Exception
*/
public function getName(): string
{
throw new Exception("Please implement getName method in worker");
}
abstract public function run(): void;
/**
* Function executed before running first task.
* Can include any preparations, such as connecting to external services or loading files
*
* @return void
* @throws \Exception|\Throwable
*/
public function init() {
throw new Exception("Please implement getName method in worker");
}
abstract public function shutdown(): void;
/**
* Function executed when new task requests is received.
* You can access $args here, it will contain event information
*
* @return void
* @throws \Exception|\Throwable
*/
public function run() {
throw new Exception("Please implement getName method in worker");
}
/**
* Function executed just before shutting down the worker.
* You can do cleanup here, such as disconnecting from services or removing temp files
*
* @return void
* @throws \Exception|\Throwable
*/
public function shutdown() {
throw new Exception("Please implement getName method in worker");
}
const MAX_ATTEMPTS = 10;
const SLEEP_TIME = 2;
@ -25,19 +76,73 @@ abstract class Worker
const DATABASE_PROJECT = 'project';
const DATABASE_CONSOLE = 'console';
/**
* A wrapper around 'init' function with non-worker-specific code
*
* @return void
* @throws \Exception|\Throwable
*/
public function setUp(): void
{
$this->init();
try {
$this->init();
} catch(\Throwable $error) {
foreach (self::$errorCallbacks as $errorCallback) {
$errorCallback($error, "init", $this->getName());
}
throw $error;
}
}
/**
* A wrapper around 'run' function with non-worker-specific code
*
* @return void
* @throws \Exception|\Throwable
*/
public function perform(): void
{
$this->run();
try {
$this->run();
} catch(\Throwable $error) {
foreach (self::$errorCallbacks as $errorCallback) {
$errorCallback($error, "run", $this->getName(), $this->args);
}
throw $error;
}
}
/**
* A wrapper around 'shutdown' function with non-worker-specific code
*
* @return void
* @throws \Exception|\Throwable
*/
public function tearDown(): void
{
$this->shutdown();
try {
$this->shutdown();
} catch(\Throwable $error) {
foreach (self::$errorCallbacks as $errorCallback) {
$errorCallback($error, "shutdown", $this->getName());
}
throw $error;
}
}
/**
* Register callback. Will be executed when error occurs.
* @param callable $callback
* @param Throwable $error
* @return self
*/
public static function error(callable $callback): void
{
\array_push(self::$errorCallbacks, $callback);
}
/**
* Get internal project database