日志 简介 为了帮助你更多的了解应用程序中到底发生了什么,Laravel 提供了强大的日志服务,允许你将日志消息、系统错误日志记录到文件,甚至使用 Slack 通知到你的整个团队。 在 Laravel 框架中,Laravel 使用 Monolog 库,它为各种强大的日志处理提供支持。Laravel 使配置这些处理程序变得简单,允许你混合并匹配它们自定义的应用程序日志处理。 配置 所有的应用程序日志系统配置都位于 config/logging.php 配置文件中。这个文件允许你配置你的应用程序日志通道,所以务必查看每个可用的通道及它们的选项。当然,我们将在下面回顾一些常用的选项。 默认情况下,Laravel 将使用 stack 通道去记录日志消息。stack 通道被用来将多个日志通道聚合到一个单一的通道中。关于堆栈的更多信息,查看 以下文档。 配置通道名称 默认情况下,Monolog 使用与当前环境匹配的『通道名称』进行实例化,比如 production 或者 local。要改变这个值,需添加一个 name 选项到你的通道配置中: ``` 'stack' => [ 'driver' => 'stack', 'name' => 'channel-name', 'channels' => ['single', 'slack'], ], ``` 可用的通道驱动 |名称 |描述| |------|-----| |stack 一个便于创建『多通道』通道的包装器| |single 单个文件或者基于日志通道的路径 (StreamHandler)| |daily 一个每天轮换的基于 Monolog 驱动的 RotatingFileHandler| |slack 一个基于 Monolog 驱动的 SlackWebhookHandler| |papertrail 一个基于 Monolog 驱动的 SyslogUdpHandler| |syslog 一个基于 Monolog 驱动的 SyslogHandler| |errorlog 一个基于 Monolog 驱动的 ErrorLogHandler| |monolog 一个可以使用任何支持 Monolog 处理程序的 Monolog 工厂驱动程序| |custom 一个调用指定工厂创建通道的驱动程序| 技巧:有关 monolog 和 custom 驱动,查看 高级通道自定义。 配置 Single 和 Daily 通道 single 和 daily 通道包含三个可选配置项:bubble、permission 和 locking。 |名称 |描述| 默认值| |------|------|-----| |bubble |消息处理后,指示消息是否推送到其他通道 |true| |permission |日志文件权限 |0644| |locking |写入之前尝试锁定日志文件| false| 配置 Papertrail通道 papertrail 通道需要 url 和 port 配置选项。你可以从 Papertrail 获取这些值。 配置 Slack 通道 slack 通道需要 url 配置选项。这个 URL 应当与你为 Slack 团队配置的一个 incoming webhook 相匹配。默认情况下,Slack 只会接收 critical 和更高等级的日志,你可以在 logging 配置文件中对其进行修改。 构建日志堆栈 前面说过,stack 驱动允许你在单一日志通道中整合多个通道。让我们通过一个产品级应用的配置实例来看看如何使用日志堆栈: ``` 'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => ['syslog', 'slack'], ], 'syslog' => [ 'driver' => 'syslog', 'level' => 'debug', ], 'slack' => [ 'driver' => 'slack', 'url' => env('LOG_SLACK_WEBHOOK_URL'), 'username' => 'Laravel Log', 'emoji' => ':boom:', 'level' => 'critical', ], ], ``` 我们来分析这个配置。首先要注意的是 stack 通过借助它的 channels 选项聚合了另外两个通道:syslog 和 slack。因此,在记录日志消息时,这两个通道都有机会完成日志消息记录。 日志级别 请留意上面例子中 syslog 和 slack 中的 level 配置项。这个选项决定了需要被该通道记录的日志的最低 「级别」。Monolog (一个功能强劲的 Laravel 日志服务)接受定义在 RFC 5424 specification 中的全部级别: emergency、alert、critical、error、warning、notice、info 和 debug。 假设我们使用 debug 方法记录日志消息: ``` Log::debug('An informational message.'); ``` 根据我们的配置,syslog 通道将把该消息记录到系统日志;不过因为错误消息不是 critical 或更高级别,它将不会被发送到 Slack。如果我们记录一条 emergency 消息的话,由于 emergency 的级别高于两个通道的最低级别限制,它将被发送给系统日志和 Slack: ``` Log::emergency('The system is down!'); ``` 写日志消息 可以使用 Log facade 将信息写入日志。如前所述,日志提供定义在 RFC 5424 specification 中的可用日志级别: emergency、alert、critical、error、warning、notice、info 和 debug: ``` Log::emergency($message); Log::alert($message); Log::critical($message); Log::error($message); Log::warning($message); Log::notice($message); Log::info($message); Log::debug($message); ``` 因此,你可以调用这些方法中的任一方法记录相应级别的日志。默认情况下,消息被写入到在 config/logging.php 配置文件中定义的默认日志通道: ``` <?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Support\Facades\Log; class UserController extends Controller { /** * 显示给定用户的配置信息 * * @param int $id * @return Response */ public function showProfile($id) { Log::info('Showing user profile for user: '.$id); return view('user.profile', ['user' => User::findOrFail($id)]); } } ``` 上下文信息 可以将上下文数据数组传递给日志方法。这些信息将被格式化,并与日志消息一起显示: ``` Log::info('User failed to login.', ['id' => $user->id]); ``` 写入指定通道 有时候你可能希望将消息写入到应用默认通道之外的通道中。可以使用 Log facade 的 channel 方法获取定义在配置文件中的任一通道并将消息写入其中: ``` Log::channel('slack')->info('Something happened!'); ``` 如果想要创建一个由多通道构成的按需记录的堆栈,可以使用 stack 方法: ``` Log::stack(['single', 'slack'])->info('Something happened!'); ``` 高度自定义 Monolog 通道 为通道自定义 Monolog 有时需要完全控制已存在通道的 Monolog:比如,你可能想要为给定通道的日志处理配置自定义的 Monolog FormatterInterface 实现。 先在通道配置中定义一个 tap 数组。 tap 数组包含一个在通道创建后有机会用于自定义 Monolog 实例的类列表: ``` 'single' => [ 'driver' => 'single', 'tap' => [App\Logging\CustomizeFormatter::class], 'path' => storage_path('logs/laravel.log'), 'level' => 'debug', ], ``` 一旦在通道中有了 tap 选项配置,就要准备用于自定义 Monolog 实例的类。这种类这需要一个方法: __invoke,它接受一个 Illuminate\Log\Logger 实例作为其参数。 Illuminate\Log\Logger 实例将所有方法调用代理到基础的 Monolog 实例: ``` <?php namespace App\Logging; use Monolog\Formatter\LineFormatter; class CustomizeFormatter { /** * 自定义给定的日志实例 * * @param \Illuminate\Log\Logger $logger * @return void */ public function __invoke($logger) { foreach ($logger->getHandlers() as $handler) { $handler->setFormatter(new LineFormatter( '[%datetime%] %channel%.%level_name%: %message% %context% %extra%' )); } } } ``` 技巧:所有的 “tap” 类都是由 服务容器 解析的,因此任何依赖它们的构造器都会自动被注入。 创建 Monolog 处理器通道 Monolog 多种 可用处理器。在某些情况下,你会希望仅创建一个带有指定处理器的 Monolog 驱动的日志类型。这些通道可以使用 monolog 驱动创建。 在使用 monolog 驱动时, handler 配置项用于指定被实例化的处理器。如果该处理器的构造器需要参数,可以使用可选的 with 配置项来指定: ``` 'logentries' => [ 'driver' => 'monolog', 'handler' => Monolog\Handler\SyslogUdpHandler::class, 'with' => [ 'host' => 'my.logentries.internal.datahubhost.company.com', 'port' => '10000', ], ], ``` Monolog 格式化 使用 monolog 驱动时,Monolog 的 LineFormatter 用于默认的格式化处理器。当然,你也可以使用 formatter 和 formatter_with 配置项自定义格式化处理器类型: ``` 'browser' => [ 'driver' => 'monolog', 'handler' => Monolog\Handler\BrowserConsoleHandler::class, 'formatter' => Monolog\Formatter\HtmlFormatter::class, 'formatter_with' => [ 'dateFormat' => 'Y-m-d', ], ], ``` 如果所用的 Monolog 处理器能够提供自带的格式代处理器,可以将 formatter 配置项指定为 default: ``` 'newrelic' => [ 'driver' => 'monolog', 'handler' => Monolog\Handler\NewRelicHandler::class, 'formatter' => 'default', ], ``` 通过工厂创建渠道 如果你想定义一个完全自定义的通道,你可以完全控制 Monolog 的实例化和配置,你可以在 config/logging.php 配置文件中指定 custom 驱动程序类型。 你的配置应该包含一个 via 选项,指向将被调用以创建 Monolog 实例的工厂类: ``` 'channels' => [ 'custom' => [ 'driver' => 'custom', 'via' => App\Logging\CreateCustomLogger::class, ], ], ``` 一旦配置了 custom 通道,就可以定义创建 Monolog 实例的类。 这个类只需要一个方法: __invoke,它就可以返回 Monolog 实例: ``` <?php namespace App\Logging; use Monolog\Logger; class CreateCustomLogger { /** * 创建一个 Monolog 实例 * * @param array $config * @return \Monolog\Logger */ public function __invoke(array $config) { return new Logger(...); } } ```