Files
freeshop/includes/Logger.php
T
2026-08-14 10:55:06 +08:00

123 lines
3.5 KiB
PHP

<?php
/**
* 系统日志类
*
* 将日志写入 storage/logs/ 目录,按日期自动分文件。
* 支持级别:DEBUG / INFO / WARN / ERROR
*
* 用法:
* Logger::info('用户登录', ['user_id' => 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);
}
}
}