Логування

Вступ

Щоб допомогти вам дізнатися більше про те, що відбувається у вашому застосунку, 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 Обгортка для полегшення створення каналів типу "multi-channel".
syslog Драйвер Monolog на основі SyslogHandler.

Перегляньте документацію на розширене налаштування каналів, щоб дізнатися більше про драйвери monolog та custom.

Налаштування Імені Каналу

За замовчуванням Monolog створюється з "назвою каналу", яка відповідає поточному середовищу, такому як production або local. Щоб змінити це значення, ви можете додати опцію name до конфігурації вашого каналу:

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

Попередні налаштування каналу

Налаштування каналів Single та Daily

Канали single та daily мають три необов'язкові параметри конфігурації: bubble, permission та locking.

Назва Опис За замовчуванням
bubble Вказує, чи повинні повідомлення передаватися далі до інших каналів після обробки. true
locking Спроба заблокувати файл журналу перед записом у нього. false
permission Права доступу до файлу журналу. 0644

Крім того, політика збереження для каналу daily може бути налаштована через змінну середовища LOG_DAILY_DAYS або шляхом встановлення параметра конфігурації days.

Назва Опис За замовчуванням
days Кількість днів, протягом яких слід зберігати щоденні файли журналу. 14

Налаштування каналу 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'], 
        '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. У порядку зменшення серйозності, ці рівні логування є: emergency, alert, critical, error, warning, notice, info та debug.

Отже, уявімо, що ми реєструємо повідомлення за допомогою методу debug:

Log::debug('Інформаційне повідомлення.');

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

Log::emergency('Все пропало!');

Запис повідомлень в журнал 

Ви можете записувати інформацію в логи, використовуючи 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\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(). Цей метод надасть контекстну інформацію всім створеним каналам і будь-яким каналам, які будуть створені згодом:

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

Якщо вам потрібно поділитися контекстом журналу під час обробки завдань у черзі, ви можете скористатися job 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
{
/**
* Налаштувати вказаний екземпляр журналу.
*/
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 використовується для вказівки, який обробник буде створено. За бажанням, будь-які параметри конструктора, які потрібні обробнику, можуть бути вказані за допомогою параметра конфігурації handler_with:

'logentries' => [
    'driver'  => 'monolog',
    'handler' => Monolog\Handler\SyslogUdpHandler::class,
    'handler_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,
'handler_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 require --dev 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

Щоб відобразити лише ті журнали, які були записані, коли певний користувач був автентифікований, ви можете вказати ID користувача в параметрі --user:

php artisan pail --user=1