From 5dab17e5f457828c20072151b5556f965b48d54b Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 4 Oct 2024 01:32:14 +0200 Subject: [PATCH 1/7] log-driver.php: move to Data --- inc/{driver => Data/Driver}/log-driver.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename inc/{driver => Data/Driver}/log-driver.php (100%) diff --git a/inc/driver/log-driver.php b/inc/Data/Driver/log-driver.php similarity index 100% rename from inc/driver/log-driver.php rename to inc/Data/Driver/log-driver.php From 36d48951c182da5ad1ad7f2d2c715c32bad48265 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 4 Oct 2024 12:54:49 +0200 Subject: [PATCH 2/7] log-driver.php: split up log driver --- inc/Data/Driver/ErrorLogLogDriver.php | 28 ++++ inc/Data/Driver/FileLogDriver.php | 60 ++++++++ inc/Data/Driver/LogDriver.php | 22 +++ inc/Data/Driver/LogTrait.php | 26 ++++ inc/Data/Driver/StderrLogDriver.php | 27 ++++ inc/Data/Driver/SyslogLogDriver.php | 35 +++++ inc/Data/Driver/log-driver.php | 189 -------------------------- 7 files changed, 198 insertions(+), 189 deletions(-) create mode 100644 inc/Data/Driver/ErrorLogLogDriver.php create mode 100644 inc/Data/Driver/FileLogDriver.php create mode 100644 inc/Data/Driver/LogDriver.php create mode 100644 inc/Data/Driver/LogTrait.php create mode 100644 inc/Data/Driver/StderrLogDriver.php create mode 100644 inc/Data/Driver/SyslogLogDriver.php delete mode 100644 inc/Data/Driver/log-driver.php diff --git a/inc/Data/Driver/ErrorLogLogDriver.php b/inc/Data/Driver/ErrorLogLogDriver.php new file mode 100644 index 00000000..e2050606 --- /dev/null +++ b/inc/Data/Driver/ErrorLogLogDriver.php @@ -0,0 +1,28 @@ +name = $name; + $this->level = $level; + } + + public function log(int $level, string $message): void { + if ($level <= $this->level) { + $lv = $this->levelToString($level); + $line = "{$this->name} $lv: $message"; + \error_log($line, 0, null, null); + } + } +} diff --git a/inc/Data/Driver/FileLogDriver.php b/inc/Data/Driver/FileLogDriver.php new file mode 100644 index 00000000..18423cf1 --- /dev/null +++ b/inc/Data/Driver/FileLogDriver.php @@ -0,0 +1,60 @@ +fd = \fopen($file_path, 'a'); + if ($this->fd === false) { + throw new \RuntimeException("Unable to open log file at $file_path"); + } + + $this->name = $name; + $this->level = $level; + + // In some cases PHP does not run the destructor. + \register_shutdown_function([$this, 'close']); + } + + public function __destruct() { + $this->close(); + } + + public function log(int $level, string $message): void { + if ($level <= $this->level) { + $lv = $this->levelToString($level); + $line = "{$this->name} $lv: $message\n"; + \flock($this->fd, LOCK_EX); + \fwrite($this->fd, $line); + \flock($this->fd, LOCK_UN); + } + } + + public function close() { + \flock($this->fd, LOCK_UN); + \fclose($this->fd); + } +} diff --git a/inc/Data/Driver/LogDriver.php b/inc/Data/Driver/LogDriver.php new file mode 100644 index 00000000..fddc3f27 --- /dev/null +++ b/inc/Data/Driver/LogDriver.php @@ -0,0 +1,22 @@ +name = $name; + $this->level = $level; + } + + public function log(int $level, string $message): void { + if ($level <= $this->level) { + $lv = $this->levelToString($level); + \fwrite(\STDERR, "{$this->name} $lv: $message\n"); + } + } +} diff --git a/inc/Data/Driver/SyslogLogDriver.php b/inc/Data/Driver/SyslogLogDriver.php new file mode 100644 index 00000000..c0df5304 --- /dev/null +++ b/inc/Data/Driver/SyslogLogDriver.php @@ -0,0 +1,35 @@ +level = $level; + } + + public function log(int $level, string $message): void { + if ($level <= $this->level) { + if (isset($_SERVER['REMOTE_ADDR'], $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'])) { + // CGI + \syslog($level, "$message - client: {$_SERVER['REMOTE_ADDR']}, request: \"{$_SERVER['REQUEST_METHOD']} {$_SERVER['REQUEST_URI']}\""); + } else { + \syslog($level, $message); + } + } + } +} diff --git a/inc/Data/Driver/log-driver.php b/inc/Data/Driver/log-driver.php deleted file mode 100644 index 0026f009..00000000 --- a/inc/Data/Driver/log-driver.php +++ /dev/null @@ -1,189 +0,0 @@ -level = $level; - } - - public function log(int $level, string $message): void { - if ($level <= $this->level) { - if (isset($_SERVER['REMOTE_ADDR'], $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'])) { - // CGI - syslog($level, "$message - client: {$_SERVER['REMOTE_ADDR']}, request: \"{$_SERVER['REQUEST_METHOD']} {$_SERVER['REQUEST_URI']}\""); - } else { - syslog($level, $message); - } - } - } - }; - } - - /** - * Log via the php function error_log. - */ - public static function error_log(string $name, int $level): Log { - return new class($name, $level) implements Log { - private string $name; - private int $level; - - public function __construct(string $name, int $level) { - $this->name = $name; - $this->level = $level; - } - - public function log(int $level, string $message): void { - if ($level <= $this->level) { - $lv = LogDrivers::levelToString($level); - $line = "{$this->name} $lv: $message"; - error_log($line, 0, null, null); - } - } - }; - } - - /** - * Log to a file. - */ - public static function file(string $name, int $level, string $file_path): Log { - /* - * error_log is slow as hell in it's 3rd mode, so use fopen + file locking instead. - * https://grobmeier.solutions/performance-ofnonblocking-write-to-files-via-php-21082009.html - * - * Whatever file appending is atomic is contentious: - * - There are no POSIX guarantees: https://stackoverflow.com/a/7237901 - * - But linus suggested they are on linux, on some filesystems: https://web.archive.org/web/20151201111541/http://article.gmane.org/gmane.linux.kernel/43445 - * - But it doesn't seem to be always the case: https://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/ - * - * So we just use file locking to be sure. - */ - - $fd = fopen($file_path, 'a'); - if ($fd === false) { - throw new RuntimeException("Unable to open log file at $file_path"); - } - - $logger = new class($name, $level, $fd) implements Log { - private string $name; - private int $level; - private mixed $fd; - - public function __construct(string $name, int $level, mixed $fd) { - $this->name = $name; - $this->level = $level; - $this->fd = $fd; - } - - public function log(int $level, string $message): void { - if ($level <= $this->level) { - $lv = LogDrivers::levelToString($level); - $line = "{$this->name} $lv: $message\n"; - flock($this->fd, LOCK_EX); - fwrite($this->fd, $line); - flock($this->fd, LOCK_UN); - } - } - - public function close() { - fclose($this->fd); - } - }; - - // Close the file on shutdown. - register_shutdown_function([$logger, 'close']); - - return $logger; - } - - /** - * Log to php's standard error file stream. - */ - public static function stderr(string $name, int $level): Log { - return new class($name, $level) implements Log { - private $name; - private $level; - - public function __construct(string $name, int $level) { - $this->name = $name; - $this->level = $level; - } - - public function log(int $level, string $message): void { - if ($level <= $this->level) { - $lv = LogDrivers::levelToString($level); - fwrite(STDERR, "{$this->name} $lv: $message\n"); - } - } - }; - } - - /** - * No-op logging system. - */ - public static function none(): Log { - return new class() implements Log { - public function log($level, $message): void { - // No-op. - } - }; - } -} - -interface Log { - public const EMERG = LOG_EMERG; - public const ERROR = LOG_ERR; - public const WARNING = LOG_WARNING; - public const NOTICE = LOG_NOTICE; - public const INFO = LOG_INFO; - public const DEBUG = LOG_DEBUG; - - - /** - * Log a message if the level of relevancy is at least the minimum. - * - * @param int $level Message level. Use Log interface constants. - * @param string $message The message to log. - */ - public function log(int $level, string $message): void; -} From 79af4b34dd12305e78a3ab9108c23ea8bec825c4 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 4 Oct 2024 12:57:37 +0200 Subject: [PATCH 3/7] context.php: update LogDriver --- inc/context.php | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/inc/context.php b/inc/context.php index c64f6a9c..c3ebef04 100644 --- a/inc/context.php +++ b/inc/context.php @@ -2,8 +2,7 @@ namespace Vichan; use RuntimeException; -use Vichan\Driver\{Log, LogDrivers}; -use Vichan\Data\Driver\HttpDriver; +use Vichan\Data\Driver\{HttpDriver, ErrorLogLogDriver, FileLogDriver, LogDriver, StderrLogDriver, SyslogLogDriver}; use Vichan\Service\HCaptchaQuery; use Vichan\Service\NativeCaptchaQuery; use Vichan\Service\ReCaptchaQuery; @@ -35,24 +34,22 @@ class Context { function build_context(array $config): Context { return new Context([ 'config' => $config, - Log::class => function($c) { + LogDriver::class => function($c) { $config = $c->get('config'); $name = $config['log_system']['name']; - $level = $config['debug'] ? Log::DEBUG : Log::NOTICE; + $level = $config['debug'] ? LogDriver::DEBUG : LogDriver::NOTICE; $backend = $config['log_system']['type']; // Check 'syslog' for backwards compatibility. if ((isset($config['syslog']) && $config['syslog']) || $backend === 'syslog') { - return LogDrivers::syslog($name, $level, $this->config['log_system']['syslog_stderr']); + return new SyslogLogDriver($name, $level, $this->config['log_system']['syslog_stderr']); } elseif ($backend === 'file') { - return LogDrivers::file($name, $level, $this->config['log_system']['file_path']); + return new FileLogDriver($name, $level, $this->config['log_system']['file_path']); } elseif ($backend === 'stderr') { - return LogDrivers::stderr($name, $level); - } elseif ($backend === 'none') { - return LogDrivers::none(); + return new StderrLogDriver($name, $level); } else { - return LogDrivers::error_log($name, $level); + return new ErrorLogLogDriver($name, $level); } }, HttpDriver::class => function($c) { From 927a8372165442b99b426a9da9025cb8cf696b04 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 4 Oct 2024 13:01:32 +0200 Subject: [PATCH 4/7] post.php: update LogDriver --- post.php | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/post.php b/post.php index bfd41639..47ae9820 100644 --- a/post.php +++ b/post.php @@ -6,7 +6,7 @@ require_once 'inc/bootstrap.php'; use Vichan\{Context, WebDependencyFactory}; -use Vichan\Data\Driver\{HttpDriver, Log}; +use Vichan\Data\Driver\{LogDriver, HttpDriver}; use Vichan\Service\{RemoteCaptchaQuery, NativeCaptchaQuery}; use Vichan\Functions\Format; @@ -286,7 +286,7 @@ if (isset($_GET['Newsgroups']) && $config['nntpchan']['enabled']) { $content = file_get_contents("php://input"); } elseif ($ct == 'multipart/mixed' || $ct == 'multipart/form-data') { - $context->get(Log::class)->log(Log::DEBUG, 'MM: Files: ' . print_r($GLOBALS, true)); + $context->get(LogDriver::class)->log(LogDriver::DEBUG, 'MM: Files: ' . print_r($GLOBALS, true)); $content = ''; @@ -454,8 +454,8 @@ if (isset($_POST['delete'])) { modLog("User at $ip deleted their own post #$id"); } - $context->get(Log::class)->log( - Log::INFO, + $context->get(LogDriver::class)->log( + LogDriver::INFO, 'Deleted post: /' . $board['dir'] . $config['dir']['res'] . link_for($post) . ($post['thread'] ? '#' . $id : '') ); } @@ -531,7 +531,7 @@ if (isset($_POST['delete'])) { error($config['error']['captcha']); } } catch (RuntimeException $e) { - $context->get(Log::class)->log(Log::ERROR, "Native captcha IO exception: {$e->getMessage()}"); + $context->get(LogDriver::class)->log(LogDriver::ERROR, "Native captcha IO exception: {$e->getMessage()}"); error($config['error']['local_io_error']); } } @@ -550,7 +550,7 @@ if (isset($_POST['delete'])) { $post = $query->fetch(PDO::FETCH_ASSOC); if ($post === false) { - $context->get(Log::class)->log(Log::INFO, "Failed to report non-existing post #{$id} in {$board['dir']}"); + $context->get(LogDriver::class)->log(LogDriver::INFO, "Failed to report non-existing post #{$id} in {$board['dir']}"); error($config['error']['nopost']); } @@ -559,8 +559,8 @@ if (isset($_POST['delete'])) { error($error); } - $context->get(Log::class)->log( - Log::INFO, + $context->get(LogDriver::class)->log( + LogDriver::INFO, 'Reported post: /' . $board['dir'] . $config['dir']['res'] . link_for($post) . ($post['thread'] ? '#' . $id : '') . " for \"$reason\"" @@ -673,10 +673,10 @@ if (isset($_POST['delete'])) { } } } catch (RuntimeException $e) { - $context->get(Log::class)->log(Log::ERROR, "Captcha IO exception: {$e->getMessage()}"); + $context->get(LogDriver::class)->log(LogDriver::ERROR, "Captcha IO exception: {$e->getMessage()}"); error($config['error']['remote_io_error']); } catch (JsonException $e) { - $context->get(Log::class)->log(Log::ERROR, "Bad JSON reply to captcha: {$e->getMessage()}"); + $context->get(LogDriver::class)->log(LogDriver::ERROR, "Bad JSON reply to captcha: {$e->getMessage()}"); error($config['error']['remote_io_error']); } @@ -1162,7 +1162,7 @@ if (isset($_POST['delete'])) { try { $file['size'] = strip_image_metadata($file['tmp_name']); } catch (RuntimeException $e) { - $context->get(Log::class)->log(Log::ERROR, "Could not strip image metadata: {$e->getMessage()}"); + $context->get(LogDriver::class)->log(LogDriver::ERROR, "Could not strip image metadata: {$e->getMessage()}"); // Since EXIF metadata can countain sensible info, fail the request. error(_('Could not strip EXIF metadata!'), null, $error); } @@ -1200,7 +1200,7 @@ if (isset($_POST['delete'])) { $post['body_nomarkup'] .= "" . htmlspecialchars($txt) . ""; } } catch (RuntimeException $e) { - $context->get(Log::class)->log(Log::ERROR, "Could not OCR image: {$e->getMessage()}"); + $context->get(LogDriver::class)->log(LogDriver::ERROR, "Could not OCR image: {$e->getMessage()}"); } } } @@ -1394,8 +1394,8 @@ if (isset($_POST['delete'])) { buildThread($post['op'] ? $id : $post['thread']); - $context->get(Log::class)->log( - Log::INFO, + $context->get(LogDriver::class)->log( + LogDriver::INFO, 'New post: /' . $board['dir'] . $config['dir']['res'] . link_for($post) . (!$post['op'] ? '#' . $id : '') ); From b501852ea49ae5c11355f64523bd65a796706380 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 4 Oct 2024 13:10:38 +0200 Subject: [PATCH 5/7] config.php: update LogDriver configuration --- inc/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/config.php b/inc/config.php index 15fe3f64..11ac8809 100644 --- a/inc/config.php +++ b/inc/config.php @@ -71,7 +71,7 @@ $config['log_system'] = [ /* * Log all error messages and unauthorized login attempts. - * Can be "syslog", "error_log" (default), "file", "stderr" or "none". + * Can be "syslog", "error_log" (default), "file", or "stderr". */ 'type' => 'error_log', // The application name used by the logging system. Defaults to "tinyboard" for backwards compatibility. From 65a668d3a8a7153c96838f2863c735303b949749 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 4 Oct 2024 13:11:08 +0200 Subject: [PATCH 6/7] composer.json: use classmap for LogDriver --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 5fdb4226..88ee789d 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,6 @@ "inc/functions/num.php", "inc/functions/format.php", "inc/functions/theme.php", - "inc/driver/log-driver.php", "inc/service/captcha-queries.php", "inc/context.php" ] From d88d6c814abb20eb3e3590d98d98cee466401592 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 4 Oct 2024 13:19:27 +0200 Subject: [PATCH 7/7] docker: use shorter name for instance --- compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compose.yml b/compose.yml index c691b303..4b87e0b6 100644 --- a/compose.yml +++ b/compose.yml @@ -9,7 +9,7 @@ services: depends_on: - db volumes: - - ./local-instances/${LOCAL_INSTANCE_NAME:-0}/www:/var/www/html + - ./local-instances/${INSTANCE:-0}/www:/var/www/html - ./docker/nginx/vichan.conf:/etc/nginx/conf.d/default.conf - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf - ./docker/nginx/proxy.conf:/etc/nginx/conf.d/proxy.conf @@ -21,7 +21,7 @@ services: context: . dockerfile: ./docker/php/Dockerfile volumes: - - ./local-instances/${LOCAL_INSTANCE_NAME:-0}/www:/var/www + - ./local-instances/${INSTANCE:-0}/www:/var/www - ./docker/php/www.conf:/usr/local/etc/php-fpm.d/www.conf - ./docker/php/jit.ini:/usr/local/etc/php/conf.d/jit.ini @@ -37,4 +37,4 @@ services: MYSQL_DATABASE: vichan MYSQL_ROOT_PASSWORD: password volumes: - - ./local-instances/${LOCAL_INSTANCE_NAME:-0}/mysql:/var/lib/mysql + - ./local-instances/${INSTANCE:-0}/mysql:/var/lib/mysql