1. Основы
  2. Логирование

Присоединяйся к нашему Telegram сообществу @webblend!

Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.

Введение

Чтобы узнать больше о том, что происходит в вашем приложении, Laravel предоставляет мощные сервисы ведения журнала, которые позволяют вам записывать сообщения в файлы, системный журнал ошибок, а также отправлять их в Slack для уведомления всей вашей команды.

Ведение журнала Laravel основано на "каналах". Каждый канал представляет собой конкретный способ записи информации в журнал. Например, канал single записывает журналы в один файл журнала, в то время как канал slack отправляет сообщения журнала в Slack. Сообщения журнала могут быть записаны в несколько каналов в зависимости от их серьезности.

Под капотом Laravel использует библиотеку Monolog, которая обеспечивает поддержку различных мощных обработчиков журналов. Laravel делает настройку этих обработчиков легкой, позволяя вам комбинировать их для настройки ведения журнала вашего приложения.

Настройка

Все параметры конфигурации для ведения журнала поведения вашего приложения находятся в файле конфигурации config/logging.php. В этом файле вы можете настроить каналы ведения журнала вашего приложения, поэтому обязательно просмотрите каждый из доступных каналов и их параметры. Мы рассмотрим несколько общих параметров ниже.

По умолчанию Laravel будет использовать канал stack при регистрации сообщений. Канал stack используется для агрегации нескольких каналов ведения журнала в один канал. Дополнительные сведения о создании стеков см. в документации ниже.

Настройка имени канала

По умолчанию Monolog создается с "именем канала", соответствующим текущей среде, такой как production или local. Чтобы изменить это значение, добавьте опцию name в конфигурацию вашего канала:

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

Доступные драйверы каналов

Каждый канал регистрации работает на основе "драйвера". Драйвер определяет, как и где фактически записывается сообщение в журнале. В каждом приложении Laravel доступны следующие драйверы канала регистрации. Запись для большинства из этих драйверов уже присутствует в файле конфигурации вашего приложения config/logging.php, поэтому обязательно изучите этот файл, чтобы ознакомиться с его содержимым:

Имя Описание
custom Драйвер, который вызывает указанную фабрику для создания канала
daily Драйвер Monolog на основе RotatingFileHandler, который вращается ежедневно
errorlog Драйвер Monolog на основе ErrorLogHandler
monolog Фабричный драйвер Monolog, который может использовать любой поддерживаемый обработчик Monolog
papertrail Драйвер Monolog на основе SyslogUdpHandler
single Канал регистрации логов в один файл или на основе указанного пути (StreamHandler)
slack Драйвер Monolog на основе SlackWebhookHandler
stack Оболочка для упрощения создания "многоканальных" каналов
syslog Драйвер Monolog на основе SyslogHandler

Примечание Ознакомьтесь с документацией по расширенной настройке канала, чтобы узнать больше о драйверах monolog и custom.

Предварительные требования для канала

Настройка единичного и ежедневного каналов

У каналов single и daily есть три дополнительные настраиваемые опции: bubble, permission и locking.

Имя Описание По умолчанию
bubble Указывает, следует ли сообщениям всплывать в другие каналы после их обработки true
locking Попытка блокировки файла журнала перед записью в него false
permission Разрешения файла журнала 0644

Кроме того, политика удержания для канала daily может быть настроена с помощью опции days:

Имя Описание По умолчанию
days Количество дней, в течение которых должны сохраняться ежедневные файлы журнала 7

Настройка канала Papertrail

Канал papertrail требует наличие опций конфигурации host и port. Вы можете получить эти значения из Papertrail.

Настройка канала Slack

Канал 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 объединяет два других канала через свою опцию channels: syslog и slack. Таким образом, при ведении журнала сообщений оба эти канала получат возможность зарегистрировать сообщение. Однако, как мы увидим ниже, реально ли эти каналы будут регистрировать сообщение, может быть определено уровнем серьезности / "уровнем".

Уровни журнала

Обратите внимание на опцию конфигурации level, присутствующую в конфигурациях каналов syslog и slack в приведенном выше примере. Эта опция определяет минимальный "уровень", который должно иметь сообщение, чтобы быть зарегистрированным каналом. Monolog, который обеспечивает функции ведения журнала Laravel, предоставляет все уровни журнала, определенные в спецификации RFC 5424. В порядке убывания серьезности эти уровни журнала следующие: emergency, alert, critical, error, warning, notice, info, и debug.

Итак, представьте, что мы регистрируем сообщение с использованием метода debug:

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

Учитывая нашу конфигурацию, канал syslog будет записывать сообщение в системный журнал; однако, поскольку сообщение об ошибке не является critical или выше, оно не будет отправлено в Slack. Однако, если мы зарегистрируем сообщение emergency, оно будет отправлено как в системный журнал, так и в Slack, поскольку уровень emergency выше нашего минимального порога уровня для обоих каналов:

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

Запись сообщений журнала

Вы можете записывать информацию в журналы, используя фасад Log. Как было упомянуто ранее, регистратор предоставляет восемь уровней ведения журнала, определенных в спецификации RFC 5424: emergency, alert, critical, error, warning, notice, info и debug:

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
{
/**
* Показать профиль для указанного пользователя.
*/
public function show(string $id): View
{
Log::info('Showing the user profile for user: {id}', ['id' => $id]);
 
return view('user.profile', [
'user' => User::findOrFail($id)
]);
}
}

Контекстная информация

Массив контекстных данных может быть передан в методы ведения журнала. Эти контекстные данные будут отформатированы и отображены с сообщением журнала:

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

Иногда вам может потребоваться указать некоторую контекстную информацию, которая должна включаться во все последующие записи журнала в определенном канале. Например, вы можете желать зарегистрировать идентификатор запроса, который связан с каждым входящим запросом к вашему приложению. Для этого вы можете вызвать метод withContext фасада Log:

<?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
{
/**
* Обработать входящий запрос.
*
* @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
]);
 
$response = $next($request);
 
$response->headers->set('Request-Id', $requestId);
 
return $response;
}
}

Если вы хотите передать контекстную информацию по всем каналам ведения журнала, вы можете вызвать метод Log::shareContext(). Этот метод предоставит контекстную информацию всем созданным каналам и любым каналам, которые будут созданы впоследствии. Обычно метод shareContext следует вызывать из метода boot сервис-провайдера приложения:

use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
 
class AppServiceProvider
{
/**
* Инициализировать любые службы приложения.
*/
public function boot(): void
{
Log::shareContext([
'invocation-id' => (string) Str::uuid(),
]);
}
}

Запись в конкретные каналы

Иногда вам может потребоваться записать сообщение в канал, отличный от канала по умолчанию вашего приложения. Вы можете использовать метод channel фасада Log для получения и регистрации в любом канале, определенном в вашем файле конфигурации:

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

Если вы хотите создать стек ведения журнала по требованию, состоящий из нескольких каналов, вы можете использовать метод stack:

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

Каналы по требованию

Также можно создать канал по требованию, предоставив конфигурацию во время выполнения без ее наличия в конфигурационном файле logging вашего приложения. Для этого вы можете передать массив конфигурации методу build фасада Log:

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

Настройка Monolog для каналов

Иногда вам может потребоваться полный контроль над тем, как Monolog настроен для существующего канала. Например, вы можете хотеть настроить реализацию пользовательского интерфейса FormatterInterface Monolog для встроенного канала single Laravel.

Для начала определите массив tap в конфигурации вашего канала. Массив tap должен содержать список классов, которым следует предоставить возможность настроить (или "подключиться") к экземпляру 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 передает все вызовы методов базовому экземпляру Monolog:

<?php
 
namespace App\Logging;
 
use Illuminate\Log\Logger;
use Monolog\Formatter\LineFormatter;
 
class CustomizeFormatter
{
/**
* Настроить предоставленный экземпляр журналирования.
*/
public function __invoke(Logger $logger): void
{
foreach ($logger->getHandlers() as $handler) {
$handler->setFormatter(new LineFormatter(
'[%datetime%] %channel%.%level_name%: %message% %context% %extra%'
));
}
}
}

Примечание Все ваши классы "tap" разрешаются контейнером служб, поэтому любые зависимости их конструктора будут автоматически внедрены.

Создание обработчиков каналов Monolog

У Monolog есть разнообразие доступных обработчиков, и Laravel не включает в себя встроенный канал для каждого из них. В некоторых случаях вы можете захотеть создать пользовательский канал, который представляет собой всего лишь экземпляр конкретного обработчика Monolog, не имеющего соответствующего драйвера регистрации Laravel. Эти каналы могут быть легко созданы с использованием драйвера monolog.

При использовании драйвера monolog опция handler используется для указания, какой обработчик будет создан. При необходимости можно указать любые параметры конструктора, которые нужны обработчику, с использованием опции конфигурации with:

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

Форматеры Monolog

При использовании драйвера monolog в качестве форматера по умолчанию будет использоваться форматер Monolog LineFormatter. Однако можно настроить тип форматера, передаваемого обработчику, с использованием опций конфигурации formatter и formatter_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

Monolog также может обрабатывать сообщения перед их регистрацией. Вы можете создавать собственные процессоры или использовать существующие процессоры, предлагаемые Monolog.

Если вы хотите настроить процессоры для драйвера monolog, добавьте значение конфигурации processors в конфигурацию вашего канала:

'memory' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\StreamHandler::class,
'with' => [
'stream' => 'php://stderr',
],
'processors' => [
// Простой синтаксис...
Monolog\Processor\MemoryUsageProcessor::class,
 
// С параметрами...
[
'processor' => Monolog\Processor\PsrLogMessageProcessor::class,
'with' => ['removeUsedContextFields' => true],
],
],
],

Создание пользовательских каналов через фабрики

Если вы хотите определить полностью настраиваемый канал, в котором у вас есть полный контроль над созданием и настройкой 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
{
/**
* Создать пользовательский экземпляр Monolog.
*/
public function __invoke(array $config): Logger
{
return new Logger(/* ... */);
}
}

Следование за сообщениями журнала с использованием Pail

Часто вам может потребоваться отслеживать логи вашего приложения в реальном времени. Например, при отладке проблемы или мониторинге логов вашего приложения для определенных типов ошибок.

Laravel Pail - это пакет, который позволяет легко просматривать лог-файлы вашего приложения Laravel напрямую из командной строки. В отличие от стандартной команды tail, Pail предназначен для работы с любым драйвером регистрации, включая Sentry или Flare. Кроме того, Pail предоставляет набор полезных фильтров, чтобы помочь вам быстро найти то, что вам нужно.

Установка

Внимание Для Laravel Pail требуется PHP 8.2+ и расширение PCNTL.

Для начала установите Pail в свой проект с использованием менеджера пакетов Composer:

composer require laravel/pail

Использование

Для начала отслеживания логов выполните команду pail:

php artisan pail

Для увеличения подробности вывода и избегания усечения (…), используйте опцию -v:

php artisan pail -v

Для максимальной подробности и отображения трассировок стека исключений используйте опцию -vv:

php artisan pail -vv

Для прекращения отслеживания логов в любое время нажмите Ctrl+C.

Фильтрация журнала

--filter

Вы можете использовать опцию --filter для фильтрации логов по их типу, файлу, сообщению и содержимому трассировки стека:

php artisan pail --filter="QueryException"

--message

Для фильтрации логов только по их сообщению вы можете использовать опцию --message:

php artisan pail --message="User created"

--level

Опция --level позволяет фильтровать логи по уровню:

php artisan pail --level=error

--user

Чтобы отобразить только логи, записанные во время аутентификации определенного пользователя, вы можете предоставить идентификатор пользователя опции --user:

php artisan pail --user=1