日志

简介

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

在内部,Laravel 利用 Monolog 库为多种强大的日志处理器提供支持。Laravel 使配置这些日志处理器变得很简单,允许您混合或者搭配它们来自定义应用的日志处理。

配置

应用的日志系统的所有配置都位于 config/logging.php 配置文件中。该文件允许您配置应用的日志频道,因此确保查看了每个可用的频道及其选项。当然,我们会在下面介绍一些常见选项。

默认情况下,Laravel 会在记录信息时使用 stack 频道。stack 频道用于将多个日志频道聚合到一个单独频道中。有关构建日志栈的更多信息,请查看 下方文档

配置频道名称

默认情况下,Monolog 使用与当前环境匹配的「频道名称」进行实例化,例如 productionlocal。要更改此值,可以在频道配置中添加一个 name 选项:

'stack' => [
    'driver' => 'stack',
    'name' => 'channel-name',
    'channels' => ['single', 'slack'],
],

可用的频道驱动

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

查看有关 advanced channel customization 文档,来了解有关 monologcustom 驱动的更多信息。

配置 Slack 频道

slack 频道需要一个 url 配置选项。该 URL 应与您为 Slack 团队配置的 incoming webhook 的 URL 相匹配。

构建日志栈

如前所述,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 频道通过 'channel' => ['syslog', 'slack'] 选项聚合了其它两个频道。因此,在记录信息时,这两个频道都有机会记录信息。

日志级别

请注意上述示例的配置中 syslogslack 频道的 level 配置选项。该选项决定了要记录到频道的最低日志「级别」。驱动 Laravel 的日志服务的 Monolog,提供 RFC 5424 规范 中定义的所有日志级别:emergencyalertcriticalerrorwarningnoticeinfo 以及 debug

因此,假设我们使用 debug 方法记录日志信息:

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

根据我们的配置,syslog 分配会将信息写入到系统日志;但是,由于错误信息不是 critical 或以上,因此它不会被发送给 Slack。然而,如果我们记录一个 emergency 日志,它将被同时发送给系统日志和 Slack,因为 emergency 级别已经超过了两个频道的等级阈值:

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

写入日志信息

可以使用 Log facade 来写入日志信息。如前面所提到的,日志记录器提供了 RFC 5424 specification 中定义的八种日志级别:emergencyalertcriticalerrorwarningnoticeinfo 以及 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 配置文件中的配置的默认日志频道:

namespace App\Http\Controllers;

use App\User;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controller;

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 实例的类了。Illuminate\Log\Logger 实例代理了所有调用底层的 Monolog 实例的方法:

namespace App\Logging;

class CustomizeFormatter
{
    /**
     * 定制给定的日志记录器实例
     *
     * @param  \Illuminate\Log\Logger  $logger
     * @return void
     */
    public function __invoke($logger)
    {
        foreach ($logger->getHandlers() as $handler) {
            $handler->setFormatter(...);
        }
    }
}

所有「tap」类都会由 服务容器 解析,因此构造器中任何所需的依赖都会被自动注入。

创建 Monolog 处理器频道

Monolog 有各种各样的 可用的处理器。在某些情况下,希望创建的日志记录器类型仅仅是具有特定处理器实例的 Monolog 驱动。可以使用 monolog 驱动来创建这些频道。

当使用 monolog 驱动时,handler 配置项用于指定要被实例化的处理器。作为可选项,可以使用 handler_with 配置项指定处理器所需的任何构造参数:

'logentries' => [
    'driver'  => 'monolog',
    'handler' => Monolog\Handler\SyslogUdpHandler::class,
    'handler_with' => [
        'host' => 'my.logentries.internal.datahubhost.company.com',
        'port' => '10000',
    ],
],

Monolog 格式处理器

使用 monolog 驱动时,Monolog 的 LineFormatter 将被用作默认的格式处理器。但是,可以使用 formatterformatter_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 实例:

namespace App\Logging;

use Monolog\Logger;

class CreateCustomLogger
{
    /**
     * 创建一个自定义 Monolog 实例
     *
     * @param  array  $config
     * @return \Monolog\Logger
     */
    public function __invoke(array $config)
    {
        return new Logger(...);
    }
}