Laravel 异常处理与日志

在 Web 应用开发中,错误和异常是不可避免的。Laravel 提供了强大而灵活的异常处理和日志系统,帮助你在开发阶段快速定位问题,在生产环境中优雅地记录错误并通知开发者。本章将详细介绍如何配置异常处理器、自定义错误页面、写入日志以及利用多频道日志进行监控。

🛡️ 核心理念: 异常应被“抛出”或“捕获”,日志是记录应用行为的“黑匣子”。合理结合两者,能大幅提升应用的稳定性和可维护性。

1. 异常处理机制

Laravel 的所有异常都集中在 App\Exceptions\Handler 类中处理。这个类负责报告异常(记录日志)和渲染异常(转换为 HTTP 响应)。你可以根据异常类型自定义行为。

报告异常

report 方法中,你可以决定哪些异常需要记录,或向外部服务发送告警。


// 在 App\Exceptions\Handler 中
public function report(Throwable $e)
{
    if ($e instanceof CustomException) {
        // 仅记录部分信息
        Log::channel('slack')->warning('自定义异常发生:'.$e->getMessage());
        return;
    }

    parent::report($e);
}
                    

渲染异常

通过 render 方法,你可以自定义异常对应的 HTTP 响应。


public function render($request, Throwable $e)
{
    if ($e instanceof CustomException) {
        return response()->view('errors.custom', [], 500);
    }

    return parent::render($request, $e);
}
                    

2. 日志系统配置

Laravel 使用 Monolog 作为日志库。配置文件位于 config/logging.php。你可以定义多个“频道”,每个频道可以有不同的驱动和级别。

日志级别说明
emergency系统不可用
alert需要立即采取行动
critical严重错误
error运行时错误
warning警告信息
notice普通但重要的事件
info一般信息
debug调试信息

.env 配置示例:


LOG_CHANNEL=stack
LOG_STACK=single,slack
LOG_LEVEL=debug
                    

3. 写入日志

使用 Log Facade 可以轻松写入日志。你可以指定级别:


use Illuminate\Support\Facades\Log;

Log::emergency('系统崩溃');
Log::alert('数据库连接失败');
Log::critical('磁盘空间不足');
Log::error('用户认证失败', ['user_id' => 123]);
Log::warning('API 响应缓慢', ['time' => 5.2]);
Log::notice('任务已开始');
Log::info('用户登录成功', ['user' => 'admin']);
Log::debug('SQL 查询', ['sql' => 'select * from users']);
                    

上下文信息

第二个参数可以传入关联数组,这些数据会被格式化为 JSON 附加到日志中。

指定频道


Log::channel('slack')->info('系统通知', ['event' => 'deploy']);
                    

4. 自定义频道与日志栈

你可以在 config/logging.php 中定义多个频道,并组合成“栈”(stack),同时写入多个位置。


'channels' => [
    'single' => [
        'driver' => 'single',
        'path' => storage_path('logs/laravel.log'),
        'level' => env('LOG_LEVEL', 'debug'),
    ],
    'slack' => [
        'driver' => 'slack',
        'url' => env('LOG_SLACK_WEBHOOK_URL'),
        'username' => 'Laravel Log',
        'level' => 'error',
    ],
    'stack' => [
        'driver' => 'stack',
        'channels' => ['single', 'slack'],
    ],
],
                    

5. 自定义错误页面

当发生 HTTP 异常(如 404、403、500)时,Laravel 会自动渲染对应的视图。你可以创建自定义视图文件:

  • resources/views/errors/404.blade.php – 404 页面
  • resources/views/errors/403.blade.php – 禁止访问
  • resources/views/errors/500.blade.php – 服务器错误
  • resources/views/errors/419.blade.php – CSRF 过期

示例 404 页面:


@extends('layouts.app')

@section('content')
    <h1>404 - 页面未找到</h1>
    <p>您访问的页面不存在。</p>
@endsection
                    

6. 手动抛出异常

在业务代码中,你可以手动抛出异常,Laravel 会自动处理并记录。


use App\Exceptions\CustomException;

if (!$user) {
    throw new CustomException('用户不存在');
}

// 抛出 HTTP 异常
abort(403, '无权访问');
                    

7. 忽略特定异常的报告

有些异常(如模型未找到)你不想记录到日志,可以在 Handler 的 $dontReport 属性中列出。


protected $dontReport = [
    \Illuminate\Database\Eloquent\ModelNotFoundException::class,
    \Symfony\Component\HttpKernel\Exception\HttpException::class,
];
                    

8. 最佳实践与常见陷阱

  • 使用合适的日志级别: 不要将所有信息都设为 error,合理区分 debug/info/error 便于监控。
  • 生产环境关闭调试模式: 设置 APP_DEBUG=false,避免暴露敏感信息。
  • 日志轮转: 使用 daily 驱动,避免单个日志文件过大。
  • 异常上报: 集成 Sentry、Bugsnag 等工具,自动捕获生产异常。
  • 不要在异常处理中再次抛出异常: 避免死循环或丢失原始错误。
  • 记录必要上下文: 在日志中包含用户 ID、请求 URL、IP 等信息,便于排查。

集成 Sentry 示例


// 安装 sentry/sentry-laravel
// 在 Handler 的 report 方法中
public function report(Throwable $e)
{
    if (app()->bound('sentry') && $this->shouldReport($e)) {
        app('sentry')->captureException($e);
    }
    parent::report($e);
}
                    

9. Artisan 命令与队列中的异常

Artisan 命令失败时会输出错误信息到控制台;队列任务失败时,会记录到 failed_jobs 表。你可以自定义失败处理。


// 在任务类中定义 failed 方法
public function failed(Throwable $exception)
{
    Log::error('队列任务失败', [
        'job' => static::class,
        'error' => $exception->getMessage(),
    ]);
}
                    

10. 总结

Laravel 的异常处理和日志系统为你提供了完善的应用监控能力。通过自定义异常处理器、多频道日志和灵活的错误页面,你可以确保应用在出错时依然优雅,并能够快速定位问题根源。合理利用日志级别和上报工具,将大幅提升生产环境的可维护性。

📖 官方文档: Laravel ErrorsLogging 提供了更深入的配置和高级用法。