Эффективное логирование 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, который использует буферизацию и асинхронные обработчики.

- Line num php (использование __line__ в php)
- Error php stack trace (стек вызовов ошибки php)
- Php stack trace (получение стека вызовов в php)

Расширенные примеры логирования с помощью 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}

Логирование в PHP - comments

En
Index php log (php)