Поддержите проект сделав пожертвование.
Будьте в курсе последних новостей!
Будьте в курсе последних новостей!

Логирование

Введение

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

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

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

Конфигурирование

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

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

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

Каждый канал журнала работает через «драйвер». Драйвер определяет, как и где фактически записывается сообщение журнала. Следующие драйверы канала журнала доступны в каждом приложении 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.

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

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

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

Предварительная подготовка канала

Конфигурирование каналов Single и Daily

Каналы (Channels) single и daily имеют три необязательных параметра конфигурации: bubble, permission, и locking.

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

Дополнительно, способ хранения для канала daily можно настроить с помощью переменной среды LOG_DAILY_DAYS или путем установки параметра конфигурации days.

Name Description Default
days Количество дней, в течение которых следует хранить файлы daily channel. 7

Конфигурирование канала Papertrail

Для канала papertrail требуются параметры конфигурации host и port. Их можно определить с помощью переменных среды PAPERTRAIL_URL и PAPERTRAIL_PORT. Эти значения можно получить из Papertrail.

Конфигурирование канала Slack

Для канала slack требуется параметр конфигурации url. Это значение может быть определено через переменную среды LOG_SLACK_WEBHOOK_URL. Этот URL-адрес должен соответствовать URL-адресу входящего веб-хука, который вы настроили для своей команды Slack.

По умолчанию Slack будет получать логи только с уровнем critical и выше; однако вы можете настроить это, используя переменную среды LOG_LEVEL или изменив параметр конфигурации level в массиве вашего драйвера Slack.

Логирование предупреждений об устаревании

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

'deprecations' => [
    'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
    'trace' => env('LOG_DEPRECATIONS_TRACE', false),
],

'channels' => [
    // ...
]

Или вы можете определить канал журнала с именем deprecations. Если канал журнала с таким именем существует, он всегда будет использоваться для регистрации устаревания:

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

Построение стека журналов

Как упоминалось ранее, драйвер stack позволяет для удобства объединить несколько каналов в один канал журнала. Чтобы проиллюстрировать, как использовать стеки журналов, давайте рассмотрим пример конфигурации, которую вы можете увидеть в эксплуатационном приложении:

'channels' => [
    'stack' => [
        'driver' => 'stack',
        'channels' => ['syslog', 'slack'], // [tl! add]
        'ignore_exceptions' => false,
    ],

    'syslog' => [
        'driver' => 'syslog',
        'level' => env('LOG_LEVEL', 'debug'),
        'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
        'replace_placeholders' => true,
    ],

    'slack' => [
        'driver' => 'slack',
        'url' => env('LOG_SLACK_WEBHOOK_URL'),
        'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
        'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
        'level' => env('LOG_LEVEL', 'critical'),
        'replace_placeholders' => true,
    ],
],

Давайте разберем эту конфигурацию. Во-первых, обратите внимание, что наш канал stack объединяет два других канала с помощью параметра channels: syslog и slack. Таким образом, при регистрации сообщений оба канала будут иметь возможность регистрировать сообщение. Однако, как мы увидим ниже, действительно ли эти каналы регистрируют сообщение, может быть определено серьезностью / «уровнем» сообщения.

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

Обратите внимание на параметр конфигурации level, присутствующий в конфигурациях каналов syslog и slack в приведенном выше примере. Эта опция определяет минимальный «уровень» сообщения, которое должно быть зарегистрировано каналом. Monolog, на котором работают службы ведения журналов Laravel, предлагает все уровни журналов, определенные в спецификации RFC 5424 specification. Эти уровни журнала в порядке убывания критичности: 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 specification: 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
{
    /**
     * Показать профиль конкретного пользователя.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    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  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     * @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(). Этот метод предоставит дополнительную информацию всем созданным каналам и всем каналам, которые будут созданы впоследствии.

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

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::shareContext([
            'request-id' => $requestId
        ]);

        // ...
    }
}

Если вам нужно передавать контекст журнала при обработке задач в очереди, вы можете использовать middleware заданий.

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

По желанию можно записать сообщение в канал, отличный от канала по умолчанию вашего приложения. Вы можете использовать метод 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 для существующего канала. Например, бывает необходимо настроить собственную реализацию Monolog FormatterInterface для встроенного в Laravel канала single.

Для начала определите массив tap в конфигурации канала. Массив tap должен содержать список классов, которые должны иметь возможность настраивать (или «касаться») экземпляр Monolog после его создания. Не существует обычного места для размещения этих классов, поэтому вы можете создать каталог в своем приложении, чтобы разместить эти классы:

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

После того как вы настроили опцию tap своего канала, вы готовы определить класс, который будет контролировать ваш экземпляр Monolog. Этому классу нужен только один метод: __invoke, который получает экземпляр Illuminate\Log\Logger. Экземпляр Illuminate\Log\Logger передает все вызовы методов базовому экземпляру Monolog:

<?php

namespace App\Logging;

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

class CustomizeFormatter
{
    /**
     * Настроить переданный экземпляр регистратора.
     *
     * @param  \Illuminate\Log\Logger  $logger
     * @return void
     */
    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 Процессоры (Processors)

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

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

 'memory' => [
     'driver' => 'monolog',
     'handler' => Monolog\Handler\StreamHandler::class,
     'with' => [
         'stream' => 'php://stderr',
     ],
     'processors' => [
         // Simple syntax...
         Monolog\Processor\MemoryUsageProcessor::class,

         // With options...
         [
            '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.
     *
     * @param  array  $config
     * @return \Monolog\Logger
     */
    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