Логування · Laravel
Щоб ви могли більше дізнатися про те, що відбувається всередині вашого застосунку, Laravel надає надійні сервіси логування, які дозволяють писати повідомлення у файли, у системний журнал помилок і навіть у Slack, щоб повідомити всю команду.
Логування в Laravel побудоване на «каналах». Кожен канал представляє окремий спосіб запису інформації журналу. Наприклад, канал single пише записи в один файл журналу, а канал slack надсилає повідомлення в Slack. Залежно від серйозності повідомлення можуть писатися в кілька каналів одночасно.
Під капотом Laravel використовує бібліотеку Monolog, яка підтримує широкий набір потужних обробників (handlers) журналу. Laravel дозволяє налаштувати ці обробники без зусиль, поєднуючи їх так, як потрібно саме вашому застосунку.
Конфігурація
Усі опції конфігурації, які керують поведінкою логування у вашому застосунку, зібрані у файлі config/logging.php. Цей файл дозволяє налаштувати канали журналу застосунку, тож обовʼязково перегляньте кожен доступний канал і його опції. Кілька поширених опцій розглянемо нижче.
Типово Laravel використовує для запису повідомлень канал stack. Канал stack обʼєднує кілька каналів журналу в один. Докладніше про побудову стеків читайте в документації нижче.
Доступні драйвери каналів
Кожен канал журналу працює на «драйвері». Драйвер визначає, як і куди повідомлення насправді записується. Наведені нижче драйвери каналів доступні в кожному застосунку Laravel. Запис для більшості з них уже присутній у файлі конфігурації config/logging.php, тож перегляньте цей файл, щоб ознайомитися з його вмістом:
| Назва | Опис |
|---|---|
custom |
Драйвер, який викликає вказану фабрику для створення каналу. |
daily |
Драйвер Monolog на основі RotatingFileHandler із щоденною ротацією. |
monthly |
Драйвер 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 і Monthly
Канали single, daily і monthly мають три необовʼязкові опції конфігурації: bubble, permission і locking.
| Назва | Опис | Типове значення |
|---|---|---|
bubble |
Чи повинні повідомлення після обробки передаватися далі в інші канали. | true |
locking |
Спробувати заблокувати файл журналу перед записом у нього. | false |
permission |
Права доступу до файлу журналу. | 0644 |
Крім того, політику зберігання для каналів daily і monthly можна налаштувати опцією max_files. Для каналу daily термін зберігання також задається змінною середовища LOG_DAILY_DAYS.
Налаштування каналу Papertrail
Канал papertrail вимагає опцій host і port. Їх можна визначити через змінні середовища PAPERTRAIL_URL і PAPERTRAIL_PORT. Ці значення ви отримаєте в Papertrail.
Налаштування каналу Slack
Канал slack вимагає опції url. Це значення можна визначити через змінну середовища LOG_SLACK_WEBHOOK_URL. Цей URL має відповідати адресі incoming webhook, який ви налаштували для своєї команди в 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('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\Models\User;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*/
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
{
/**
* 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::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
{
/**
* 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
]);
// ...
}
}
Якщо потрібно поділитися контекстом журналу під час обробки завдань (jobs) із черги, скористайтеся 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 налаштований для наявного каналу. Наприклад, ви можете захотіти передати власну реалізацію FormatterInterface вбудованому каналу 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
{
/**
* Customize the given logger instance.
*/
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 типовим форматувальником буде LineFormatter із Monolog. Проте тип форматувальника, переданого обробнику, можна змінити опціями 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
{
/**
* Create a custom Monolog instance.
*/
public function __invoke(array $config): Logger
{
return new Logger(/* ... */);
}
}
Читання журналу в реальному часі за допомогою Pail
Часто потрібно спостерігати за журналом застосунку в реальному часі. Наприклад, коли ви шукаєте причину проблеми або відстежуєте певні типи помилок.
Laravel Pail - це пакет, який дозволяє легко зануритися у файли журналу застосунку Laravel просто з командного рядка. На відміну від стандартної команди tail, Pail працює з будь-яким драйвером журналу, включно з Laravel Nightwatch, Sentry чи Flare. Крім того, Pail надає набір корисних фільтрів, які допоможуть швидко знайти потрібне.

Встановлення
Laravel Pail вимагає розширення PHP PCNTL.
Для початку встановіть Pail у проєкт менеджером пакетів Composer:
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
Перекладаємо з офіційної документації, розділ за розділом, і не ховаємо недоперекладене. Помітили неточність у терміні чи реченні: напишіть, виправимо.