
(!) Примеры и описание для более новой версии фреймворка HLEB2 могут отличаться от приведенных в тексте.
Логированием называют запись данных информационного характера в логи. Обычно вместе с логами автоматически сохраняется вторичная информация — когда и при каких системных обстоятельствах это происходило. Эта информация пригодится также при сортировке логов, чтобы помочь в расследовании конкретного инцидента или проверки работы участков системы. Реализация логирования важных участков позволяет восстановить картину произошедшего при выполнении программы.
Отладочная информация
При этом не стоить путать логи с отладочной информацией, которая представляет собой классический вывод сформированных данных в файл при необходимости срочно поправить критичный баг. На PHP это может выглядеть так:
file_put_contents(__DIR__ . '/info.log', print_r($problemData, true), FILE_APPEND);
Такой код обычно используется при локальной разработке, но не на рабочем сервере и после вывода данных и получения необходимой информации удаляется из кода и из директории исследуемого файла. Чтобы не забыть в проекте логируемые данные нужно называть файлы логов однообразно, например 'info.log' и в дальнейшем просто найти их через поиск. Этот вариант вывода не может быть полноценным логированием, так как он неудобен (нужно каждый раз писать код) и не содержит сам по себе сопутствующие данные (каждый раз их нужно включать в выводимый набор). Тех-же результатов можно добиться при использовании отладчика Xdebug, если он подключён. Но этот вариант нельзя назвать логированием.
Логирование по PSR-3
PSR-3 — это документ рекомендуемых общих стандартов для логирования. В него входит интерфейс LoggerInterface с уровнями логирования (отладка, информация, уведомление, предупреждение, ошибка, критическое состояние, аварийное предупреждение, аварийная ситуация). Подразумевается, что в проекте можно выставлять максимальный уровень логирования в зависимости от необходимости, например, если самый верхний - отладка нам нужен только при разработке на рабочем сервере его надо отключить. Кроме основного интерфейса в спецификации описаны другие классы, но использование их не обязательно. Упрощённый класс LoggerInterface (удалены оригинальные комментарии):
<?php
/** @see https://www.php-fig.org/psr/psr-3/ */
namespace Psr\Log;
interface LoggerInterface
{
/* Система непригодна для использования. */
public function emergency($message, array $context = []): void;
/* Действия по исправлению должны быть произведены немедленно. */
public function alert($message, array $context = []): void;
/* Критическое состояние системы. */
public function critical($message, array $context = []): void;
/* Ошибки выполнения, которые не требуют немедленных действий, но
должны быть зарегистрированы и контролироваться. */
public function error($message, array $context = []): void;
/* Исключительные случаи, не являющиеся критическими ошибками,
а предупреждениями. */
public function warning($message, array $context = []): void;
/* Обычные, но важные события. */
public function notice($message, array $context = []): void;
/* События, достойные внимания. */
public function info($message, array $context = []): void;
/* Детальная отладочная информация. */
public function debug($message, array $context = []): void;
/* Логи с произвольным уровнем. */
public function log($level, $message, array $context = []): void;
}
Из всего этого для логирования нужно понимать определение этих уровней, которые считаются стандартными. Несмотря на желание создателей этого интерфейса стандартизировать уровни логирования, последний метод 'log' сводит на нет эти усилия, позволяя использовать произвольные уровни, включая и те, что могут не поддерживаться. Поэтому рекомендуется использовать только именованные методы. Во фреймворке HLEB уже есть возможность логирования, далее, как её использовать.
Логирование во фреймворке HLEB
Для вывода лога в нужном месте достаточно добавить функцию Logger.
Функция Logger() возвращает объект с различными уровнями логирования, например:
Logger()->info('Info message');
Текущий логгер сохраняет логи в файлы, в том числе исключения и предупреждения PHP. В веб-окружении может быть вызван также как App()->logger()->error('Error message'). Задать максимальный принимаемый логгером уровень можно при помощи константы HLEB_MAX_LOG_LEVEL в конфигурации фреймворка, например:
/* Файл start.hleb.php */
define('HLEB_MAX_LOG_LEVEL', 'warning');
При значении 'warning' уровни 'notice', ' info' и 'debug' не будут использованы, так как по шкале тревожности они менее выражены. Остальные будут попадать в логи. Пример такой зависимости (интерфейс не может быть примером, так как там методы идут наоборот) с актуальным уровнем 'warning'.
debug (отладка)info (информация)notice (уведомление)- warning (предупреждение) - назначенный максимальный уровень логирования.
- error (ошибка)
- critical (критическое состояние)
- alert (аварийное предупреждение)
- emergency (аварийная ситуация)
Не рекомендуется устанавливать максимальное значение ниже "warning", иначе могут быть не учтены в логах ошибки выполнения и компиляции PHP кода. Если вы уже использовали фреймворк, то стандартные ошибки PHP(если возникали) как раз сохранялись в логи с уровнем ошибки. Оптимальный уровень при временной отладке - 'debug', в остальном - 'info'.
Вторым параметром (согласно LoggerInterface) можно передать подробные данные. Например:
Logger()->error('Error test message', ['value1' => 'test', 'value2' => 1001]);
Вывод в файл лога будет таким:
[11:34:00 29.09.2022 UTC] Web:ERROR Error test message {D:\web\hleb\resources\views\default.php on line 1} test.localhost.ru 127.0.0.1 {"value1":"test","value2":1001,"request_id":"fe63bd740e8e775521f8648b855b5108"}
Здесь выведены неоходимые данные, в том числе дата, тип ошибки, сообщение, файл в котором логгер находится, данные запроса и массив c параметрами логирования, преобразованный в JSON.
Для использования таких данных в специальных программах парсинга логов нужно создать собственный логгер (подменит текущий), на основе интерфейса LoggerInterface и разместить в папке app/Optional/MainLogger.php (класс App\Optional\MainLogger), сохраняющий в формате согласно требованиям этих программ.
Из приведённого здесь интерфейса видно, что $message в нём не обязательно является строкой. Но во фреймворке используется копия оригинального интерфейса Hleb/Scheme/Home/Main/LoggerInterface согласно которому реализация логгера во фреймворке по-умолчанию подразумевает, что первый параметр - это строка, так как в выводе лога это самая важная часть. При изменении на собственный класс логгера нужно будет следовать этому интерфейсу фреймворка, однако, если возникнет необходимость включить логи по оригинальному интерфейсу PSR-3, нужно будет отправлять их параллельно по тому-же пути сохранения.