日志

简介

为了帮助你更多的了解应用程序中到底发生了什么,Laravel 提供了强大的日志服务,允许你将日志消息、系统错误日志记录到文件,甚至使用 Slack 通知到你的整个团队。

在 Laravel 框架中,Laravel 使用 Monolog 库,它为各种强大的日志处理提供支持。Laravel 使配置这些处理程序变得简单,允许你混合并匹配它们自定义的应用程序日志处理。

配置

所有的应用程序日志系统配置都位于 config/logging.php 配置文件中。这个文件允许你配置你的应用程序日志通道,所以务必查看每个可用的通道及它们的选项。当然,我们将在下面回顾一些常用的选项。

默认情况下,Laravel 将使用 stack 去记录日志消息。stack 通道被用来将多个日志通道聚合到一个单一的通道中。关于堆栈的更多信息,查看 以下文档

配置通道名称

默认情况下,Monolog 使用与当前环境匹配的『通道名称』进行实例化,比如 production 或者 local。要改变这个值,需添加一个 name 选项到你的通道配置中:

  1. 'stack' => [
  2. 'driver' => 'stack',
  3. 'name' => 'channel-name',
  4. 'channels' => ['single', 'slack'],
  5. ],

可用的通道驱动

名称描述
stack一个便于创建『多通道』通道的包装器
single单个文件或者基于日志通道的路径 (StreamHandler)
daily一个每天轮换的基于 Monolog 驱动的 RotatingFileHandler
slack一个基于 Monolog 驱动的 SlackWebhookHandler
papertrail一个基于 Monolog 驱动的 SyslogUdpHandler
syslog一个基于 Monolog 驱动的 SyslogHandler
errorlog一个基于 Monolog 驱动的 ErrorLogHandler
monolog一个可以使用任何支持 Monolog 处理程序的 Monolog 工厂驱动程序
custom一个调用指定工厂创建通道的驱动程序

Tip:有关 monologcustom 驱动,查看 高级通道自定义

配置 Single 和 Daily 通道

singledaily 通道包含三个可选配置项:bubblepermissionlocking.

名称描述默认值
bubble消息处理后,指示消息是否推送到其他通道true
permission日志文件权限0644
locking写入之前尝试锁定日志文件false

配置 Papertrail 通道

papertrail 通道需要 url端口 配置选项。你可一从这里获取这些值 Papertrail

配置 Slack 通道

slack 通道需要 url 配置选项。这个 URL 应当与你为 Slack 团队配置的一个 incoming webhook 相匹配。

构建日志堆栈

前面说过, stack 驱动允许你在单一日志通道中整合多个通道。让我们通过一个产品级应用的配置实例来看看如果使用日志堆栈:

  1. 'channels' => [
  2. 'stack' => [
  3. 'driver' => 'stack',
  4. 'channels' => ['syslog', 'slack'],
  5. ],
  6. 'syslog' => [
  7. 'driver' => 'syslog',
  8. 'level' => 'debug',
  9. ],
  10. 'slack' => [
  11. 'driver' => 'slack',
  12. 'url' => env('LOG_SLACK_WEBHOOK_URL'),
  13. 'username' => 'Laravel Log',
  14. 'emoji' => ':boom:',
  15. 'level' => 'critical',
  16. ],
  17. ],

我们来分析这个配置。首先要注意的是 stack 通过借助它的 channels 选项聚合了另外两个通道: syslogslack 。因此,在记录日志消息时,这两个通道都有机会完成日志消息记录:

日志级别

请留意上面例子中 syslogslack 中存在的 level 配置项。这个选项决定了需要被该通道记录的日志的最低 「级别」。Monolog (一个功能强劲的 Laravel 日志服务)接受定义在 RFC 5424 specification 中的全部级别: emergencyalertcriticalerrorwarningnoticeinfodebug

假设我们使用 debug 方法记录日志消息:

  1. Log::debug('An informational message.');

根据我们的配置, syslog 通道将把该消息记录到系统日志;不过因为错误消息不是 critical 或更高级别,它将不会被发送到 Slack。如果我们记录一条 emergency 消息,它将被发送给系统日志和 Slack,因为 emergency 的级别高于两个通道的最低级别限制:

  1. Log::emergency('The system is down!');

写日志消息

可以使用 Log facade 将信息写入日志。如前所述,日志提供定义在 RFC 5424 specification 中的可用日志级别: emergencyalertcriticalerrorwarningnoticeinfodebug

  1. Log::emergency($message);
  2. Log::alert($message);
  3. Log::critical($message);
  4. Log::error($message);
  5. Log::warning($message);
  6. Log::notice($message);
  7. Log::info($message);
  8. Log::debug($message);

因此,你可以调用这些方法中的任一方法记录相应级别的日志。默认情况下,消息被写入到在 config/logging.php 配置文件中定义的默认日志通道:

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\User;
  4. use Illuminate\Support\Facades\Log;
  5. use App\Http\Controllers\Controller;
  6. class UserController extends Controller
  7. {
  8. /**
  9. * 显示给定用户的配置信息。
  10. *
  11. * @param int $id
  12. * @return Response
  13. */
  14. public function showProfile($id)
  15. {
  16. Log::info('Showing user profile for user: '.$id);
  17. return view('user.profile', ['user' => User::findOrFail($id)]);
  18. }
  19. }

上下文信息

可以将上下文数据数组传递给日志方法。这些信息将被格式化,并与日志消息一直显示:

  1. Log::info('User failed to login.', ['id' => $user->id]);

写入指定通道

有时候你可能希望将消息写入到应用默认通道之外的通道中。可以使用 Log facade 的 channel 方法获取定义在配置文件中的任一通道并将消息写入其中 :

  1. Log::channel('slack')->info('Something happened!');

如果想要创建一个由多通道构成的按需记录的堆栈,可以使用 stack 方法:

  1. Log::stack(['single', 'slack'])->info('Something happened!');

高度自定义 Monolog 通道

为通道自定义 Monolog

有时需要完全控制已存在通道的 Monolog: 比如,你可能想要为给定通道的日志处理配置自定义的 Monolog FormatterInterface 实现:

先在通道配置中定义一个 tap 数组。 tap 数组包含一个在通道创建后有机会用于自定义 Monolog 实例的类列表:

  1. 'single' => [
  2. 'driver' => 'single',
  3. 'tap' => [App\Logging\CustomizeFormatter::class],
  4. 'path' => storage_path('logs/laravel.log'),
  5. 'level' => 'debug',
  6. ],

一旦在通道中有了 tap 选项配置,就要准备用于自定义 Monolog 实例的类。这种类这需要一个方法: __invoke,它接受一个 Illuminate\Log\Logger 实例作为其参数。 Illuminate\Log\Logger 实例将所有方法调用代理到基础的 Monolog 实例:

  1. <?php
  2. namespace App\Logging;
  3. class CustomizeFormatter
  4. {
  5. /**
  6. * 自定义给定的日志实例。
  7. *
  8. * @param \Illuminate\Log\Logger $logger
  9. * @return void
  10. */
  11. public function __invoke($logger)
  12. {
  13. foreach ($logger->getHandlers() as $handler) {
  14. $handler->setFormatter(...);
  15. }
  16. }
  17. }

Tip:所有的 "tap" 类都是由 服务容器 解析的,因此任何依赖它们的构造器都会自动被注入。

创建 Monolog 处理器通道

Monolog 多种 可用处理器。在某些情况下,你会希望仅创建一个带有指定处理器的 Monolog 驱动的日志类型。这些通道可以使用 monolog 驱动创建。

在使用 monolog 驱动时, handler 配置项用于指定被实例化的处理器。如果该处理器的构造器需要参数,可以使用可选的 with 配置项来指定:

  1. 'logentries' => [
  2. 'driver' => 'monolog',
  3. 'handler' => Monolog\Handler\SyslogUdpHandler::class,
  4. 'with' => [
  5. 'host' => 'my.logentries.internal.datahubhost.company.com',
  6. 'port' => '10000',
  7. ],
  8. ],

Monolog 格式化

使用 monolog 驱动时,Monolog 的 LineFormatter 用于默认的格式化处理器。当然,你也可以使用 formatter and formatter_with 配置项自定义格式化处理器类型:

  1. 'browser' => [
  2. 'driver' => 'monolog',
  3. 'handler' => Monolog\Handler\BrowserConsoleHandler::class,
  4. 'formatter' => Monolog\Formatter\HtmlFormatter::class,
  5. 'formatter_with' => [
  6. 'dateFormat' => 'Y-m-d',
  7. ],
  8. ],

如果所用的 Monolog 处理器能够提供自带的格式代处理器,可以将 formatter 配置项指定为 default:

  1. 'newrelic' => [
  2. 'driver' => 'monolog',
  3. 'handler' => Monolog\Handler\NewRelicHandler::class,
  4. 'formatter' => 'default',
  5. ],

通过工厂创建渠道

如果你想定义一个完全自定义的通道,你可以完全控制 Monolog 的实例化和配置,你可以在 config/logging.php 配置文件中指定 custom 驱动程序类型。 你的配置应该包含一个 via 选项,指向将被调用以创建 Monolog 实例的工厂类:

  1. 'channels' => [
  2. 'custom' => [
  3. 'driver' => 'custom',
  4. 'via' => App\Logging\CreateCustomLogger::class,
  5. ],
  6. ],

一旦配置了 custom 通道,就可以定义创建 Monolog 实例的类。 这个类只需要一个方法: __invoke ,它就可以返回 Monolog 实例:

  1. <?php
  2. namespace App\Logging;
  3. use Monolog\Logger;
  4. class CreateCustomLogger
  5. {
  6. /**
  7. * 创建一个 Monolog 实例.
  8. *
  9. * @param array $config
  10. * @return \Monolog\Logger
  11. */
  12. public function __invoke(array $config)
  13. {
  14. return new Logger(...);
  15. }
  16. }

本文章首发在 LearnKu.com 网站上。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接 我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。