Files
FreshRSS/lib/Minz/Log.php
T
Inverle 1f055da218 Avoid writing to log files during unit tests (#9373)
* Avoid writing to log files during unit tests

* Translate PHPDoc of `record()`

* Move environment variable into `phpunit.xml` and use tabs for indents
2026-09-30 21:50:47 +02:00

134 lines
4.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
/**
* MINZ - Copyright 2011 Marien Fressinaud
* Sous licence AGPL3 <https://www.gnu.org/licenses/>
*/
/**
* The Minz_Log class is used to log errors and warnings
*/
class Minz_Log {
/**
* Syslog priority corresponding to each value accepted by the `log_level` system setting,
* from the most to the least severe.
* @var array<string,int>
*/
private const LOG_LEVELS = [
'error' => LOG_ERR,
'warning' => LOG_WARNING,
'notice' => LOG_NOTICE,
'info' => LOG_INFO,
'debug' => LOG_DEBUG,
];
/**
* Records a message in a specific log file
* The message is not logged if
* - environment = SILENT
* - level is less severe than the threshold determined by `log_level`,
* or, by default, by `environment` (PRODUCTION keeps only warnings and errors)
* @param string $information error message / information to record
* @param int $level error level https://www.php.net/function.syslog
* @param string $file_name log file
* @throws Minz_PermissionDeniedException
*/
public static function record(string $information, int $level, ?string $file_name = null): void {
$env = getenv('FRESHRSS_ENV');
$log_level = '';
try {
$conf = Minz_Configuration::get('system');
$log_level = $conf->log_level;
if ($env == '') {
$env = $conf->environment;
}
} catch (Minz_ConfigurationException $e) {
if ($env == '') {
$env = 'production';
}
}
if ($log_level === '' || !isset(self::LOG_LEVELS[$log_level])) {
$log_level = match ($env) {
'silent' => 'error',
'production' => 'warning',
default => 'debug',
};
}
if (! ($env === 'silent' || $level > self::LOG_LEVELS[$log_level])) {
$username = Minz_User::name() ?? Minz_User::INTERNAL_USER;
if ($file_name == null) {
$file_name = join_path(USERS_PATH, $username, LOG_FILENAME);
}
$level_labels = array_flip(self::LOG_LEVELS);
if (!isset($level_labels[$level])) {
$level = LOG_INFO;
}
$level_label = $level_labels[$level];
$log = '[' . date('r') . '] [' . $level_label . '] --- ' . str_replace(["\r", "\n"], ' ', $information) . "\n";
if (defined('COPY_LOG_TO_SYSLOG') && COPY_LOG_TO_SYSLOG) {
syslog($level, '[' . $username . '] ' . trim($log));
}
self::ensureMaxLogSize($file_name);
if (file_put_contents($file_name, $log, FILE_APPEND | LOCK_EX) === false) {
throw new Minz_PermissionDeniedException($file_name, Minz_Exception::ERROR);
}
}
}
/**
* Make sure we do not waste a huge amount of disk space with old log messages.
*
* This method can be called multiple times for one script execution, but its result will not change unless
* you call clearstatcache() in between. We won’t do do that for performance reasons.
*
* @throws Minz_PermissionDeniedException
*/
protected static function ensureMaxLogSize(string $file_name): void {
$maxSize = defined('MAX_LOG_SIZE') ? MAX_LOG_SIZE : 1048576;
if ($maxSize > 0 && @filesize($file_name) > $maxSize) {
$fp = fopen($file_name, 'c+');
if (is_resource($fp) && flock($fp, LOCK_EX)) {
fseek($fp, -(int)($maxSize / 2), SEEK_END);
$content = fread($fp, $maxSize);
rewind($fp);
ftruncate($fp, 0);
fwrite($fp, $content ?: '');
fwrite($fp, sprintf("[%s] [notice] --- Log rotate.\n", date('r')));
fflush($fp);
flock($fp, LOCK_UN);
} else {
throw new Minz_PermissionDeniedException($file_name, Minz_Exception::ERROR);
}
fclose($fp);
}
}
/**
* Some helpers to Minz_Log::record() method
* Parameters are the same of those of the record() method.
* @throws Minz_PermissionDeniedException
*/
public static function debug(string $msg, ?string $file_name = null): void {
self::record($msg, LOG_DEBUG, $file_name);
}
/** @throws Minz_PermissionDeniedException */
public static function notice(string $msg, ?string $file_name = null): void {
self::record($msg, LOG_NOTICE, $file_name);
}
/** @throws Minz_PermissionDeniedException */
public static function warning(string $msg, ?string $file_name = null): void {
self::record($msg, LOG_WARNING, $file_name);
}
/** @throws Minz_PermissionDeniedException */
public static function error(string $msg, ?string $file_name = null): void {
self::record($msg, LOG_ERR, $file_name);
}
}