介绍

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

Laravel 日志基于「 通道 」。 每个通道代表一种写入日志信息的特定方式。 例如,php single 通道是将日志写入到单个日志文件中。而 php slack 通道是将日志发送到 Slack 上。 基于它们的重要程度,日志可以被写入到多个通道中去。

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

配置

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

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

配置频道名称
默认情况下,Monolog 使用与当前环境相匹配的“频道名称”(例如 php productionphp local)进行实例化。要更改此值,请向频道的配置中添加一个 php name 选项:

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

可用频道驱动程序

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

名称 描述
php custom 调用指定工厂创建频道的驱动程序
php daily 基于 php RotatingFileHandler 的 Monolog 驱动程序,每天轮换一次日志文件
php errorlog 基于 php ErrorLogHandler 的 Monolog 驱动程序
php monolog 可使用任何支持的 Monolog 处理程序的 Monolog 工厂驱动程序
php null 丢弃所有日志消息的驱动程序
php papertrail 基于 php SyslogUdpHandler 的 Monolog 驱动程序
php single 单个文件或路径为基础的记录器频道(php StreamHandler
php slack 基于 php SlackWebhookHandler 的 Monolog 驱动程序
php stack 包装器,用于方便地创建“多通道”频道
php syslog 基于 php SyslogHandler 的 Monolog 驱动程序
注意
查看 高级频道自定义 文档,了解有关 php monologphp custom 驱动程序的更多信息。

频道前提条件

配置单一和日志频道
在处理消息时,php singlephp daily 频道有三个可选配置选项:php bubblephp permissionphp locking

名称 描述 默认值
php bubble 表示是否在处理后将消息传递到其他频道 php true
php locking 在写入日志文件之前尝试锁定日志文件 php false
php permission 日志文件的权限 php 0644
另外,可以通过 php days 选项配置 php daily 频道的保留策略:

名称 描述 默认值
php days 保留每日日志文件的天数 php 7
配置 Papertrail 频道
php papertrail 频道需要 php hostphp port 配置选项。您可以从 Papertrail获取这些值。

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

默认情况下,Slack仅会接收 php critical 级别及以上的日志;但是,您可以通过修改 php config/logging.php 配置文件中您的Slack日志频道配置数组中的 php level 配置选项来调整此设置。

记录弃用警告

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

  1. 'deprecations' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
  2. 'channels' => [
  3. ...
  4. ]

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

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

构建日志堆栈

如前所述,php ## 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. ],

让我们分解一下这个配置。首先,请注意我们的 php stack 通道通过其 php channels 选项聚合了两个其他通道:php syslogphp slack。因此,在记录消息时,这两个通道都有机会记录消息。但是,正如我们将在下面看到的那样,这些通道是否实际记录消息可能取决于消息的严重程度/“级别”。

[RFC 5424规范](#log-levels”>日志级别
请注意上面示例中 php syslogphp slack 通道配置中存在的 php level 配置选项。此选项确定必须记录消息的最小“级别”。Laravel的日志服务采用Monolog,提供emergency,alert,critical,error,warning,notice,info和debug。

在我们的配置中,如果我们使用 php debug 方法记录消息:

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

根据我们的配置,php syslog 渠道将把消息写入系统日志;但由于错误消息不是 php critical 或以上级别,它不会被发送到 Slack。然而,如果我们记录一个 php emergency 级别的消息,则会发送到系统日志和 Slack,因为 php emergency 级别高于我们两个渠道的最小级别阈值:

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

写入日志消息

您可以使用 php Log facade 向日志写入信息。正如之前提到的,日志记录器提供了 emergency、alert、critical、error、warning、notice、info 和 debug:

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

您可以调用其中任何一个方法来记录相应级别的消息。默认情况下,该消息将根据您的 php logging 配置文件配置的默认日志渠道进行写入:

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

上下文信息

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

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

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

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

如果要在所有日志频道之间共享上下文信息,则可以调用 php Log::shareContext() 方法。此方法将向所有已创建的频道提供上下文信息,以及随后创建的任何频道。通常,php shareContext 方法应从应用程序服务提供程序的 php boot 方法中调用:

  1. use Illuminate\Support\Facades\Log;
  2. use Illuminate\Support\Str;
  3. class AppServiceProvider
  4. {
  5. /**
  6. * 启动任何应用程序服务。
  7. */
  8. public function boot(): void
  9. {
  10. Log::shareContext([
  11. 'invocation-id' => (string) Str::uuid(),
  12. ]);
  13. }
  14. }

写入特定频道

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

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

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

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

按需通道
还可以创建一个按需通道,方法是在运行时提供配置而无需将该配置包含在应用程序的 php logging 配置文件中。为此,可以将配置数组传递给 php Log 门面的 php build 方法:

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

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

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

Monolog 通道定制

为通道定制 Monolog

有时,您可能需要完全控制 Monolog 如何配置现有通道。例如,您可能希望为 Laravel 内置的 php single 通道配置自定义的 Monolog php ### FormatterInterface 实现。

要开始,请在通道配置中定义 php tap 数组。php tap 数组应包含一系列类,这些类在创建 Monolog 实例后应有机会自定义(或“tap”)它。没有这些类应放置在何处的惯例位置,因此您可以在应用程序中创建一个目录以包含这些类:

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

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

  1. <?php
  2. namespace App\Logging;
  3. use Illuminate\Log\Logger;
  4. use Monolog\Formatter\LineFormatter;
  5. class CustomizeFormatter
  6. {
  7. /**
  8. * 自定义给定的日志记录器实例。
  9. */
  10. public function __invoke(Logger $logger): void
  11. {
  12. foreach ($logger->getHandlers() as $handler) {
  13. $handler->setFormatter(new LineFormatter(
  14. '[%datetime%] %channel%.%level_name%: %message% %context% %extra%'
  15. ));
  16. }
  17. }
  18. }

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

创建 Monolog 处理程序通道

Monolog 有多种 ### monolog ``` 驱动程序轻松创建。

使用 php monolog 驱动程序时,php handler 配置选项用于指定将实例化哪个处理程序。可选地,可以使用 php 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 格式化程序
使用 php monolog 驱动程序时,Monolog php LineFormatter 将用作默认格式化程序。但是,你可以使用 php formatterphp 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 处理程序,你可以将 php formatter 配置选项的值设置为 php default

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

[Monolog提供的现有处理器](#monolog-processors”>Monolog 处理器
Monolog 也可以在记录消息之前对其进行处理。你可以创建你自己的处理器或使用

如果你想为 php monolog 驱动定制处理器,请在通道的配置中加入php processors 配置值。

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

通过工厂创建通道

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

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

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

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