Laravel框架的日志功能并非从零开发,而是建立在成熟的Monolog库之上。框架通过Log门面提供统一的调用入口,把Emergency、Alert、Critical、Error、Warning、Notice、Info、Debug八个级别的日志写入到文件、数据库或者第三方通知服务中。这套日志系统结构清晰、扩展灵活,日常开发中用好了能大幅提升排错效率,应用上线后也是监控系统健康状态的眼睛。

认识Laravel日志系统的基本构成
Laravel日志系统的核心配置文件是config/logging.php,默认情况下返回一个包含default和channels两个键的数组。default键指定默认使用的日志通道名称,channels键则定义所有可用的通道配置。Laravel自带single、daily、slack、stderr、syslog、errorlog和stack等驱动,其中stack驱动可以把多个通道组合在一起使用。
举个例子,Laravel项目默认的logging.php配置中,default值通常是stack,而channels数组里定义了stack通道,它由多个独立的子日志通道组成。配置内容大致如下:
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['single'],
'ignore_exceptions' => false,
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
],
],
stack通道本身并不直接写日志,而是把日志委托给channels数组中列出的子通道处理。这种设计让开发者可以按环境灵活组合日志目的地,比如开发环境写入本地文件,生产环境同时写入文件并同步到Slack。理解这个委托机制,就掌握了Laravel日志配置的核心。
使用Log门面写入不同级别的日志
在Laravel中写入日志最常用的方式是调用Log门面的静态方法。方法的名称对应Monolog定义的八个日志级别,从最严重的emergency到最轻微的debug,方法签名完全一致,第一个参数是日志消息,第二个可选参数是上下文数组。下面是一段覆盖全部级别的示例代码:
use Illuminate\Support\Facades\Log;
Log::emergency('数据库连接失败,服务停止');
Log::alert('磁盘空间不足', ['free_space' => '120MB']);
Log::critical('订单支付回调验签失败', ['order_id' => $order->id]);
Log::error('接口调用异常', ['url' => $request->url()]);
Log::warning('缓存命中率偏低', ['rate' => 0.62]);
Log::notice('用户修改了邮箱', ['user_id' => 10086]);
Log::info('用户登录成功', ['user_id' => $user->id, 'ip' => $request->ip()]);
Log::debug('SQL语句执行', ['sql' => $sql, 'bindings' => $bindings]);
上下文数组是日志系统的一个亮点,它允许在日志消息之外附加任意结构化数据。写入后日志记录上会显示这些键值对,便于在日志工具中按字段过滤检索。值得注意的是,上下文中的内容会在日志中明文保存,操作密码、token等敏感信息绝不能放进上下文。
除了常规的八级别写入方法,Log门面还提供了write方法统一写入某级别日志,以及channel和stack两个选择器方法。当项目中配置了多个日志通道时,可以用channel('daily')指定写入daily通道,也可以先用Log::channel获取某个通道实例再调用对应级别方法。这样业务日志和错误日志可以分文件存放,互不干扰。
了解single、daily、stack等内置通道
single驱动把所有日志都写进同一个文件,路径由path配置项指定,默认是storage/logs/laravel.log。这种模式简单直观,适合日志量不大的应用,但日志文件会随着时间无限增长,单个文件过大会影响日志写入性能,也不利于按时间维度查找问题。
daily驱动则按天生成日志文件,文件名会在path的基础上追加日期后缀。days配置项控制日志文件的保留天数,超过指定天数的日志文件会被自动清理,这样既保证了日志的连续性,也避免了磁盘空间被无限消耗。生产环境推荐优先使用daily驱动:
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'info',
'days' => 14,
],
使用daily驱动时,日志文件的命名格式类似laravel-2025-03-20.log,每天零点的首条日志会触发新文件创建。days设为14表示只保留最近两周的日志,更早的文件会被自动删除。这里提醒一下,实际运行Laravel的用户必须对storage/logs目录拥有写入权限,否则日志写入会直接失败并抛出异常。
自定义日志通道与处理器扩展
内置配置不足以覆盖业务需求时,Laravel允许开发者自定义日志通道。一种常见做法是使用自定义驱动,在channels配置的driver项里设置为custom,并指定via选项指向一个实现接口的类。下面先看配置文件里的写法:
'channels' => [
'sql_log' => [
'driver' => 'custom',
'via' => App\Logging\CustomLogger::class,
],
],
然后再看一下CustomLogger这个类的具体实现:
namespace App\Logging;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
class CustomLogger
{
public function __invoke(array $config)
{
$logger = new Logger('sql');
$logger->pushHandler(new StreamHandler(
storage_path('logs/sql.log')
));
return $logger;
}
}
原理上,Laravel调用通道配置时会执行via指定的类的__invoke方法,方法接收整个通道的配置数组,返回一个Monolog的Logger实例。返回的Logger实例支持任意Monolog处理器和格式化器,这等于把Monolog的全部能力都开放给了开发者。比如接入Sentry、接邮件告警、写系统日志,都可以在这一层实现。
另外一个实用技巧是日志格式化。默认的LineFormatter输出格式是时间加消息加上下文,内容都在一行中。如果希望输出JSON格式便于日志采集系统解析,可以在自定义驱动中给处理器设置JsonFormatter,这样每条日志就是一行合法的JSON,接入ELK等日志分析平台时无需额外解析规则。
生产环境日志处理的几个实操建议
日志级别需要结合环境设置。开发环境默认debug级别可以输出最详细的记录,生产环境通常设置为info或error,避免日志量过大拖慢磁盘写入,同时防止敏感调试信息泄漏。配置层面可以通过环境变量LOG_LEVEL统一控制,也可以在config/logging.php里对每个通道单独设置。
关于敏感信息脱敏,Laravel框架提供了Log::withContext方法,它可以在一段代码执行期间把公共上下文附加到所有写入的日志记录中。比如在中间件里调用Log::withContext(['request_id' => $requestId]),后面所有日志都会自动带上request_id字段。如果某条日志包含密码、身份证号等敏感数据,可以借助Monolog的处理器在写入前过滤,也可以干脆在写入前手动从上下文数组中剔除敏感字段。
最后建议养成给日志补充上下文的习惯。记录错误时把异常堆栈传进上下文,记录接口日志时带上请求参数和响应耗时,排查问题时这些看似琐碎的信息价值远超一句简单的错误描述。配合Tail、Sentry等日志查看工具,规范化的日志记录能让线上问题定位从小时级缩短到分钟级。