Эффективное логирование в PHP: библиотеки и техники
Эффективное логирование PHP с Monolog
Наиболее распространенное и гибкое решение для логирования в PHP - библиотека Monolog. Она позволяет отправлять логи в различные каналы (файлы, базы данных, email, системные журналы) с разными уровнями серьезности.
Как настроить Monolog для записи логов в файл?
Установка через Composer:
composer require monolog/monologRu log php (логирование php на русском)
Пример базовой настройки:
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger('my_app');
$log->pushHandler(new StreamHandler('/var/log/php/app.log', Logger::WARNING));
$log->warning('Это предупреждение');
$log->error('Произошла ошибка');В данном примере создается логгер с именем канала 'my_app', добавляется обработчик, который пишет в файл /var/log/php/app.log все записи уровня WARNING и выше.
Типичные проблемы:
- Файл лога не создается или не доступен для записи - проверьте права на директорию и файл.
- Логи не отображаются - возможно, уровень логирования слишком высокий (например, DEBUG не будет записан, если обработчик настроен на WARNING).
- Кодировка файла - рекомендуется указывать encoding в StreamHandler.
Как использовать встроенную функцию error_log?
PHP предоставляет функцию error_log для записи сообщений в журнал ошибок сервера или в указанный файл.
error_log('Текст ошибки', 3, '/tmp/php_errors.log');Второй параметр - тип сообщения: 0 (отправляется в системный журнал), 1 (по email), 3 (в файл). Третий параметр - путь к файлу.
Проблемы: Нет гибкости в форматировании, нельзя указать уровень, сложно ротировать логи.
Как настроить пользовательский обработчик ошибок с set_error_handler?
Для перехвата всех ошибок PHP (кроме фатальных) можно установить свою функцию.
function customError($severity, $message, $file, $line) {
$log = date('Y-m-d H:i:s') . \" [\" . $severity . \"] \" . $message . \" in \" . $file . \":\" . $line . \"\\n\";
file_put_contents('/var/log/php/errors.log', $log, FILE_APPEND);
}
set_error_handler('customError');
// Вызов несуществующей функции
trigger_error('Тестовая ошибка', E_USER_WARNING);Этот способ позволяет контролировать формат записи и обрабатывать ошибки произвольно.
Проблема: Фатальные ошибки (E_ERROR, E_PARSE) не перехватываются. Для них требуется register_shutdown_function.
Как логировать исключения с set_exception_handler?
function exceptionHandler($exception) {
file_put_contents('/var/log/php/exceptions.log',
'[' . date('Y-m-d H:i:s') . '] ' . $exception->getMessage() . \"\\n\",
FILE_APPEND);
}
set_exception_handler('exceptionHandler');
throw new \Exception('Тестовое исключение');Как объединить Monolog с обработчиками ошибок PHP?
Можно интегрировать Monolog с set_error_handler и set_exception_handler, чтобы все ошибки и исключения записывались через единую систему.
$log = new Logger('app');
$log->pushHandler(new StreamHandler('/var/log/php/errors.log', Logger::ERROR));
set_error_handler(function($severity, $message, $file, $line) use ($log) {
$log->error($message, ['file' => $file, 'line' => $line]);
});
set_exception_handler(function($exception) use ($log) {
$log->critical($exception->getMessage(), ['trace' => $exception->getTraceAsString()]);
});Такой подход дает единый формат и возможность добавлять контекст.
Расширенные примеры логирования
// Пример 1: Monolog с ротацией и форматтером
use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Formatter\LineFormatter;
$log = new Logger('app');
$handler = new RotatingFileHandler('/var/log/php/app.log', 30, Logger::DEBUG);
$handler->setFormatter(new LineFormatter(\"[%datetime%] %channel%.%level_name%: %message% %context% %extra%\\n\"));
$log->pushHandler($handler);
$log->info('Информационное сообщение', ['user_id' => 123]);[2023-10-01 12:00:00] app.INFO: Информационное сообщение {"user_id":123} []// Пример 2: Логирование в syslog через SyslogHandler
use Monolog\Handler\SyslogHandler;
$syslogHandler = new SyslogHandler('myapp', LOG_USER, Logger::ERROR);
$log->pushHandler($syslogHandler);
$log->error('Критическая ошибка');// Пример 3: Пользовательский обработчик ошибок с Monolog
function errorToMonolog($severity, $message, $file, $line) {
global $log;
$log->error($message, ['file' => $file, 'line' => $line]);
}
set_error_handler('errorToMonolog');
// Теперь все ошибки будут записываться через Monolog в файл с ротацией// Пример 4: Логирование в базу данных (PDO)
use Monolog\Logger;
use Monolog\Handler\PDOHandler;
$pdo = new PDO('mysql:host=localhost;dbname=logs', 'user', 'pass');
$handler = new PDOHandler($pdo, 'log_table', Logger::WARNING);
$log = new Logger('db');
$log->pushHandler($handler);
$log->warning('Сообщение в БД');// Пример 5: Добавление контекста через IntrospectionProcessor
use Monolog\Processor\IntrospectionProcessor;
$log->pushProcessor(new IntrospectionProcessor(Logger::DEBUG, ['skipClassesPartials' => ['Monolog\\']]));
$log->info('Где я?');extra: {\"file\":\"/var/www/html/index.php\",\"line\":42,\"class\":\"App\\Controller\\IndexController\",\"function\":\"index\"}// Пример 6: Группировка каналов с GroupHandler
use Monolog\Handler\GroupHandler;
$fileHandler = new StreamHandler('/var/log/php/app.log', Logger::INFO);
$syslogHandler = new SyslogHandler('myapp', LOG_USER, Logger::ERROR);
$group = new GroupHandler([$fileHandler, $syslogHandler]);
$log = new Logger('multi');
$log->pushHandler($group);
$log->info('Сообщение в файл');
$log->error('Ошибка в файл и syslog');// Пример 7: Использование BufferHandler для накопления и пакетной записи
use Monolog\Handler\BufferHandler;
$buffer = new BufferHandler($fileHandler, 10, Logger::DEBUG);
$log->pushHandler($buffer);
for ($i = 0; $i < 5; $i++) {
$log->info('Буферизованное сообщение ' . $i);
}
// При разрушении логгера или принудительном flush данные будут записаны// Пример 8: Логирование с использованием PSR-3 интерфейса (через абстракцию)
use Psr\Log\LoggerInterface;
use Monolog\Logger;
class MyService {
public function __construct(private LoggerInterface $logger) {}
public function doSomething() {
$this->logger->info('Сервис выполнен');
}
}
$monolog = new Logger('service');
$monolog->pushHandler(new StreamHandler('/var/log/php/service.log', Logger::INFO));
$service = new MyService($monolog);
$service->doSomething();