Эффективное логирование PHP-приложений: настройка и примеры
Логирование в PHP: обзор методов и лучшие практики
Наиболее эффективное решение: использование библиотеки Monolog
Monolog предоставляет гибкую систему каналов и обработчиков, позволяющую отправлять логи в файлы, базы данных, Slack и другие сервисы. Она поддерживает уровни логирования, форматирование, ротацию файлов и легко интегрируется с популярными фреймворками (Laravel, Symfony).
Установка через Composer:
composer require monolog/monologпроверка php (проверка установки php)
Простой пример использования в файле index.php:
<?php
require __DIR__ . '/vendor/autoload.php';
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// Создаём логгер для канала 'main'
$log = new Logger('main');
// Добавляем обработчик, пишущий в файл app.log
$log->pushHandler(new StreamHandler(__DIR__ . '/app.log', Logger::DEBUG));
// Записываем сообщение с уровнем INFO
$log->info('Приложение запущено');
$log->warning('Не найден конфигурационный файл', ['file' => 'config.php']);
Index php log (логирование в php)
Пояснение: Logger создаёт канал с именем. StreamHandler записывает логи в файл. Второй параметр конструктора - минимальный уровень логирования (DEBUG - все сообщения). Массив во втором параметре warning - контекстные данные, которые добавляются к записи.
Типичная ошибка: не указан путь к лог-файлу с правами на запись. Если файл не создаётся, проверьте наличие папки и права chmod. Monolog выбросит исключение UnexpectedValueException.
Решение: убедитесь, что директория существует и доступна для записи веб-сервером (обычно chmod 755 или 775 для директории, 664 для файла).
Как быстро записать сообщение в лог-файл без дополнительных библиотек?
Использование встроенной функции error_log(). Она отправляет сообщение в системный лог (обычно error_log PHP) или в указанный файл.
<?php
// Запись в файл custom.log
$message = 'Пользователь авторизован: ' . $username;
error_log($message, 3, __DIR__ . '/custom.log');
// Второй параметр 3 означает запись в файл, третий - путь к файлу
Php warning session start (предупреждение сессии)
Пояснение: первый параметр - сообщение, второй - тип сообщения (3 - файл), третий - путь. Если не указывать третий параметр, сообщение уходит в лог веб-сервера (Apache/Nginx).
Проблема: функция error_log() не добавляет временную метку и уровень логирования. Также нет ротации файлов.
Решение: самостоятельно форматировать сообщение, добавляя дату и уровень:
$message = '[' . date('Y-m-d H:i:s') . '] [INFO] ' . $message;
error_log($message, 3, 'custom.log');
Php view source (просмотр исходного кода php)
Как организовать собственное логирование с ротацией файлов?
Использование ручного управления файлами через fopen, fwrite и проверку размера файла.
<?php
$logFile = __DIR__ . '/app.log';
$maxSize = 5 * 1024 * 1024; // 5 MB
// Проверка размера и ротация
if (file_exists($logFile) && filesize($logFile) > $maxSize) {
rename($logFile, $logFile . '.' . date('YmdHis'));
}
$handle = fopen($logFile, 'a');
if ($handle) {
$message = '[' . date('Y-m-d H:i:s') . '] [ERROR] Ошибка подключения к БД';
fwrite($handle, $message . PHP_EOL);
fclose($handle);
}
Php stack (стек вызовов в php)
Пояснение: перед записью проверяется размер файла. Если превышен, текущий файл переименовывается (добавляется метка времени), затем создаётся новый. Запись происходит в режиме 'a' (добавление).
Типичная ошибка: одновременный доступ нескольких процессов к файлу может привести к повреждению данных. Функции fwrite не блокируют файл.
Решение: использовать блокировку flock($handle, LOCK_EX) перед записью:
flock($handle, LOCK_EX);
fwrite($handle, $message . PHP_EOL);
flock($handle, LOCK_UN);
Php trace (трассировка в php)
Как отправлять логи в системный журнал операционной системы?
Открытие соединения с syslog через openlog() и запись через syslog().
<?php
openlog('myapp', LOG_PID | LOG_PERROR, LOG_LOCAL0);
syslog(LOG_WARNING, 'Недостаточно памяти');
closelog();
Php get null (ошибка получения get параметров)
Пояснение: openlog() определяет идентификатор (myapp), опции (LOG_PID - добавить PID, LOG_PERROR - выводить на stderr) и facility (LOG_LOCAL0 - локальное использование). syslog() принимает уровень (LOG_WARNING) и сообщение. closelog() закрывает соединение.
Проблема: сообщения могут быть расценены как спам в системном логе, если не настроен фильтр. Также на некоторых хостингах доступ к syslog может быть ограничен.
Решение: использовать facility LOG_LOCAL0..LOG_LOCAL7 для отделения от системных сообщений. На сервере настроить rsyslog для записи в отдельный файл.
Как инкапсулировать логирование для переиспользования?
Создание класса-обёртки с методами info(), error() и т.п., которые внутри вызывают нужную реализацию.
<?php
class Logger {
private $logFile;
private $logLevel;
const LEVELS = ['DEBUG' => 0, 'INFO' => 1, 'WARNING' => 2, 'ERROR' => 3];
public function __construct($logFile, $level = 'DEBUG') {
$this->logFile = $logFile;
$this->logLevel = self::LEVELS[$level];
}
public function log($message, $level = 'INFO') {
if (self::LEVELS[$level] >= $this->logLevel) {
$formatted = '[' . date('Y-m-d H:i:s') . '] [' . $level . '] ' . $message . PHP_EOL;
file_put_contents($this->logFile, $formatted, FILE_APPEND | LOCK_EX);
}
}
public function info($message) { $this->log($message, 'INFO'); }
public function error($message) { $this->log($message, 'ERROR'); }
}
$logger = new Logger('app.log', 'ERROR');
$logger->info('Это не будет записано'); // игнорируется, т.к. уровень ERROR
$logger->error('Критическая ошибка'); // записывается
Пояснение: класс принимает путь к файлу и минимальный уровень. Метод log() проверяет, нужно ли записывать сообщение, форматирует строку и использует file_put_contents() с блокировкой для атомарной записи.
Типичная ошибка: при большом количестве логов класс может не справляться с нагрузкой. Рекомендуется для высоконагруженных проектов переходить на Monolog, который использует буферизацию и асинхронные обработчики.
Расширенные примеры логирования с помощью Monolog
Пример 1. Несколько обработчиков и форматирование
В одном логгере можно комбинировать разные обработчики: запись в файл и отправка email для критических ошибок.
<?php
require 'vendor/autoload.php';
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\NativeMailerHandler;
use Monolog\Formatter\LineFormatter;
$log = new Logger('app');
// Формат строки
$formatter = new LineFormatter("[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n");
// Обработчик для файла: все уровни
$fileHandler = new StreamHandler(__DIR__ . '/app.log', Logger::DEBUG);
$fileHandler->setFormatter($formatter);
$log->pushHandler($fileHandler);
// Обработчик для email: только ERROR и выше
$mailHandler = new NativeMailerHandler('admin@example.com', 'Critical Error', 'from@example.com', Logger::ERROR);
$log->pushHandler($mailHandler);
$log->info('Пользователь зарегистрирован'); // пойдёт только в файл
$log->error('База данных недоступна'); // пойдёт в файл и на email
# app.log после выполнения: [2025-04-13 10:00:00] app.INFO: Пользователь зарегистрирован [] [] [2025-04-13 10:00:01] app.ERROR: База данных недоступна [] [] # Письмо с темой 'Critical Error' отправлено на admin@example.com
Пример 2. Ротация файлов на основе размера
Monolog предоставляет готовый обработчик RotatingFileHandler, который автоматически создаёт новый файл каждый день (или по другому периоду) и удаляет старые.
<?php
use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;
$log = new Logger('app');
// Файл будет вида app-2025-04-13.log, хранить 30 дней
$handler = new RotatingFileHandler(__DIR__ . '/app.log', 30, Logger::DEBUG);
$log->pushHandler($handler);
$log->info('Логи будут ротироваться ежедневно');
# После нескольких дней в папке появятся файлы: app-2025-04-13.log app-2025-04-14.log ... # Файлы старше 30 дней будут автоматически удалены
Пример 3. Логирование с контекстом и исключениями
Monolog позволяет передавать произвольные данные и объекты исключений.
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger('app');
$log->pushHandler(new StreamHandler('app.log', Logger::DEBUG));
// Контекст - массив дополнительных данных
try {
// код, который может выбросить исключение
throw new \Exception('Неверный формат', 42);
} catch (\Exception $e) {
$log->error('Ошибка выполнения', [
'exception' => $e,
'user_id' => 123
]);
}
# app.log:
[2025-04-13 10:05:00] app.ERROR: Ошибка выполнения {"exception":"[object] (Exception(code: 42): Неверный формат at /path/to/file.php:12)","user_id":123} []
Пример 4. Использование процессоров для добавления метаданных
Процессоры автоматически добавляют информацию (IP, сессию, URL) к каждой записи.
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Processor\WebProcessor;
use Monolog\Processor\IntrospectionProcessor;
$log = new Logger('app');
$log->pushHandler(new StreamHandler('app.log', Logger::DEBUG));
// Добавляет IP, User-Agent, URL
$log->pushProcessor(new WebProcessor());
// Добавляет файл, строку, класс, функцию откуда вызван логгер
$log->pushProcessor(new IntrospectionProcessor());
$log->info('Запрос обработан');
# app.log (пример):
[2025-04-13 10:10:00] app.INFO: Запрос обработан {"url":"/index.php","ip":"127.0.0.1","http_method":"GET","server":"localhost","referrer":""} {"file":"/var/www/index.php","line":15,"class":null,"function":null}