Лог ошибок PHP: от базовой настройки до продвинутых решений

Раздел: Отладка PHP -> Обработка ошибок

Логирование ошибок PHP: настройка и использование

Наиболее эффективный способ организовать запись ошибок PHP в лог это настройка директив log_errors и error_log в файле php.ini. Это позволяет централизованно управлять логированием для всех скриптов на сервере.

Как настроить запись ошибок в файл?

; Включить запись ошибок
log_errors = On

; Указать путь к файлу лога
error_log = /var/log/php_errors.log

; Уровень ошибок (E_ALL & ~E_NOTICE & ~E_DEPRECATED)
error_reporting = E_ALL

После изменения php.ini необходимо перезапустить веб-сервер (Apache, Nginx, PHP-FPM). Лог-файл должен быть доступен для записи пользователем веб-сервера (обычно www-data).

Типичные проблемы:

  • Файл лога не создается или пуст. Следует проверить права доступа к директории и наличие директивы log_errors.
  • Ошибки не записываются, если PHP работает в режиме CGI или FastCGI, может потребоваться отдельная настройка.
  • При использовании пула PHP-FPM, логи могут перенаправляться в stderr пула.

Как включить логирование ошибок в конкретном скрипте без доступа к php.ini?

Использование функции ini_set() позволяет изменить директивы во время выполнения скрипта.

<?php
// Включить логирование ошибок только для текущего скрипта
ini_set('log_errors', 1);
ini_set('error_log', '/tmp/my_script_errors.log');

// Установить уровень ошибок
error_reporting(E_ALL & ~E_NOTICE);

// Пример ошибки
echo $undefined_var;
?>

Это удобно для отладки отдельных страниц, но не заменяет глобальную настройку.

Возможные проблемы:

  • ini_set() может быть отключена в некоторых окружениях (например, в disable_functions).
  • Директива log_errors может быть уже отключена на уровне php.ini, и ini_set() не сможет её включить, если allow_url_include отключено (на самом деле не связано, но бывает путаница).

Как записывать ошибки с дополнительной информацией (время, стек вызовов)?

Создание собственного обработчика ошибок даёт полный контроль над форматом записи.

<?php
function customErrorHandler($errno, $errstr, $errfile, $errline) {
    $logMessage = sprintf(
        '[%s] Error level: %d, Message: %s, File: %s, Line: %d' . "\n",
        date('Y-m-d H:i:s'),
        $errno,
        $errstr,
        $errfile,
        $errline
    );
    error_log($logMessage, 3, '/var/log/custom_php_errors.log');
    return true; // предотвращаем стандартную обработку
}
set_error_handler('customErrorHandler');

// Пример ошибки
trigger_error('Тестовая ошибка', E_USER_WARNING);
?>

Функция error_log() с типом 3 записывает сообщение в указанный файл.

Подводные камни:

  • Не все типы ошибок могут быть перехвачены set_error_handler (например, E_ERROR, E_PARSE). Для них нужен register_shutdown_function или обработчик исключений.
  • Важно возвращать true, чтобы избежать двойной записи.

Как отправлять ошибки в системный лог (syslog)?

Директива error_log = syslog направляет все ошибки в системный журнал (rsyslog, syslog-ng).

; В php.ini
error_log = syslog

Или в коде:

<?php
ini_set('error_log', 'syslog');
openlog('MyApp', LOG_PID | LOG_PERROR, LOG_LOCAL0);
syslog(LOG_WARNING, 'Предупреждение: что-то пошло не так');
closelog();
?>

Полезно для интеграции с централизованными системами мониторинга.

Проблемы:

  • Сложность чтения логов неспециалистами.
  • Размер логов может быстро расти, требуется настройка ротации.

Как хранить ошибки в базе данных для последующего анализа?

С помощью того же пользовательского обработчика можно вставлять записи в таблицу БД.

<?php
function dbErrorHandler($errno, $errstr, $errfile, $errline) {
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    $stmt = $pdo->prepare('INSERT INTO error_log (timestamp, errno, errstr, errfile, errline) VALUES (NOW(), ?, ?, ?, ?)');
    $stmt->execute([$errno, $errstr, $errfile, $errline]);
}
set_error_handler('dbErrorHandler');
?>

Этот подход удобен для веб-приложений с админ-панелью.

Риски:

  • Ошибки подключения к БД могут вызывать рекурсивные ошибки.
  • Снижение производительности при большом количестве ошибок.
Как настроить продвинутое логирование с помощью библиотеки Monolog?

Monolog предоставляет гибкие обработчики (файлы, syslog, email, Slack и т.д.).

<?php
require 'vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\BrowserConsoleHandler;

$log = new Logger('my_app');
$log->pushHandler(new StreamHandler('/var/log/app.log', Logger::WARNING));
$log->pushHandler(new BrowserConsoleHandler(Logger::DEBUG));

// Пример
$log->error('Ошибка базы данных', ['code' => 500]);
?>

Дополнительно можно настроить формат сообщений через Formatter.

Сложности:

  • Требуется установка через Composer.
  • Избыточно для простых проектов.

Расширенные примеры логирования ошибок PHP

Пример 1: Обработка фатальных ошибок через register_shutdown_function

Пример
<?php
function shutdownHandler() {
    $error = error_get_last();
    if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
        $log = sprintf(
            '[%s] FATAL: %s in %s on line %d' . "\n",
            date('Y-m-d H:i:s'),
            $error['message'],
            $error['file'],
            $error['line']
        );
        error_log($log, 3, '/tmp/fatal_errors.log');
    }
}
register_shutdown_function('shutdownHandler');

// Вызов фатальной ошибки
// undefined_function();
?>
Результат: в файл /tmp/fatal_errors.log будет записана информация о фатальной ошибке.

Пример 2: Логирование исключений

Пример
<?php
function exceptionHandler($exception) {
    $log = sprintf(
        '[%s] Exception: %s in %s on line %d' . "\n" . 'Trace:' . "\n" . '%s' . "\n",
        date('Y-m-d H:i:s'),
        $exception->getMessage(),
        $exception->getFile(),
        $exception->getLine(),
        $exception->getTraceAsString()
    );
    error_log($log, 3, '/tmp/exceptions.log');
}
set_exception_handler('exceptionHandler');

throw new Exception('Тестовое исключение');
?>
Результат: в файл /tmp/exceptions.log появится запись с трассировкой стека.

Пример 3: Запись логов в формате JSON с Monolog

Пример
<?php
require 'vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\JsonFormatter;

$log = new Logger('api');
$handler = new StreamHandler('/var/log/api.log', Logger::DEBUG);
$handler->setFormatter(new JsonFormatter());
$log->pushHandler($handler);

$log->info('Пользователь авторизован', ['user_id' => 42, 'ip' => '192.168.1.1']);
$log->error('Ошибка валидации', ['field' => 'email', 'value' => 'invalid']);
?>
Результат: в файле api.log строки вида {\"message\":\"Пользователь авторизован\",\"context\":{\"user_id\":42,\"ip\":\"192.168.1.1\"},\"level\":200,\"level_name\":\"INFO\",\"channel\":\"api\",\"datetime\":\"2025-03-28T10:00:00+00:00\",\"extra\":[]}

Пример 4: Ротация логов с помощью logrotate

Пример
/var/log/php_errors.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 0640 www-data adm
    postrotate
        /usr/bin/killall -USR1 rsyslogd 2>/dev/null || true
    endscript
}
Результат: логи будут архивироваться каждый день, храниться 7 дней.

Пример 5: Использование error_log с типом 1 (отправка по email)

Пример
<?php
error_log('Критическая ошибка: потеря соединения с БД', 1, 'admin@example.com');
?>
Результат: письмо с сообщением будет отправлено на указанный адрес.

Пример 6: Комбинирование нескольких обработчиков в Monolog (файл + Slack)

Пример
<?php
require 'vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SlackWebhookHandler;

$log = new Logger('my_app');
$log->pushHandler(new StreamHandler('/var/log/app.log', Logger::WARNING));
$log->pushHandler(new SlackWebhookHandler(
    'https://hooks.slack.com/services/...',
    '#errors',
    'PHP Error Bot',
    true,
    null,
    Logger::CRITICAL
));
?>
Результат: WARNING и выше в файл, CRITICAL и выше в Slack.

Лог ошибок PHP - comments

En
Php error log (php)