跳转到主要内容

什么是日志

Laravel 日志系统以**通道(Channel)**为核心概念。 通道指的是一组定义了日志写入目标与方式的配置单元,可以组合文件、Slack、syslog 等各种输出目的地。 底层使用 Monolog 库,可以充分利用其丰富的 handler 与 formatter。
默认使用 stack 通道。stack 是把多个通道组合在一起的父通道。

配置

日志配置集中在 config/logging.php 中。可通过环境变量 LOG_CHANNEL 切换默认通道。
// config/logging.php
'default' => env('LOG_CHANNEL', 'stack'),

可用的通道驱动

驱动说明
single把所有日志写入单一文件
daily按日期拆分文件写入(自动轮转)
slack通过 Slack Incoming Webhook 发送消息
stack组合多个通道的父通道
syslog写入系统 syslog
errorlog写入 PHP 错误日志
monolog直接指定 Monolog 的 handler
custom通过工厂类完全自定义通道

日志级别

Laravel 支持 RFC 5424 定义的 8 个日志级别,按严重性从高到低排列:
级别用途示例
emergency系统整体不可用,需立即处理
alert必须由人立即介入的状态(如数据库断连)
critical主要功能停摆的重大故障
error运行时错误,需要处理,但通常不必即时
warning潜在问题,如使用了已弃用 API、遇到意外数据
notice正常运行,但值得关注的信息
info用户登录、订单确认等常规操作日志
debug开发时的详细调试信息
通道的 level 设置表示最低日志级别。 例如 levelerror 时,只会记录 error 及以上(criticalalertemergency)。

基本用法

Log 门面

使用 Illuminate\Support\Facades\Log 门面按级别写入日志。
use Illuminate\Support\Facades\Log;

Log::emergency('系统已停机。');
Log::alert('数据库连接已断开。');
Log::critical('支付服务无响应。');
Log::error('更新用户数据失败。');
Log::warning('调用了已弃用的方法。');
Log::notice('配置文件已重载。');
Log::info('用户已登录。');
Log::debug('查询执行耗时:42ms');
在控制器中的示例:
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;

class UserController extends Controller
{
    public function show(string $id): View
    {
        Log::info('显示用户资料。', ['user_id' => $id]);

        return view('user.profile', [
            'user' => User::findOrFail($id),
        ]);
    }
}

log() 辅助函数

使用 log() 辅助函数可以无需 import Log 门面直接写。
log('来自 helper 的日志。');

// 也可以指定级别和上下文
log('已创建用户。', 'info', ['user_id' => $user->id]);

附加上下文信息

传入上下文(数组)可与消息一起记录相关信息。
Log::info('登录失败。', [
    'user_id' => $user->id,
    'ip'      => $request->ip(),
    'reason'  => '密码不匹配',
]);

withContext() — 为通道后续所有日志附加公共上下文

若希望对特定通道之后的所有日志附加公共信息,可使用 withContext()。 适合像请求 ID 这样希望出现在所有日志中的信息。
Log::withContext(['request-id' => (string) Str::uuid()]);

// 之后所有日志都会自动包含 request-id
Log::info('开始处理。');
Log::error('发生错误。');

shareContext() — 对所有通道附加公共上下文

withContext() 只对目标通道生效,而 shareContext() 会作用于所有通道。
Log::shareContext(['app-version' => config('app.version')]);

通道配置

stack 通道 — 同时写入多个通道

stack 通道让你一次调用同时写入多个通道。
// config/logging.php
'channels' => [
    'stack' => [
        'driver'   => 'stack',
        'channels' => ['daily', 'slack'],
    ],

    'daily' => [
        'driver' => 'daily',
        'path'   => storage_path('logs/laravel.log'),
        'level'  => env('LOG_LEVEL', 'debug'),
        'days'   => 14,
    ],

    'slack' => [
        'driver'   => 'slack',
        'url'      => env('LOG_SLACK_WEBHOOK_URL'),
        'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
        'emoji'    => env('LOG_SLACK_EMOJI', ':boom:'),
        'level'    => 'critical',
    ],
],
以上配置将 debug 以上的日志写入 daily(文件),只有 critical 以上的日志才会发送到 slack
在生产环境,建议将 slack 通道级别设为 errorcritical。 若将琐碎日志推送到 Slack,会产生大量通知,重要告警反而容易被淹没。

daily 通道 — 日志轮转

daily 通道按日期分文件保存,并自动清理旧文件。
'daily' => [
    'driver' => 'daily',
    'path'   => storage_path('logs/laravel.log'),
    'level'  => env('LOG_LEVEL', 'debug'),
    'days'   => env('LOG_DAILY_DAYS', 14), // 保留 14 天
],
days 越小旧日志越快被删除。 为在生产环境排查故障,请保留足够的保留期。

向 Slack 发送错误通知

获取 Slack 的 Incoming Webhook URL 并配置到 .env 中。
LOG_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz
// config/logging.php
'slack' => [
    'driver'   => 'slack',
    'url'      => env('LOG_SLACK_WEBHOOK_URL'),
    'username' => 'Laravel Error Bot',
    'emoji'    => ':fire:',
    'level'    => 'error',
],
stack 通道中加入 slack 并设置 LOG_CHANNEL=stack,发生错误时会自动通知。

写入特定通道

通过 Log::channel() 显式指定目标通道。
// 只写入 slack
Log::channel('slack')->error('支付服务无响应。');

// 同时写入多个通道
Log::stack(['daily', 'slack'])->critical('数据库连接失败。');

按需通道

使用 Log::build() 可以在不修改配置文件的情况下即时构建自定义通道。 适合测试或临时的输出目的。
use Illuminate\Support\Facades\Log;

$channel = Log::build([
    'driver' => 'single',
    'path'   => storage_path('logs/import-' . now()->format('Ymd') . '.log'),
]);

Log::stack([$channel])->info('开始导入 CSV。');

实用场景

通过中间件附加请求 ID

给所有日志附加同一个请求 ID,能大幅提升按请求追踪日志的能力。
1

创建中间件

php artisan make:middleware AssignRequestId
2

实现 handle() 方法

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;

class AssignRequestId
{
    public function handle(Request $request, Closure $next): Response
    {
        $requestId = (string) Str::uuid();

        Log::withContext(['request-id' => $requestId]);

        $response = $next($request);

        $response->headers->set('X-Request-Id', $requestId);

        return $response;
    }
}
3

注册中间件

bootstrap/app.php 中作为全局中间件注册。
->withMiddleware(function (Middleware $middleware) {
    $middleware->append(\App\Http\Middleware\AssignRequestId::class);
})
配置好后,所有日志都会自动附带 request-id
[2026-03-01 12:00:00] local.INFO: 用户已登录。 {"request-id":"550e8400-...","user_id":1}
[2026-03-01 12:00:00] local.INFO: 显示仪表盘。 {"request-id":"550e8400-..."}

记录弃用警告

可以将 PHP 或 Laravel 弃用功能的调用记录到日志中。
// config/logging.php
'deprecations' => [
    'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
    'trace'   => env('LOG_DEPRECATIONS_TRACE', false),
],
.env 中指定通道:
LOG_DEPRECATIONS_CHANNEL=daily

Laravel Pail — 实时日志监视

Laravel Pail 是可以在终端实时查看应用日志的开发工具。
运行 Pail 需要 PHP 的 PCNTL 扩展。

安装

composer require --dev laravel/pail

基本用法

# 流式查看日志
php artisan pail

# 详细展示(不省略)
php artisan pail -v

# 同时展示堆栈
php artisan pail -vv

日志过滤

# 按关键字过滤
php artisan pail --filter="QueryException"

# 只按消息过滤
php artisan pail --message="登录"

# 按级别过滤
php artisan pail --level=error

# 只显示特定用户的日志
php artisan pail --user=1

小结

级别使用场景
emergency系统整体停机等致命故障
alert必须由人立即介入的状态
critical主要功能停摆的重大故障
error意料之外的运行时错误,需要处理
warning使用已弃用功能、遇到异常数据等需要关注的状态
notice正常但值得记录的重要操作
info用户操作、业务事件的记录
debug开发期间的详细调试信息(生产环境通常不需要)
  • 开发环境:使用 singledaily 写文件
  • 生产环境:使用 stack 组合 daily(文件保存)和 slack(错误通知)
  • 特定处理的日志:使用 Log::build() 创建按需通道写入独立文件
  • 实时监视:使用 php artisan pail 从终端查看
  • debug 日志中可能包含敏感信息,生产环境建议使用 LOG_LEVEL=error 或更高。
  • 日志文件请定期轮转,避免占满磁盘(使用 daily 通道的 days 设置)。
  • 向 Slack 等外部服务发送通知时注意速率限制,只推送严重错误。
最后修改于 2026年7月13日