mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-08-10 15:21:22 +02:00
Log level: production default was Level::Warning, but all facades
(AppLogger, AdminLogger, Audit) write at Monolog INFO level. This
silently discarded submission, admin action, and audit logs when
LOG_LEVEL env var was unset. Changed to Level::Info.
PeerTube: replaced raw error_log() calls in PeerTubeService::upload(),
deleteVideo(), FilepondHandler::process(), ThesisCreateController,
and ThesisFileHandler with structured logging:
- Successes → Logger::get('app')->info() (visible in App — soumissions tab)
- Failures → ErrorHandler::log('peertube_*', ...) (visible in Erreurs tab)
Added require_once for Logger and ErrorHandler in PeerTubeService.
111 lines
3.4 KiB
PHP
111 lines
3.4 KiB
PHP
<?php
|
|
|
|
use Monolog\Formatter\LineFormatter;
|
|
use Monolog\Handler\NullHandler;
|
|
use Monolog\Handler\RotatingFileHandler;
|
|
use Monolog\Level;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
/**
|
|
* Central logger factory — provides named Monolog channel instances
|
|
* backed by rotating JSON-line log files.
|
|
*
|
|
* Channels:
|
|
* - 'app' → replaces AppLogger
|
|
* - 'admin' → replaces AdminLogger file output
|
|
* - 'error' → replaces ErrorHandler logging
|
|
* - 'audit' → replaces Audit file shadow (DB writes stay in Audit)
|
|
*
|
|
* Usage:
|
|
* Logger::get('app')->info('message', [...]);
|
|
* Logger::get('admin')->warning('message', [...]);
|
|
*/
|
|
class Logger
|
|
{
|
|
/** @var array<string, LoggerInterface> */
|
|
private static array $channels = [];
|
|
|
|
/**
|
|
* Get (or lazily create) a named Monolog channel.
|
|
*/
|
|
public static function get(string $channel): LoggerInterface
|
|
{
|
|
if (!isset(self::$channels[$channel])) {
|
|
self::$channels[$channel] = self::create($channel);
|
|
}
|
|
return self::$channels[$channel];
|
|
}
|
|
|
|
/**
|
|
* Create a Monolog channel with rotating JSON file handler.
|
|
*
|
|
* Falls back to NullHandler if the log directory is not writable
|
|
* (e.g. CLI scripts on a machine that doesn't have the production path).
|
|
*/
|
|
private static function create(string $channel): \Monolog\Logger
|
|
{
|
|
$logDir = self::logDir();
|
|
|
|
if (!is_dir($logDir) && !@mkdir($logDir, 0755, true) && !is_dir($logDir)) {
|
|
// Directory can't be created — use null handler (graceful degradation)
|
|
$logger = new \Monolog\Logger($channel);
|
|
$logger->pushHandler(new NullHandler());
|
|
return $logger;
|
|
}
|
|
|
|
try {
|
|
$handler = new RotatingFileHandler(
|
|
$logDir . '/' . $channel . '.log',
|
|
30, // keep 30 days of logs
|
|
self::level()
|
|
);
|
|
} catch (\Throwable $e) {
|
|
// Can't open log file — use null handler
|
|
$logger = new \Monolog\Logger($channel);
|
|
$logger->pushHandler(new NullHandler());
|
|
return $logger;
|
|
}
|
|
|
|
// Pass-through formatter: the facades (AppLogger, AdminLogger, etc.)
|
|
// construct their own JSON lines and pass them as the log message.
|
|
// %message% preserves the existing JSON format contract exactly.
|
|
$handler->setFormatter(new LineFormatter("%message%\n", null, true));
|
|
|
|
$logger = new \Monolog\Logger($channel);
|
|
$logger->pushHandler($handler);
|
|
|
|
return $logger;
|
|
}
|
|
|
|
/**
|
|
* Read the LOG_LEVEL env var with sensible defaults.
|
|
*
|
|
* All facades (AppLogger, AdminLogger, Audit) write at INFO level,
|
|
* so the production default must be at least INFO — otherwise
|
|
* submission logs, admin action logs, and audit traces are silently
|
|
* discarded.
|
|
*/
|
|
private static function level(): Level
|
|
{
|
|
$level = strtoupper(getenv('LOG_LEVEL') ?: '');
|
|
|
|
// Default: INFO in production, DEBUG in dev (cli-server)
|
|
if ($level === '') {
|
|
return php_sapi_name() === 'cli-server' ? Level::Debug : Level::Info;
|
|
}
|
|
|
|
return Level::fromName($level);
|
|
}
|
|
|
|
/**
|
|
* Resolve the log directory.
|
|
*/
|
|
private static function logDir(): string
|
|
{
|
|
if (defined('STORAGE_ROOT')) {
|
|
return STORAGE_ROOT . '/logs';
|
|
}
|
|
return __DIR__ . '/../storage/logs';
|
|
}
|
|
}
|