diff options
Diffstat (limited to 'engine/logging.php')
-rw-r--r-- | engine/logging.php | 271 |
1 files changed, 174 insertions, 97 deletions
diff --git a/engine/logging.php b/engine/logging.php index 24803cf..d3fcc78 100644 --- a/engine/logging.php +++ b/engine/logging.php @@ -1,96 +1,122 @@ <?php -require_once 'engine/ansi.php'; -use \ansi\Color; -use function \ansi\wrap; - -enum LogLevel { - case ERROR; - case WARNING; - case INFO; - case DEBUG; -} - -function logDebug(...$args): void { logging::logCustom(LogLevel::DEBUG, ...$args); } -function logInfo(...$args): void { logging::logCustom(LogLevel::INFO, ...$args); } -function logWarning(...$args): void { logging::logCustom(LogLevel::WARNING, ...$args); } -function logError(...$args): void { logging::logCustom(LogLevel::ERROR, ...$args); } +require_once 'lib/ansi.php'; -class logging { +enum LogLevel: int { + case ERROR = 10; + case WARNING = 5; + case INFO = 3; + case DEBUG = 2; +} - // private static $instance = null; +function logDebug(...$args): void { global $__logger; $__logger->log(LogLevel::DEBUG, ...$args); } +function logInfo(...$args): void { global $__logger; $__logger->log(LogLevel::INFO, ...$args); } +function logWarning(...$args): void { global $__logger; $__logger->log(LogLevel::WARNING, ...$args); } +function logError(...$args): void { global $__logger; $__logger->log(LogLevel::ERROR, ...$args); } - protected static ?string $logFile = null; - protected static bool $enabled = false; - protected static int $counter = 0; +abstract class Logger { + protected bool $enabled = false; + protected int $counter = 0; + protected int $recursionLevel = 0; /** @var ?callable $filter */ - protected static $filter = null; + protected $filter = null; - public static function setLogFile(string $log_file): void { - self::$logFile = $log_file; + function setErrorFilter(callable $filter): void { + $this->filter = $filter; } - public static function setErrorFilter(callable $filter): void { - self::$filter = $filter; + function disable(): void { + $this->enabled = false; } - public static function disable(): void { - self::$enabled = false; + function enable(): void { + static $error_handler_set = false; + $this->enabled = true; - restore_error_handler(); - register_shutdown_function(function() {}); - } + if ($error_handler_set) + return; - public static function enable(): void { - self::$enabled = true; + $self = $this; - set_error_handler(function($no, $str, $file, $line) { - if (is_callable(self::$filter) && !(self::$filter)($no, $file, $line, $str)) + set_error_handler(function($no, $str, $file, $line) use ($self) { + if (!$self->enabled) return; - self::write(LogLevel::ERROR, $str, + if (is_callable($self->filter) && !($self->filter)($no, $file, $line, $str)) + return; + + static::write(LogLevel::ERROR, $str, errno: $no, errfile: $file, errline: $line); }); - register_shutdown_function(function() { - if (!($error = error_get_last())) + register_shutdown_function(function () use ($self) { + if (!$self->enabled || !($error = error_get_last())) return; - if (is_callable(self::$filter) - && !(self::$filter)($error['type'], $error['file'], $error['line'], $error['message'])) { + if (is_callable($self->filter) + && !($self->filter)($error['type'], $error['file'], $error['line'], $error['message'])) { return; } - self::write(LogLevel::ERROR, $error['message'], + static::write(LogLevel::ERROR, $error['message'], errno: $error['type'], - errfile: $error['file'], + errfile: $error['file'], errline: $error['line']); }); + + $error_handler_set = true; } - public static function logCustom(LogLevel $level, ...$args): void { - global $config; - if (!$config['is_dev'] && $level == LogLevel::DEBUG) + function log(LogLevel $level, ...$args): void { + if (!is_dev() && $level == LogLevel::DEBUG) return; - self::write($level, self::strVars($args)); + $this->write($level, strVars($args)); } - protected static function write(LogLevel $level, - string $message, - ?int $errno = null, - ?string $errfile = null, - ?string $errline = null): void { + protected function canReport(): bool { + return $this->recursionLevel < 3; + } + + protected function write(LogLevel $level, + string $message, + ?int $errno = null, + ?string $errfile = null, + ?string $errline = null): void { + $this->recursionLevel++; + + if ($this->canReport()) + $this->writer($level, $this->counter++, $message, $errno, $errfile, $errline); + + $this->recursionLevel--; + } + + abstract protected function writer(LogLevel $level, + int $num, + string $message, + ?int $errno = null, + ?string $errfile = null, + ?string $errline = null): void; +} + +class FileLogger extends Logger { + + function __construct(protected string $logFile) {} - // TODO test - if (is_null(self::$logFile)) { + protected function writer(LogLevel $level, + int $num, + string $message, + ?int $errno = null, + ?string $errfile = null, + ?string $errline = null): void + { + if (is_null($this->logFile)) { fprintf(STDERR, __METHOD__.': logfile is not set'); return; } - $num = self::$counter++; $time = time(); // TODO rewrite using sprintf @@ -98,47 +124,56 @@ class logging { if (strlen($exec_time) < 6) $exec_time .= str_repeat('0', 6 - strlen($exec_time)); - // $bt = backtrace(2); - - $title = PHP_SAPI == 'cli' ? 'cli' : $_SERVER['REQUEST_URI']; + $title = is_cli() ? 'cli' : $_SERVER['REQUEST_URI']; $date = date('d/m/y H:i:s', $time); $buf = ''; if ($num == 0) { - $buf .= wrap(" $title ", - fg: Color::WHITE, - bg: Color::MAGENTA, - fg_bright: true, - bold: true); - $buf .= wrap(" $date ", fg: Color::WHITE, bg: Color::BLUE, fg_bright: true); + $buf .= ansi(" $title ", + fg: AnsiColor::WHITE, + bg: AnsiColor::MAGENTA, + bold: true, + fg_bright: true); + $buf .= ansi(" $date ", fg: AnsiColor::WHITE, bg: AnsiColor::BLUE, fg_bright: true); $buf .= "\n"; } $letter = strtoupper($level->name[0]); $color = match ($level) { - LogLevel::ERROR => Color::RED, - LogLevel::INFO, LogLevel::DEBUG => Color::WHITE, - LogLevel::WARNING => Color::YELLOW + LogLevel::ERROR => AnsiColor::RED, + LogLevel::INFO => AnsiColor::GREEN, + LogLevel::DEBUG => AnsiColor::WHITE, + LogLevel::WARNING => AnsiColor::YELLOW }; - $buf .= wrap($letter.wrap('='.wrap($num, bold: true)), fg: $color).' '; - $buf .= wrap($exec_time, fg: Color::CYAN).' '; + $buf .= ansi($letter.ansi('='.ansi($num, bold: true)), fg: $color).' '; + $buf .= ansi($exec_time, fg: AnsiColor::CYAN).' '; if (!is_null($errno)) { - $buf .= wrap($errfile, fg: Color::GREEN); - $buf .= wrap(':', fg: Color::WHITE); - $buf .= wrap($errline, fg: Color::GREEN, fg_bright: true); - $buf .= ' ('.self::getPhpErrorName($errno).') '; + $buf .= ansi($errfile, fg: AnsiColor::GREEN); + $buf .= ansi(':', fg: AnsiColor::WHITE); + $buf .= ansi($errline, fg: AnsiColor::GREEN, fg_bright: true); + $buf .= ' ('.getPHPErrorName($errno).') '; } $buf .= $message."\n"; if (in_array($level, [LogLevel::ERROR, LogLevel::WARNING])) - $buf .= backtrace(2)."\n"; + $buf .= backtrace_as_string(2)."\n"; + + $set_perm = false; + if (!file_exists($this->logFile)) { + $set_perm = true; + $dir = dirname($this->logFile); + echo "dir: $dir\n"; + + if (!file_exists($dir)) { + mkdir($dir); + setperm($dir); + } + } - // TODO test - $set_perm = !file_exists(self::$logFile); - $f = fopen(self::$logFile, 'a'); + $f = fopen($this->logFile, 'a'); if (!$f) { - fprintf(STDERR, __METHOD__.': failed to open file "'.self::$logFile.'" for writing'); + fprintf(STDERR, __METHOD__.': failed to open file \''.$this->logFile.'\' for writing'); return; } @@ -146,34 +181,76 @@ class logging { fclose($f); if ($set_perm) - setperm(self::$logFile); + setperm($this->logFile); } - protected static function getPhpErrorName(int $errno): string { - static $errors = null; - if (is_null($errors)) - $errors = array_flip(array_slice(get_defined_constants(true)['Core'], 0, 15, true)); - return $errors[$errno]; - } +} - protected static function strVarDump($var, bool $print_r = false): string { - ob_start(); - $print_r ? print_r($var) : var_dump($var); - return trim(ob_get_clean()); - } +class DatabaseLogger extends Logger { + protected function writer(LogLevel $level, + int $num, + string $message, + ?int $errno = null, + ?string $errfile = null, + ?string $errline = null): void + { + $db = DB(); + + $data = [ + 'ts' => time(), + 'num' => $num, + 'time' => exectime(), + 'errno' => $errno, + 'file' => $errfile, + 'line' => $errline, + 'text' => $message, + 'level' => $level->value, + 'stacktrace' => backtrace_as_string(2), + 'is_cli' => intval(is_cli()), + 'user_id' => 0, + ]; + + if (is_cli()) { + $data += [ + 'ip' => '', + 'ua' => '', + 'url' => '', + ]; + } else { + $data += [ + 'ip' => ip2ulong($_SERVER['REMOTE_ADDR']), + 'ua' => $_SERVER['HTTP_USER_AGENT'] ?? '', + 'url' => $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'] + ]; + } - protected static function strVars(array $args): string { - $args = array_map(fn($a) => match (gettype($a)) { - 'string' => $a, - 'array', 'object' => self::strVarDump($a, true), - default => self::strVarDump($a) - }, $args); - return implode(' ', $args); + $db->insert('backend_errors', $data); } +} + +function getPHPErrorName(int $errno): string { + static $errors = null; + if (is_null($errors)) + $errors = array_flip(array_slice(get_defined_constants(true)['Core'], 0, 15, true)); + return $errors[$errno]; +} + +function strVarDump($var, bool $print_r = false): string { + ob_start(); + $print_r ? print_r($var) : var_dump($var); + return trim(ob_get_clean()); +} +function strVars(array $args): string { + $args = array_map(fn($a) => match (gettype($a)) { + 'string' => $a, + 'array', 'object' => strVarDump($a, true), + default => strVarDump($a) + }, $args); + return implode(' ', $args); } -function backtrace(int $shift = 0): string { +function backtrace_as_string(int $shift = 0): string { $bt = debug_backtrace(); $lines = []; foreach ($bt as $i => $t) { @@ -187,4 +264,4 @@ function backtrace(int $shift = 0): string { } } return implode("\n", $lines); -} +}
\ No newline at end of file |