跳至内容

日志记录

介绍

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

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

Laravel 底层使用了Monolog库,该库支持多种强大的日志处理程序。Laravel 让配置这些处理程序变得轻而易举,您可以Mix搭配使用它们来定制应用程序的日志处理方式。

配置

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

默认情况下,Laravel 会stack在记录消息时使用通道。该stack通道用于将多个日志通道聚合为单个通道。有关构建堆栈的更多信息,请参阅以下文档

可用的通道驱动程序

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

姓名 描述
custom 调用指定Factories来创建通道的驱动程序。
daily 基于 Monolog 的驱动程序RotatingFileHandler,每日轮换。
errorlog 基于 Monolog 的驱动程序ErrorLogHandler
monolog 可以使用任何受支持的 Monolog 处理程序的 Monolog Factories驱动程序。
papertrail 基于 Monolog 的驱动程序SyslogUdpHandler
single 基于单个文件或路径的记录器通道(StreamHandler)。
slack 基于 Monolog 的驱动程序SlackWebhookHandler
stack 便于创建“多通道”通道的包装器。
syslog 基于 Monolog 的驱动程序SyslogHandler

查看有关高级频道定制的文档以了解有关monologcustom驱动程序的更多信息。

配置频道名称

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

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

渠道先决条件

配置单一和每日频道

single通道daily有三个可选配置选项:bubblepermissionlocking

姓名 描述 默认
bubble 指示消息处理后是否应该冒泡到其他渠道。 true
locking 尝试在写入日志文件之前锁定它。 false
permission 日志文件的权限。 0644

daily此外,可以通过LOG_DAILY_DAYS环境变量或设置配置选项来配置通道的保留策略days

姓名 描述 默认
days 每日日志文件应保留的天数。 14

配置Papertrail通道

papertrail通道需要host和配置选项。这些可以通过和环境变量port定义。您可以从Papertrail获取这些值。PAPERTRAIL_URLPAPERTRAIL_PORT

配置 Slack 通道

slack频道需要一个url配置选项。此值可以通过环境变量定义。此 URL 应与您为 Slack 团队配置的传入 WebhookLOG_SLACK_WEBHOOK_URL的 URL 匹配。

默认情况下,Slack 只会接收级别critical及以上的日志;但是,您可以使用LOG_LEVEL环境变量或通过修改levelSlack 日志通道的配置数组中的配置选项来调整这一点。

记录弃用警告

PHP、Laravel 和其他库经常会通知用户某些功能已被弃用,并将在未来的版本中移除。如果你想记录这些弃用警告,你可以使用环境变量或在应用程序的配置文件中指定你首选的deprecations日志通道LOG_DEPRECATIONS_CHANNELconfig/logging.php

1'deprecations' => [
2 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
3 'trace' => env('LOG_DEPRECATIONS_TRACE', false),
4],
5 
6'channels' => [
7 // ...
8]

或者,你可以定义一个名为 的日志通道deprecations。如果存在同名的日志通道,它将始终用于记录弃用内容:

1'channels' => [
2 'deprecations' => [
3 'driver' => 'single',
4 'path' => storage_path('logs/php-deprecation-warnings.log'),
5 ],
6],

建造木栈

如前所述,stack为了方便起见,驱动程序允许您将多个通道组合成一个日志通道。为了说明如何使用日志堆栈,我们来看一个在生产应用程序中可能会看到的示例配置:

1'channels' => [
2 'stack' => [
3 'driver' => 'stack',
4 'channels' => ['syslog', 'slack'],
5 'ignore_exceptions' => false,
6 ],
7 
8 'syslog' => [
9 'driver' => 'syslog',
10 'level' => env('LOG_LEVEL', 'debug'),
11 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
12 'replace_placeholders' => true,
13 ],
14 
15 'slack' => [
16 'driver' => 'slack',
17 'url' => env('LOG_SLACK_WEBHOOK_URL'),
18 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
19 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
20 'level' => env('LOG_LEVEL', 'critical'),
21 'replace_placeholders' => true,
22 ],
23],

让我们剖析一下这个配置。首先,请注意,我们的stack通道通过其channels选项syslog和聚合了另外两个通道slack。因此,在记录消息时,这两个通道都有机会记录该消息。但是,正如我们将在下面看到的,这些通道是否真正记录该消息可能取决于消息的严重性/“级别”。

日志级别

请注意上面示例levelsyslogslack频道配置中的配置选项。此选项决定了消息必须达到的最低“级别”才能被频道记录。Monolog 为 Laravel 的日志服务提供支持,它提供了RFC 5424 规范中定义的所有日志级别。这些日志级别按严重程度降序排列,分别为:emergencyalertcriticalerrorwarningnoticeinfodebug

因此,假设我们使用该debug方法记录一条消息:

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

根据我们的配置,该syslog通道会将消息写入系统日志;但是,由于错误消息级别不高于critical或低于此级别,因此不会发送到 Slack。但是,如果我们记录一条emergency消息,它将同时发送到系统日志和 Slack,因为emergency其级别高于两个通道的最低级别阈值:

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

写入日志消息

Log 您可以使用Facade将信息写入日志。如前所述,记录器提供了RFC 5424 规范中定义的八个日志级别:emergencyalertcriticalerrorwarningnoticeinfodebug

1use Illuminate\Support\Facades\Log;
2 
3Log::emergency($message);
4Log::alert($message);
5Log::critical($message);
6Log::error($message);
7Log::warning($message);
8Log::notice($message);
9Log::info($message);
10Log::debug($message);

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

1<?php
2 
3namespace App\Http\Controllers;
4 
5use App\Models\User;
6use Illuminate\Support\Facades\Log;
7use Illuminate\View\View;
8 
9class UserController extends Controller
10{
11 /**
12 * Show the profile for the given user.
13 */
14 public function show(string $id): View
15 {
16 Log::info('Showing the user profile for user: {id}', ['id' => $id]);
17 
18 return view('user.profile', [
19 'user' => User::findOrFail($id)
20 ]);
21 }
22}

上下文信息

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

1use Illuminate\Support\Facades\Log;
2 
3Log::info('User {id} failed to login.', ['id' => $user->id]);

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

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Illuminate\Support\Facades\Log;
8use Illuminate\Support\Str;
9use Symfony\Component\HttpFoundation\Response;
10 
11class AssignRequestId
12{
13 /**
14 * Handle an incoming request.
15 *
16 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
17 */
18 public function handle(Request $request, Closure $next): Response
19 {
20 $requestId = (string) Str::uuid();
21 
22 Log::withContext([
23 'request-id' => $requestId
24 ]);
25 
26 $response = $next($request);
27 
28 $response->headers->set('Request-Id', $requestId);
29 
30 return $response;
31 }
32}

如果您想在所有日志通道之间共享上下文信息,可以调用该Log::shareContext()方法。此方法将向所有已创建的通道以及后续创建的通道提供上下文信息:

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Illuminate\Support\Facades\Log;
8use Illuminate\Support\Str;
9use Symfony\Component\HttpFoundation\Response;
10 
11class AssignRequestId
12{
13 /**
14 * Handle an incoming request.
15 *
16 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
17 */
18 public function handle(Request $request, Closure $next): Response
19 {
20 $requestId = (string) Str::uuid();
21 
22 Log::shareContext([
23 'request-id' => $requestId
24 ]);
25 
26 // ...
27 }
28}

如果您需要在处理排队作业时共享日志上下文,则可以使用作业中间件

写入特定通道

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

1use Illuminate\Support\Facades\Log;
2 
3Log::channel('slack')->info('Something happened!');

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

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

点播频道

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

1use Illuminate\Support\Facades\Log;
2 
3Log::build([
4 'driver' => 'single',
5 'path' => storage_path('logs/custom.log'),
6])->info('Something happened!');

您可能还希望在按需日志堆栈中包含一个按需通道。这可以通过将按需通道实例包含在传递给该stack方法的数组中来实现:

1use Illuminate\Support\Facades\Log;
2 
3$channel = Log::build([
4 'driver' => 'single',
5 'path' => storage_path('logs/custom.log'),
6]);
7 
8Log::stack(['slack', $channel])->info('Something happened!');

Monolog 频道定制

为频道定制 Monolog

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

首先,tap在频道配置中定义一个数组。该tap数组应包含一系列类,这些类应该有机会在 Monolog 实例创建后进行自定义(或“利用”)。这些类没有固定的放置位置,因此您可以自由地在应用程序中创建一个目录来包含这些类:

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

在频道上配置好tap选项后,就可以定义自定义 Monolog 实例的类了。这个类只需要一个方法:__invoke,它接收一个Illuminate\Log\Logger实例。该Illuminate\Log\Logger实例将所有方法调用代理到底层 Monolog 实例:

1<?php
2 
3namespace App\Logging;
4 
5use Illuminate\Log\Logger;
6use Monolog\Formatter\LineFormatter;
7 
8class CustomizeFormatter
9{
10 /**
11 * Customize the given logger instance.
12 */
13 public function __invoke(Logger $logger): void
14 {
15 foreach ($logger->getHandlers() as $handler) {
16 $handler->setFormatter(new LineFormatter(
17 '[%datetime%] %channel%.%level_name%: %message% %context% %extra%'
18 ));
19 }
20 }
21}

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

创建 Monolog 处理程序通道

Monolog 提供多种可用的处理器,而 Laravel 并未为每种处理器都提供内置通道。在某些情况下,你可能希望创建一个自定义通道,该通道仅仅是特定 Monolog 处理器的实例,而没有对应的 Laravel 日志驱动程序。这些通道可以通过monolog驱动程序轻松创建。

使用monolog驱动程序时,handler配置选项用于指定要实例化哪个处理程序。此外,处理程序所需的任何构造函数参数也可以使用handler_with配置选项指定:

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

Monolog 格式化程序

使用monolog驱动程序时,Monolog将用作默认格式化程序。但是,您可以使用和配置选项LineFormatter自定义传递给处理程序的格式化程序类型formatterformatter_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 处理器

Monolog 还可以在记录消息之前对其进行处理。您可以创建自己的处理器,也可以使用Monolog 提供的现有处理器

如果您想要为monolog驱动程序自定义处理器,请processors向您的通道配置添加配置值:

1'memory' => [
2 'driver' => 'monolog',
3 'handler' => Monolog\Handler\StreamHandler::class,
4 'handler_with' => [
5 'stream' => 'php://stderr',
6 ],
7 'processors' => [
8 // Simple syntax...
9 Monolog\Processor\MemoryUsageProcessor::class,
10 
11 // With options...
12 [
13 'processor' => Monolog\Processor\PsrLogMessageProcessor::class,
14 'with' => ['removeUsedContextFields' => true],
15 ],
16 ],
17],

通过Factories创建自定义渠道

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

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

配置好custom驱动通道后,就可以定义创建 Monolog 实例的类了。该类只需要一个__invoke方法,该方法返回 Monolog 记录器实例。该方法将接收通道配置数组作为其唯一参数:

1<?php
2 
3namespace App\Logging;
4 
5use Monolog\Logger;
6 
7class CreateCustomLogger
8{
9 /**
10 * Create a custom Monolog instance.
11 */
12 public function __invoke(array $config): Logger
13 {
14 return new Logger(/* ... */);
15 }
16}

使用 Pail 跟踪日志消息

你经常需要实时查看应用程序的日志。例如,在调试问题或监控应用程序日志中特定类型的错误时。

Laravel Pail 是一个软件包,可让您直接从命令行轻松查看 Laravel 应用程序的日志文件。与标准tail命令不同,Pail 可与任何日志驱动程序(包括 Sentry 或 Flare)配合使用。此外,Pail 还提供了一组实用的过滤器,可帮助您快速找到所需内容。

安装

Laravel Pail 需要PHP 8.2+PCNTL扩展。

首先,使用 Composer 包管理器将 Pail 安装到您的项目中:

1composer require laravel/pail

用法

要开始跟踪日志,请运行以下pail命令:

1php artisan pail

为了增加输出的详细程度并避免截断(...),请使用以下-v选项:

1php artisan pail -v

为了最大程度地显示异常堆栈跟踪,请使用以下-vv选项:

1php artisan pail -vv

要停止拖尾日志,请Ctrl+C随时按。

过滤日志

--filter

您可以使用该--filter选项按日志类型、文件、消息和堆栈跟踪内容过滤日志:

1php artisan pail --filter="QueryException"

--message

要仅根据消息过滤日志,您可以使用以下--message选项:

1php artisan pail --message="User created"

--level

--level选项可用于按日志级别过滤日志:

1php artisan pail --level=error

--user

要仅显示在特定用户经过身份验证时写入的日志,您可以向该选项提供用户的 ID --user

1php artisan pail --user=1