В статье:
Из Composer проекта, если он там есть, иначе — из своей копии в
vendor/monolog/monolog модуля. Решает .settings.php модуля через
ShProjectContext из shef.options: если в vendor проекта Monolog лежит, своя
копия не регистрируется. Без shef.options или при ошибке разбора
composer.json проекта — своя копия.
Версия своей копии обязана подходить под ограничение из composer.json модуля
и не давать deprecation на поддерживаемых версиях PHP — сторожит
tests/vendor_test.php.
Сервисы \Bitrix\Main\DI\ServiceLocator, ключ services в .settings.php.
Каждому соответствует случай enum \Shef\Problems\Logger. Файлы — в каталоге
логов \Shef\Problems\Main\Constants::getLogDir(), вне корня сайта: при корне
/home/bitrix/www это /home/bitrix/sh_log, см. security.md.
Через enum:
\Bitrix\Main\Loader::includeModule('shef.problems');
\Shef\Problems\Logger::PrHtml->getLogger()->debug('что пришло', ['fields' => $fields]);
Через сервис напрямую:
$logger = \Bitrix\Main\DI\ServiceLocator::getInstance()->get('shef.problems.log.debug');
$logger->info('Импорт начат');
Logger::Problems->getLogger() бросает LogicException: это фабрика, а не
логгер, её строят через трейт — ниже.
Проблема — запись, которую надо не только сохранить, но и найти потом в
журнале событий по типу и понять, кому она адресована. Фабрика
\Shef\Problems\Factory\SystemLoggerFactory::build() строит логгер, который
пишет сразу:
<тип>.log в каталоге логов;В каждую запись добавляются модуль, класс и ответственный.
Обычно фабрику напрямую не зовут, а подключают трейт
\Shef\Problems\Factory\Trait\LoggerProblems:
\Bitrix\Main\Loader::includeModule('shef.problems');
final class OrdersExchange
{
use \Shef\Problems\Factory\Trait\LoggerProblems;
public function __construct()
{
$this->initLogger();
}
public static function getClassName(): string
{
return static::class;
}
public static function getModuleId(): string
{
return 'acme.exchange';
}
// Необязательное — умолчания: Info, SH_PROBLEMS_PROBLEM, «сотрудник по умолчанию».
protected static function getLogLevel(): \Monolog\Level
{
return \Monolog\Level::Error;
}
protected static function getAuditType(): string
{
return \Shef\Problems\Main\Constants::AuditTypeSync;
}
public static function getAssignedId(): int
{
return \Shef\Problems\Main\Constants::getSyncUserId();
}
public function run(): void
{
$this->logger->critical('1С не ответила за 30 секунд', ['itemId' => 1024]);
}
}
Трейт \Shef\Problems\Factory\Trait\DebuggerProblems — то же для отладки: даёт
$this->debugger на сервисе shef.problems.prHtml.debug.
Запускаемый пример — examples/problems.php.
\Bitrix\Main\Diag\Logger::createЯдро умеет создавать логгеры по имени из ключа loggers в
/bitrix/.settings.php или /bitrix/.settings_extra.php. Логгеры модуля туда
встают так:
return [
'loggers' => [
'value' => [
'shef.problems.prHtml' => [
'constructor' => static function () {
if(!\Bitrix\Main\Loader::includeModule('shef.problems'))
{
return null;
}
return \Shef\Problems\Logger::PrHtml->getLogger();
},
],
],
'readonly' => true,
],
];
\Bitrix\Main\Diag\Logger::create('shef.problems.prHtml')?->debug('сообщение', ['контекст']);
Собственный логгер с любыми обработчиками Monolog — например, файл плюс Telegram для важного:
'test' => [
'constructor' => static function () {
if(!\Bitrix\Main\Loader::includeModule('shef.problems'))
{
return null;
}
return (new \Shef\Problems\Integration\Monolog\Logger('test'))
->pushHandler(new \Monolog\Handler\StreamHandler(
stream: \Shef\Problems\Main\Constants::getLogFullPath('test'),
level: \Monolog\Level::Debug
))
->pushHandler(new \Monolog\Handler\TelegramBotHandler(
apiKey: '<ключ бота>',
channel: '<id канала>',
level: \Monolog\Level::Info
));
},
],
Ключ бота — секрет: держите его в .settings_extra.php, который не уезжает в
репозиторий проекта.
Все обработчики Monolog плюс свои:
Вывод на экран экранируется. В сообщение и контекст попадает что угодно, в том числе ввод посетителя, а смотрит на вывод администратор. До 2.0.0 это шло в страницу как есть.
Трассировка начинается с места вызова, где бы ни висел TraceProcessor —
на обработчике или на логгере: кадры самого Monolog и слоя интеграции
отрезаются по файлам, а не по счёту.
\Shef\Problems\Integration\Monolog\LoggerНаследник \Monolog\Logger. Первым аргументом принимает не только строку:
Остальное — InvalidArgumentException: лучше увидеть ошибку сразу, чем
потерять запись молча. Превращения делают стратегии
\Shef\Problems\Integration\Monolog\Strategy\LoggerConverter\IStrategy, по одной
на тип. Запускаемый пример — examples/logger.php.
Сбой записи не бросает. Обработчик не смог записать — каталог логов вне
open_basedir, нет прав, упал CEventLog — и исключение не уходит в
вызывающий код: логгер пишет в лог PHP строку shef.problems: запись логгера <канал> не прошла: <класс>: <первая строка сообщения>. Запись при этом
прерывается: обработчики ниже по стеку её не получают — так устроен
Monolog\Logger::addRecord(). У фабрики проблем журнал событий стоит выше
файла, поэтому сбой файла журнал не отменяет, а сбой журнала отменяет файл.
Нужно иначе — свой обработчик через setExceptionHandler(). Неверный тип
сообщения (InvalidArgumentException выше) по-прежнему бросает: это ошибка
вызова, а не записи.
← Уровни логирования | ↑ Содержание | Ротация логов →