Мониторинг и логирование PHP-приложений: как не пропустить баг

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Ваше приложение молчит — это плохой знак

Вы когда-нибудь узнавали о баге на продакшене от разгневанного пользователя? Если да — вы знаете, как это больно. Если нет — вам повезло (пока).

PHP — язык, который работает на 78% всех сайтов мира. Но у него есть одна особенность: он слишком «тихий». По умолчанию PHP просто проглотит ошибку, покажет белую страницу или, что ещё хуже, продолжит работать с неверными данными. Без системы мониторинга и логирования вы слепы.

В этой статье мы разберём, как превратить ваше PHP-приложение из «чёрного ящика» в прозрачную систему, где каждая ошибка, каждый медленный запрос и каждое подозрительное действие будут зафиксированы. Поехали!

Почему «echo $error» — это не логирование?

Многие начинающие разработчики пишут:

try {
    // какой-то код
} catch (Exception $e) {
    echo $e->getMessage();
}

Это не логирование. Это крик в пустоту. Вывод в браузер не сохраняется, не структурируется и не даёт вам никакой аналитики. Настоящее логирование — это когда вы можете:

  • Увидеть ошибку через 5 минут после её возникновения
  • Понять, на каком сервере и в каком окружении это произошло
  • Отфильтровать тысячи записей и найти ту самую
  • Получить уведомление в Telegram/Slack, если что-то сломалось

Давайте перейдём к инструментам, которые это умеют.

Monolog — стандарт де-факто для PHP

Если вы пишете на PHP и не используете Monolog, вы, скорее всего, изобретаете велосипед. Это библиотека, которая стала стандартом для логирования в PHP (да, Laravel и Symfony используют её под капотом).

Установка и простейший пример

composer require monolog/monolog
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// Создаём канал логов
$log = new Logger('my_app');
// Пишем в файл /var/log/my_app.log
$log->pushHandler(new StreamHandler('/var/log/my_app.log', Logger::WARNING));

// Используем
$log->warning('Это предупреждение');
$log->error('Критическая ошибка: база данных недоступна');

Уже лучше. Но Monolog умеет гораздо больше. Вы можете отправлять логи в разные места одновременно:

  • В файл — для локальной отладки
  • В Elasticsearch — для полноценного поиска
  • В Telegram/Slack — для срочных уведомлений
  • В Syslog — для системного администрирования

Продвинутый пример с несколькими обработчиками

<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\TelegramBotHandler;
use Monolog\Handler\ElasticsearchHandler;
use Monolog\Formatter\LineFormatter;

$log = new Logger('production');

// Все ошибки пишем в файл
$fileHandler = new StreamHandler('/var/log/app.log', Logger::DEBUG);
$log->pushHandler($fileHandler);

// Критические ошибки — сразу в Telegram
$telegramHandler = new TelegramBotHandler(
    'YOUR_BOT_TOKEN', 
    'YOUR_CHAT_ID', 
    Logger::CRITICAL
);
$telegramHandler->setFormatter(new LineFormatter(
    "[%datetime%] %channel%.%level_name%: %message% %context%\n"
));
$log->pushHandler($telegramHandler);

// Логируем
$log->critical('База данных упала', ['server' => gethostname()]);

Теперь, если база упадёт ночью, вы узнаете об этом по уведомлению в телефоне. А не утром от клиента.

Sentry — мониторинг ошибок с контекстом

Monolog отлично пишет логи, но когда ошибок становится много, просто читать файлы — это ад. Тут на помощь приходит Sentry — система сбора ошибок с веб-интерфейсом.

Sentry автоматически собирает:

  • Стек вызовов (stack trace)
  • Данные запроса (URL, заголовки, тело)
  • Информацию о пользователе (если передали)
  • Состояние переменных в момент ошибки
  • Количество повторений (чтобы понять, насколько проблема массовая)

Подключение Sentry в PHP

composer require sentry/sdk
<?php
use Sentry\SentrySdk;
use Sentry\State\Hub;
use Sentry\ClientBuilder;

SentrySdk::init()->bindClient(
    ClientBuilder::create(['dsn' => 'https://your-dsn@sentry.io/1234567'])->getClient()
);

// Пример: логируем исключение
try {
    throw new \RuntimeException('Что-то пошло не так');
} catch (\Throwable $e) {
    \Sentry\captureException($e);
}

// Или просто сообщение
\Sentry\captureMessage('Пользователь загрузил странный файл');

После этого все ошибки улетают в Sentry, где их можно группировать, назначать ответственных и просматривать статистику. Бесплатный план покрывает до 5 тысяч событий в месяц — для небольшого проекта хватает за глаза.

ELK-стек: когда логов много

Если ваше приложение обрабатывает миллионы запросов, файлы логов становятся бесполезными. Вы просто не сможете в них найти нужную строчку. Тут нужен ELK-стек (Elasticsearch, Logstash, Kibana).

Схема работы:

  1. PHP-приложение пишет логи в JSON-формате в файл или напрямую в Logstash
  2. Logstash собирает, парсит и отправляет в Elasticsearch
  3. Kibana визуализирует данные: графики, дашборды, поиск

Пример: отправка логов в Elasticsearch через Monolog

<?php
use Monolog\Logger;
use Monolog\Handler\ElasticsearchHandler;
use Elastic\Elasticsearch\ClientBuilder;

$client = ClientBuilder::create()
    ->setHosts(['localhost:9200'])
    ->build();

$log = new Logger('app');
$esHandler = new ElasticsearchHandler($client, [
    'index' => 'php-logs-' . date('Y-m-d'),
    'type'  => '_doc'
]);
$log->pushHandler($esHandler);

$log->info('Пользователь залогинился', [
    'user_id' => 42,
    'ip'      => $_SERVER['REMOTE_ADDR']
]);

Теперь вы можете в Kibana построить график «ошибки по часам» или найти все логи конкретного пользователя за секунду.

Мониторинг производительности: Xdebug и Blackfire

Логирование ошибок — это полдела. Но что делать, если приложение просто тормозит? Тут нужен профилировщик.

Xdebug — бесплатно и сердито

Включите профилирование в php.ini:

xdebug.mode = profile
xdebug.output_dir                

Блоги

Рекомендаций по книгам