feat: log plugin runtime errors
This commit is contained in:
@@ -10,6 +10,9 @@ if (!defined('IN_DA_PLUGIN') || IN_DA_PLUGIN !== true) {
|
|||||||
define('PLUGIN_ROOT', dirname(__DIR__));
|
define('PLUGIN_ROOT', dirname(__DIR__));
|
||||||
define('PLUGIN_EXEC_DIR', __DIR__);
|
define('PLUGIN_EXEC_DIR', __DIR__);
|
||||||
|
|
||||||
|
require_once PLUGIN_EXEC_DIR . '/lib/PluginLogger.php';
|
||||||
|
PluginLogger::init(PLUGIN_ROOT . '/plugin.log');
|
||||||
|
|
||||||
foreach ([
|
foreach ([
|
||||||
'Settings.php',
|
'Settings.php',
|
||||||
'BackendRuntimeConfig.php',
|
'BackendRuntimeConfig.php',
|
||||||
@@ -58,6 +61,7 @@ try {
|
|||||||
}
|
}
|
||||||
$handler($ctx);
|
$handler($ctx);
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
|
PluginLogger::exception($e, 'BOOTSTRAP');
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
header('Content-Type: text/html; charset=UTF-8');
|
header('Content-Type: text/html; charset=UTF-8');
|
||||||
echo '<!doctype html><html><head><meta charset="utf-8"><title>Błąd</title></head><body>';
|
echo '<!doctype html><html><head><meta charset="utf-8"><title>Błąd</title></head><body>';
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ return static function (AppContext $ctx): void {
|
|||||||
}
|
}
|
||||||
Http::redirect($ctx->url('index.html', ['status' => 'created']));
|
Http::redirect($ctx->url('index.html', ['status' => 'created']));
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
|
PluginLogger::exception($e, 'CREATE');
|
||||||
$errors[] = $e->getMessage();
|
$errors[] = $e->getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ return static function (AppContext $ctx): void {
|
|||||||
$ctx->audit->record('delete', $ctx->daUser->username(), ['rule_id' => $id, 'subject_prefix' => (string)($rule['subject_prefix'] ?? $rule['subject'] ?? '')]);
|
$ctx->audit->record('delete', $ctx->daUser->username(), ['rule_id' => $id, 'subject_prefix' => (string)($rule['subject_prefix'] ?? $rule['subject'] ?? '')]);
|
||||||
Http::redirect($ctx->url('index.html', ['status' => 'deleted']));
|
Http::redirect($ctx->url('index.html', ['status' => 'deleted']));
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
|
PluginLogger::exception($e, 'DELETE');
|
||||||
$ctx->render('Confirm deletion', '<div class="alert alert-err">' . AppContext::e($e->getMessage()) . '</div>');
|
$ctx->render('Confirm deletion', '<div class="alert alert-err">' . AppContext::e($e->getMessage()) . '</div>');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
return static function (AppContext $ctx): void {
|
return static function (AppContext $ctx): void {
|
||||||
|
try {
|
||||||
$ctx->requirePost();
|
$ctx->requirePost();
|
||||||
$id = $ctx->post('id');
|
$id = $ctx->post('id');
|
||||||
$ctx->requireCsrf('toggle:' . $id);
|
$ctx->requireCsrf('toggle:' . $id);
|
||||||
@@ -14,4 +15,8 @@ return static function (AppContext $ctx): void {
|
|||||||
$ctx->rules->update($ctx->daUser->username(), $id, $next);
|
$ctx->rules->update($ctx->daUser->username(), $id, $next);
|
||||||
$ctx->syncActiveBackend();
|
$ctx->syncActiveBackend();
|
||||||
Http::redirect($ctx->url('index.html', ['status' => 'toggled']));
|
Http::redirect($ctx->url('index.html', ['status' => 'toggled']));
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
PluginLogger::exception($e, 'TOGGLE');
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ return static function (AppContext $ctx): void {
|
|||||||
$ctx->syncActiveBackend();
|
$ctx->syncActiveBackend();
|
||||||
Http::redirect($ctx->url('index.html', ['status' => 'updated']));
|
Http::redirect($ctx->url('index.html', ['status' => 'updated']));
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
|
PluginLogger::exception($e, 'UPDATE');
|
||||||
$errors[] = $e->getMessage();
|
$errors[] = $e->getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
final class PluginLogger
|
||||||
|
{
|
||||||
|
private static string $path = '';
|
||||||
|
|
||||||
|
public static function init(string $path): void
|
||||||
|
{
|
||||||
|
self::$path = $path;
|
||||||
|
@ini_set('log_errors', '1');
|
||||||
|
@ini_set('error_log', $path);
|
||||||
|
|
||||||
|
set_error_handler(static function (int $severity, string $message, string $file, int $line): bool {
|
||||||
|
if ((error_reporting() & $severity) === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self::log('PHP_ERROR', self::severityName($severity) . ': ' . $message . ' in ' . $file . ':' . $line);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
register_shutdown_function(static function (): void {
|
||||||
|
$error = error_get_last();
|
||||||
|
if (!is_array($error)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$fatal = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR];
|
||||||
|
if (in_array((int)($error['type'] ?? 0), $fatal, true)) {
|
||||||
|
self::log('PHP_FATAL', self::severityName((int)$error['type']) . ': ' . (string)$error['message'] . ' in ' . (string)$error['file'] . ':' . (string)$error['line']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function exception(Throwable $e, string $context = 'UNCAUGHT'): void
|
||||||
|
{
|
||||||
|
self::log($context, get_class($e) . ': ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine() . "\n" . $e->getTraceAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function log(string $level, string $message): void
|
||||||
|
{
|
||||||
|
$path = self::$path !== '' ? self::$path : (defined('PLUGIN_ROOT') ? PLUGIN_ROOT . '/plugin.log' : __DIR__ . '/../../plugin.log');
|
||||||
|
$line = '[' . gmdate('c') . '] ' . $level . ' ' . str_replace(["\r\n", "\r"], "\n", $message) . "\n";
|
||||||
|
$ok = @file_put_contents($path, $line, FILE_APPEND | LOCK_EX);
|
||||||
|
if ($ok !== false && is_file($path)) {
|
||||||
|
@chmod($path, 0600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function severityName(int $severity): string
|
||||||
|
{
|
||||||
|
return match ($severity) {
|
||||||
|
E_ERROR => 'E_ERROR',
|
||||||
|
E_WARNING => 'E_WARNING',
|
||||||
|
E_PARSE => 'E_PARSE',
|
||||||
|
E_NOTICE => 'E_NOTICE',
|
||||||
|
E_CORE_ERROR => 'E_CORE_ERROR',
|
||||||
|
E_CORE_WARNING => 'E_CORE_WARNING',
|
||||||
|
E_COMPILE_ERROR => 'E_COMPILE_ERROR',
|
||||||
|
E_COMPILE_WARNING => 'E_COMPILE_WARNING',
|
||||||
|
E_USER_ERROR => 'E_USER_ERROR',
|
||||||
|
E_USER_WARNING => 'E_USER_WARNING',
|
||||||
|
E_USER_NOTICE => 'E_USER_NOTICE',
|
||||||
|
E_STRICT => 'E_STRICT',
|
||||||
|
E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
|
||||||
|
E_DEPRECATED => 'E_DEPRECATED',
|
||||||
|
E_USER_DEPRECATED => 'E_USER_DEPRECATED',
|
||||||
|
default => 'E_' . $severity,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -2,7 +2,7 @@ name=global-autoresponder
|
|||||||
id=global-autoresponder
|
id=global-autoresponder
|
||||||
type=user
|
type=user
|
||||||
author=HITME.PL
|
author=HITME.PL
|
||||||
version=1.1.1
|
version=1.1.2
|
||||||
active=no
|
active=no
|
||||||
installed=no
|
installed=no
|
||||||
user_run_as=root
|
user_run_as=root
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
require_once PLUGIN_ROOT . '/exec/lib/PluginLogger.php';
|
||||||
|
|
||||||
|
test('plugin logger writes exceptions to plugin log file', function (): void {
|
||||||
|
$path = TEST_TMP . '/plugin.log';
|
||||||
|
|
||||||
|
PluginLogger::init($path);
|
||||||
|
PluginLogger::exception(new RuntimeException('DirectAdmin API command failed'), 'CREATE');
|
||||||
|
|
||||||
|
$log = file_get_contents($path) ?: '';
|
||||||
|
assert_contains('CREATE RuntimeException: DirectAdmin API command failed', $log);
|
||||||
|
assert_contains('plugin_logger_test.php', $log);
|
||||||
|
assert_same('0600', substr(sprintf('%o', fileperms($path)), -4));
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user