daily通道不满足模块化需求,因其将所有日志混写于同一日期文件,无法按模块隔离或差异化保留策略;需通过custom通道+rotatingfilehandler手动实现模块前缀与自动轮转。

为什么 daily 通道不满足模块化需求
Laravel 默认的 daily 日志通道确实按日期生成文件(如 laravel-2024-06-15.log),但它把所有日志——HTTP 请求、队列、命令、自定义业务逻辑——全塞进同一个文件里。你没法快速定位「支付模块昨天的退款失败日志」,也没法单独配置「订单服务只保留7天,而审计日志保留90天」。
根本问题在于:Laravel 的日志通道是「全局级别」的,不是「模块+时间」双维度的。要实现模块分割,必须绕过默认 daily 配置,手动构造带模块前缀的文件名,并确保轮转逻辑仍生效。
用 RotatingFileHandler 手动注册带模块前缀的通道
核心是不用 channels.daily 配置,改用 Log::build() + Monolog\Handler\RotatingFileHandler,在文件路径中嵌入模块标识(如 payment、inventory)。
- 在
config/logging.php的'channels'数组里新增一个通道,例如'payment_daily' - 类型设为
'custom',并在'via'指定一个闭包工厂函数 - 闭包中实例化
RotatingFileHandler,路径写成storage_path("logs/payment/laravel-".date('Y-m-d').".log")—— 注意:不能直接拼日期,否则每天只会写入一个固定文件;必须配合RotatingFileHandler的自动轮转机制 - 正确做法是把路径设为
storage_path("logs/payment/laravel.log"),然后靠RotatingFileHandler的$maxFiles和自动重命名能力实现按日切割
示例工厂函数:
'payment_daily' => [
'driver' => 'custom',
'via' => function () {
return Log::build([
'driver' => 'monolog',
'handler' => RotatingFileHandler::class,
'with' => [
'filename' => storage_path('logs/payment/laravel.log'),
'level' => 'debug',
'max_files' => 30, // 保留30天
'file_permission' => 0644,
],
]);
},
],
如何在代码里调用指定模块通道而不污染全局
别用 Log::info() 直接写,默认走的是 stack 或 single 通道。必须显式指定通道名,且推荐封装一层避免硬编码。
- 直接调用:
Log::channel('payment_daily')->info('Refund failed', ['order_id' => 123]); - 更安全的做法:在
app/Logging/Channels/PaymentLogger.php写个门面或辅助函数,内部固定调用Log::channel('payment_daily') - 切记不要在模型或控制器里反复写
Log::channel(...)—— 一旦通道名变更,满项目搜替换很痛苦 - 如果用了队列任务,注意日志通道配置是否在队列进程启动时已加载(常见坑:队列 worker 启动后修改了 config,但没重启 worker,新通道不可见)
权限、目录创建和 Laravel Octane 兼容性
RotatingFileHandler 不会自动创建父目录(比如 storage/logs/payment/),首次写日志会报 failed to open stream: No such file or directory 错误。
- 启动前手动运行:
mkdir -p storage/logs/payment storage/logs/inventory - 或在通道工厂函数里加
Filesystem::makeDirectory($path, 0755, true)(需注入Illuminate\Filesystem\Filesystem) - Laravel Octane 下,
RotatingFileHandler的文件锁行为可能异常,建议在octane配置中关闭日志共享:'swoole' => ['options' => ['log_file' => '/dev/null']],坚持用 Monolog 原生写入 - Windows 系统要注意路径分隔符,统一用
storage_path()而非字符串拼接storage/logs\...
模块前缀和日期切割本身不难,真正卡住人的往往是目录权限、Octane 生命周期、以及忘记给 max_files 设合理值导致磁盘爆满。先跑通一个通道,再批量复制,比一次性铺开所有模块更稳。











