在ThinkPHP生产环境中配置日志时,需要重点关注稳定性、性能和安全。以下是最佳实践配置方案:
config/log.php
)return [
'default' => env('log.channel', 'stack'),
'channels' => [
// 组合通道(核心配置)
'stack' => [
'type' => 'stack',
'channels' => ['daily', 'error_file', 'emergency'],
],
// 按日分割的常规日志
'daily' => [
'type' => 'file',
'path' => runtime_path('logs/daily'),
'level' => ['info', 'notice', 'warning'],
'max_files' => 30, // 保留30天
'file_size' => 10485760, // 10MB分割文件
'json' => false,
'format' => '[%s][%s] %s %s', // 简化格式
],
// 错误日志单独通道
'error_file' => [
'type' => 'file',
'path' => runtime_path('logs/error'),
'level' => ['error', 'critical'],
'max_files' => 90, // 保留90天
'apart_level' => true, // 每个级别单独文件
'file_size' => 20971520, // 20MB分割
'json' => true, // JSON格式便于分析
],
// 紧急事件单独处理
'emergency' => [
'type' => 'file',
'path' => runtime_path('logs/emergency'),
'level' => ['emergency'],
'max_files' => 365, // 保留1年
],
// 数据库日志(审计需要)
'audit' => [
'type' => 'database',
'table' => 'system_logs',
'connection' => 'log_db',
'level' => ['info', 'notice'],
'ignore_fields' => ['password', 'token'], // 忽略敏感字段
],
// 外部日志服务
'cloud' => [
'type' => 'custom',
'driver' => \app\log\driver\CloudLogger::class, // 自定义驱动
'level' => ['error', 'emergency'],
'endpoint' => 'https://log.example.com/api',
]
]
];
'stack' => [
'type' => 'stack',
'channels' => ['daily', 'error_file', 'emergency'],
]
'daily' => [
'file_size' => 10485760, // 10MB分割文件
'max_files' => 30, // 最多保留30个文件
],
'database' => [
'ignore_fields' => ['password', 'token'], // 过滤敏感数据
],
在自定义格式化类中添加过滤:
class SafeLogFormatter {
protected function format($msg, $type, $context) {
// 过滤敏感信息
$context = array_filter($context, function($key) {
return !in_array($key, ['password', 'credit_card']);
}, ARRAY_FILTER_USE_KEY);
return json_encode(['msg'=>$msg, 'ctx'=>$context]);
}
}
'cloud' => [
'type' => 'custom',
'driver' => \app\log\driver\CloudLogger::class,
]
自定义云日志驱动示例:
class CloudLogger {
public function save(array $log) {
try {
// 主服务写入
$this->sendToElasticSearch($log);
} catch (\Exception $e) {
// 失败时写入本地备份
Log::channel('local_fallback')->error($log);
}
}
}
在入口文件(public/index.php
)添加:
// 设置日志目录权限 (640)
umask(0027);
# 日志默认通道
LOG_CHANNEL=stack
# 错误通知阈值
LOG_ERROR_THRESHOLD=50
# 云日志配置
LOG_CLOUD_ENDPOINT=https://logs.prod.example.com
LOG_CLOUD_KEY=your_api_key_here
日志监控报警:
// 在全局异常处理中
report(function (\Throwable $e) {
Log::channel('error_file')->error($e->getMessage());
// 每小时错误超过阈值报警
if (Log::getErrorCount() > env('LOG_ERROR_THRESHOLD', 50)) {
Alert::sendToOpsTeam();
}
});
日志压缩归档:
# 每日凌晨压缩旧日志
0 2 * * * find /path/to/runtime/logs -name "*.log" -mtime +30 -exec gzip {} \;
访问控制(Nginx):
location ~ ^/runtime/logs/ {
deny all;
return 403;
}
性能关键位置禁用日志:
// 高并发接口中
public function paymentNotify()
{
// 临时关闭日志
Log::close();
// 业务逻辑...
// 恢复日志
Log::init();
}
双写策略:
'channels' => [
'local_and_cloud' => [
'type' => 'stack',
'channels' => ['daily', 'cloud'],
]
]
日志完整性校验:
class LogVerifier {
public static function verifyIntegrity($logFile) {
$hash = md5_file($logFile);
file_put_contents($logFile.'.md5', $hash);
}
}
关键操作审计跟踪:
Log::channel('audit')->info('用户权限变更', [
'operator' => Auth::id(),
'target_user' => $userId,
'changes' => $permissionChanges,
'ip' => request()->ip()
]);
创建校验路由:
Route::get('log-check', function() {
// 测试各级别日志
Log::debug('Debug test - should not appear in prod');
Log::info('Info message');
Log::error('Error test');
Log::emergency('Emergency test!');
// 检查日志目录结构
$tree = shell_exec('tree '.runtime_path('logs'));
return 'Log test completed. Directory structure:\n'
.$tree;
})->middleware('auth:admin');
生产环境日志配置的核心原则:
遵循这些原则可确保在生产环境中既保留足够的排查线索,又不会影响系统性能和安全性。