Logging

Introduction

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

Laravel 日志记录基于“通道”。每个通道代表一种写入日志信息的特定方式。例如,single channel 将日志文件写入单个日志文件,而slack channel 将日志消息发送到 Slack。日志消息可能会根据其严重性写入多个通道。

在底层,Laravel 利用Monolog 库,它为各种强大的日志处理程序提供支持。 Laravel 使配置这些处理程序变得轻而易举,允许您混合和匹配它们以自定义应用程序的日志处理。

Configuration

应用程序日志记录行为的所有配置选项都位于config/logging.php 配置文件。该文件允许您配置应用程序的日志通道,因此请务必查看每个可用通道及其选项。我们将在下面回顾一些常见的选项。

默认情况下,Laravel 将使用stack 记录消息时的通道。这stack channel 用于将多个日志通道聚合为一个通道。有关构建堆栈的更多信息,请查看下面的文档.

配置通道名称

默认情况下,Monolog 被实例化为与当前环境相匹配的“通道名称”,例如production 或者local.要更改此值,请添加name 您频道配置的选项:

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

可用的通道驱动程序

每个日志通道都由一个“驱动程序”提供支持。驱动程序确定日志消息实际记录的方式和位置。以下日志通道驱动程序在每个 Laravel 应用程序中都可用。大多数这些驱动程序的条目已经存在于您的应用程序的config/logging.php 配置文件,因此请务必查看此文件以熟悉其内容:

Name Description
custom 调用指定工厂创建通道的驱动程序
daily ARotatingFileHandler 基于 Monolog 驱动程序,每天轮换
errorlog 一个ErrorLogHandler 基于 Monolog 的驱动程序
monolog 可以使用任何受支持的 Monolog 处理程序的 Monolog 工厂驱动程序
null 丢弃所有日志消息的驱动程序
papertrail ASyslogUdpHandler 基于 Monolog 的驱动程序
single 基于单个文件或路径的记录器通道(StreamHandler)
slack ASlackWebhookHandler 基于 Monolog 的驱动程序
stack 便于创建“多渠道”渠道的包装器
syslog ASyslogHandler 基于 Monolog 的驱动程序

Note
查看文档高级频道定制 了解更多关于monologcustom 司机。

频道先决条件

配置单一频道和每日频道

singledaily 通道具有三个可选的配置选项:bubble,permission, 和locking.

Name Description Default
bubble 指示消息在处理后是否应该冒泡到其他渠道 true
locking 在写入之前尝试锁定日志文件 false
permission 日志文件的权限 0644

此外,保留政策daily 通道可以通过配置days 选项:

Name Description Default
days 每日日志文件应保留的天数 7

配置 Papertrail 频道

papertrail 频道需要hostport 配置选项。您可以从获取这些值Papertrail.

配置松弛通道

slack 频道需要一个url 配置选项。此 URL 应与 URL 相匹配传入网络钩子 您为 Slack 团队配置的。

默认情况下,Slack 只会在critical 级别及以上;但是,您可以在您的config/logging.php 通过修改配置文件level Slack 日志通道的配置数组中的配置选项。

记录弃用警告

PHP、Laravel 和其他库经常通知他们的用户他们的一些功能已被弃用,并将在未来的版本中删除。如果您想记录这些弃用警告,您可以指定您的首选deprecations 应用程序中的日志通道config/logging.php 配置文件:

'deprecations' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),

'channels' => [
    ...
]

或者,您可以定义一个名为deprecations.如果存在具有此名称的日志通道,它将始终用于记录弃用:

'channels' => [
    'deprecations' => [
        'driver' => 'single',
        'path' => storage_path('logs/php-deprecation-warnings.log'),
    ],
],

构建日志栈

如前所述,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 通过它的聚合了另外两个 channelschannels 选项:syslogslack.因此,在记录消息时,这两个通道都有机会记录消息。然而,正如我们将在下面看到的,这些通道是否实际记录消息可能取决于消息的严重性/“级别”。

日志级别

请注意level 配置选项存在于syslogslack 上例中的通道配置。此选项确定消息必须达到的最低“级别”才能被通道记录。 Monolog 为 Laravel 的日志服务提供支持,它提供了定义在RFC 5424 规范.按照严重性的降序排列,这些日志级别是:emergency,alert,critical,error,warning,notice,info, 和debug.

所以,假设我们使用debug 方法:

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

鉴于我们的配置,syslog channel 会将消息写入系统日志;但是,由于错误消息不是critical 或以上,它不会被发送到 Slack。但是,如果我们记录一个emergency 消息,它将被发送到系统日志和 Slack,因为emergency 两个通道的电平均高于我们的最低电平阈值:

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

写入日志消息

您可以使用Log facade.如前所述,记录器提供了定义在RFC 5424 规范:emergency,alert,critical,error,warning,notice,infodebug:

use Illuminate\Support\Facades\Log;

Log::emergency($message);
Log::alert($message);
Log::critical($message);
Log::error($message);
Log::warning($message);
Log::notice($message);
Log::info($message);
Log::debug($message);

您可以调用这些方法中的任何一个来记录相应级别的消息。默认情况下,消息将写入您配置的默认日志通道logging 配置文件:

<?php

namespace App\Http\Controllers;

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

class UserController extends Controller
{
    /**
     * Show the profile for the given user.
     */
    public function show(string $id): View
    {
        Log::info('Showing the user profile for user: '.$id);

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

上下文信息

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

use Illuminate\Support\Facades\Log;

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

有时,您可能希望指定一些上下文信息,这些信息应该包含在特定频道中的所有后续日志条目中。例如,您可能希望记录与应用程序的每个传入请求关联的请求 ID。为此,您可以调用Log 门面的withContext 方法:

<?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
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $requestId = (string) Str::uuid();

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

        return $next($request)->header('Request-Id', $requestId);
    }
}

如果您想共享上下文信息all 记录通道,您可以调用Log::shareContext() 方法。此方法将为所有创建的频道和随后创建的任何频道提供上下文信息。通常,shareContext 方法应该从boot 应用服务提供者的方法:

use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class AppServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Log::shareContext([
            'invocation-id' => (string) Str::uuid(),
        ]);
    }
}

写入特定频道

有时您可能希望将消息记录到应用程序默认频道以外的频道。您可以使用channel 上的方法Log facade 检索并记录到配置文件中定义的任何通道:

use Illuminate\Support\Facades\Log;

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

如果您想创建一个由多个通道组成的按需日志堆栈,您可以使用stack 方法:

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

点播频道

也可以通过在运行时提供配置来创建点播频道,而该配置不存在于您的应用程序的logging 配置文件。为此,您可以将配置数组传递给Log 门面的build 方法:

use Illuminate\Support\Facades\Log;

Log::build([
  'driver' => 'single',
  'path' => storage_path('logs/custom.log'),
])->info('Something happened!');

您可能还希望在按需日志堆栈中包含一个按需频道。这可以通过在传递给stack 方法:

use Illuminate\Support\Facades\Log;

$channel = Log::build([
  'driver' => 'single',
  'path' => storage_path('logs/custom.log'),
]);

Log::stack(['slack', $channel])->info('Something happened!');

独白频道定制

为频道定制独白

有时您可能需要完全控制如何为现有频道配置 Monolog。例如,你可能想配置一个自定义的 MonologFormatterInterface Laravel 内置的实现single 渠道。

首先,定义一个tap 通道配置上的数组。这tap array 应该包含一个类列表,这些类应该有机会在创建后定制(或“点击”到)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 instance 代理对底层 Monolog 实例的所有方法调用:

<?php

namespace App\Logging;

use Illuminate\Log\Logger;
use Monolog\Formatter\LineFormatter;

class CustomizeFormatter
{
    /**
     * Customize the given logger instance.
     */
    public function __invoke(Logger $logger): void
    {
        foreach ($logger->getHandlers() as $handler) {
            $handler->setFormatter(new LineFormatter(
                '[%datetime%] %channel%.%level_name%: %message% %context% %extra%'
            ));
        }
    }
}

Note
您所有的“点击”类都由服务容器,因此它们需要的任何构造函数依赖项都将自动注入。

创建独白处理程序通道

Monolog有多种可用的处理程序 并且 Laravel 不包含每个通道的内置通道。在某些情况下,您可能希望创建一个自定义通道,它只是一个没有相应 Laravel 日志驱动程序的特定 Monolog 处理程序的实例。这些渠道可以很容易地使用创建monolog 司机。

当使用monolog 司机handler 配置选项用于指定将实例化哪个处理程序。可选地,可以使用with 配置选项:

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

独白格式化程序

当使用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 的实例化和配置,你可以指定一个custom 司机输入你的config/logging.php 配置文件。您的配置应包括via 包含将被调用以创建 Monolog 实例的工厂类名称的选项:

'channels' => [
    'example-custom-channel' => [
        'driver' => 'custom',
        'via' => App\Logging\CreateCustomLogger::class,
    ],
],

一旦你配置了custom 驱动程序通道,您已准备好定义将创建您的 Monolog 实例的类。这个类只需要一个__invoke 应该返回 Monolog 记录器实例的方法。该方法将接收通道配置数组作为其唯一参数:

<?php

namespace App\Logging;

use Monolog\Logger;

class CreateCustomLogger
{
    /**
     * Create a custom Monolog instance.
     */
    public function __invoke(array $config): Logger
    {
        return new Logger(/* ... */);
    }
}
豫ICP备18041297号-2