123]); * Logger::error('支付失败', ['order_id' => 456, 'err' => $e->getMessage()]); */ if (!defined('IN_APP')) exit('Forbidden'); class Logger { /** @var string 日志根目录 */ private static $dir; /** @var bool 是否启用(受 DEBUG 配置控制) */ private static $enabled = true; /** @var int 最小记录级别 (DEBUG=0, INFO=1, WARN=2, ERROR=3) */ private static $minLevel = 0; // DEBUG 模式下记录全部 const DEBUG = 0; const INFO = 1; const WARN = 2; const ERROR = 3; /** * 初始化日志系统。 * @param string $dir 日志目录路径 * @param bool $enabled 是否启用 * @param int $minLevel 最小级别 */ public static function init($dir, $enabled = true, $minLevel = 0) { self::$dir = rtrim($dir, '/\\'); self::$enabled = $enabled; self::$minLevel = $minLevel; if (!is_dir(self::$dir)) { @mkdir(self::$dir, 0755, true); } } /** * 记录 DEBUG 级别日志。 */ public static function debug($message, array $context = []) { self::write(self::DEBUG, $message, $context); } /** * 记录 INFO 级别日志。 */ public static function info($message, array $context = []) { self::write(self::INFO, $message, $context); } /** * 记录 WARN 级别日志。 */ public static function warn($message, array $context = []) { self::write(self::WARN, $message, $context); } /** * 记录 ERROR 级别日志。 */ public static function error($message, array $context = []) { self::write(self::ERROR, $message, $context); } // ─── 内部实现 ─── private static function write($level, $message, array $context) { if (!self::$enabled || $level < self::$minLevel) return; $levelNames = [self::DEBUG => 'DEBUG', self::INFO => 'INFO', self::WARN => 'WARN', self::ERROR => 'ERROR']; $date = date('Y-m-d'); $time = date('Y-m-d H:i:s'); $levelName = $levelNames[$level] ?? 'LOG'; // 格式化上下文 $ctxStr = ''; if (!empty($context)) { $parts = []; foreach ($context as $k => $v) { if (is_array($v) || is_object($v)) { $v = json_encode($v, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } $parts[] = $k . '=' . $v; } $ctxStr = ' [' . implode(', ', $parts) . ']'; } // 获取调用者信息(跳过 Logger 内部调用) $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); $caller = ''; if (isset($trace[2])) { $file = basename($trace[2]['file'] ?? ''); $line = $trace[2]['line'] ?? ''; $caller = " [{$file}:{$line}]"; } $logLine = "[{$time}] [{$levelName}]{$caller} {$message}{$ctxStr}" . PHP_EOL; $file = self::$dir . DIRECTORY_SEPARATOR . $date . '.log'; @file_put_contents($file, $logLine, FILE_APPEND | LOCK_EX); // 单文件最大 10MB,超出轮转 if (file_exists($file) && filesize($file) > 10 * 1024 * 1024) { $rotated = self::$dir . DIRECTORY_SEPARATOR . $date . '.log.1'; @rename($file, $rotated); } } }