日志系统
最后更新: 2026-01-27 11:02:47日志系统
Unicode Framework 提供了灵活的日志系统,支持多通道、自定义格式化器和日志级别。
日志基础
基本使用
use Unicode\Framework\Log\LogManager;
use Unicode\Framework\Config\Config;
$config = Config::getInstance();
$logManager = new LogManager($config);
// 获取默认通道
$logger = $logManager->channel();
// 记录日志
$logger->info('User logged in', ['user_id' => 1]);
$logger->error('Database error', ['error' => $e->getMessage()]);
日志级别
$logger->debug('Debug message');
$logger->info('Info message');
$logger->notice('Notice message');
$logger->warning('Warning message');
$logger->error('Error message');
$logger->critical('Critical message');
$logger->alert('Alert message');
$logger->emergency('Emergency message');
日志通道
单文件通道
// 配置
return [
'log' => [
'default' => 'single',
'channels' => [
'single' => [
'driver' => 'file',
'path' => storage_path('logs/app.log'),
],
],
],
];
// 使用
$logger = $logManager->channel('single');
$logger->info('Message');
按日期分割通道
// 配置
return [
'log' => [
'default' => 'daily',
'channels' => [
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/app.log'),
'days' => 14, // 保留14天的日志
],
],
],
];
// 使用
$logger = $logManager->channel('daily');
$logger->info('Message');
多通道配置
// 配置
return [
'log' => [
'default' => 'single',
'channels' => [
'single' => [
'driver' => 'file',
'path' => storage_path('logs/app.log'),
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/app.log'),
],
'error' => [
'driver' => 'file',
'path' => storage_path('logs/error.log'),
'level' => 'error', // 只记录错误级别及以上的日志
],
],
],
];
// 使用不同通道
$appLogger = $logManager->channel('single');
$errorLogger = $logManager->channel('error');
日志级别
设置日志级别
// 配置中设置
return [
'log' => [
'channels' => [
'single' => [
'driver' => 'file',
'level' => 'info', // 只记录 info 及以上级别
],
],
],
];
日志级别说明
debug: 详细的调试信息info: 一般信息notice: 正常但重要的事件warning: 警告信息error: 错误信息critical: 严重错误alert: 需要立即处理emergency: 系统不可用
日志格式化
行格式(默认)
// 配置
return [
'log' => [
'channels' => [
'single' => [
'driver' => 'file',
'formatter' => 'line',
],
],
],
];
// 输出格式
// [2024-01-01 12:00:00] app.INFO: User logged in {"user_id":1}
JSON 格式
// 配置
return [
'log' => [
'channels' => [
'single' => [
'driver' => 'file',
'formatter' => 'json',
],
],
],
];
// 输出格式
// {"timestamp":"2024-01-01T12:00:00+00:00","level":"info","message":"User logged in","context":{"user_id":1}}
自定义格式化器
namespace App\Log\Formatters;
use Unicode\Framework\Interfaces\LogFormatterInterface;
use Unicode\Framework\Log\LogLevel;
class CustomFormatter implements LogFormatterInterface
{
public function format(string $level, string $message, array $context = []): string
{
$timestamp = date('Y-m-d H:i:s');
$contextStr = !empty($context) ? json_encode($context) : '';
return "[{$timestamp}] [{$level}] {$message} {$contextStr}\n";
}
}
// 使用
$logger->setFormatter(new CustomFormatter());
上下文信息
添加上下文
// 基本上下文
$logger->info('User logged in', [
'user_id' => 1,
'ip' => $request->ip(),
]);
// 复杂上下文
$logger->error('Payment failed', [
'user_id' => 1,
'order_id' => 123,
'amount' => 100.00,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
自动添加上下文
// 在中间件中自动添加请求信息
class LogContextMiddleware implements MiddlewareInterface
{
public function handle(Request $request, callable $next): Response
{
$logger = LogManager::getInstance()->channel();
$logger->setContext([
'request_id' => uniqid(),
'ip' => $request->ip(),
'user_agent' => $request->getHeader('User-Agent'),
]);
return $next($request);
}
}
最佳实践
1. 日志级别选择
// ✅ 推荐:根据重要性选择级别
$logger->debug('SQL query', ['sql' => $sql]); // 调试信息
$logger->info('User logged in', ['user_id' => 1]); // 一般信息
$logger->warning('Rate limit approaching', ['user_id' => 1]); // 警告
$logger->error('Database connection failed', ['error' => $e->getMessage()]); // 错误
$logger->critical('Payment system down', []); // 严重错误
// ❌ 错误:所有日志都使用 info 级别
$logger->info('Debug message');
$logger->info('Error message');
2. 结构化日志
// ✅ 推荐:使用结构化数据
$logger->info('Order created', [
'order_id' => $order->id,
'user_id' => $order->user_id,
'amount' => $order->amount,
'items' => $order->items->count(),
]);
// ❌ 错误:使用字符串拼接
$logger->info("Order created: ID={$order->id}, User={$order->user_id}");
3. 敏感信息处理
// ✅ 推荐:不记录敏感信息
$logger->info('User logged in', [
'user_id' => $user->id,
// 不记录密码、token 等敏感信息
]);
// ❌ 错误:记录敏感信息
$logger->info('User logged in', [
'password' => $password, // 安全风险
'token' => $token,
]);
4. 错误日志记录
// ✅ 推荐:记录完整的错误信息
try {
// 操作
} catch (\Exception $e) {
$logger->error('Operation failed', [
'error' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
}
5. 日志轮转
// ✅ 推荐:使用按日期分割的日志
return [
'log' => [
'channels' => [
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/app.log'),
'days' => 30, // 保留30天
],
],
],
];
6. 不同环境配置
// 开发环境:详细日志
if (getenv('APP_ENV') === 'development') {
$logger->setLevel('debug');
}
// 生产环境:只记录重要日志
if (getenv('APP_ENV') === 'production') {
$logger->setLevel('info');
}
常见问题
Q: 如何查看日志文件?
A: 日志文件默认存储在 storage/logs/ 目录下。
Q: 如何清理旧日志?
A: 使用按日期分割的日志通道,自动清理过期日志。
Q: 如何记录到多个通道?
A: 创建多个日志记录器实例:
$appLogger = $logManager->channel('single');
$errorLogger = $logManager->channel('error');
Q: 如何自定义日志格式?
A: 实现 LogFormatterInterface 接口,创建自定义格式化器。
Q: 日志会影响性能吗?
A: 文件日志对性能影响较小,但在高并发场景下建议使用异步日志。