На продакшене вывод ошибок в браузер выключен — и это правильно. Но у выключенного display_errors есть обратная сторона: пользователь видит белый экран, молча уходит, и никто об этом не узнаёт. Ошибка живёт в логе неделями, пока кто-нибудь случайно не откроет файл.
Мониторинг ошибок закрывает этот разрыв: приложение само сообщает о каждом исключении и фатальной ошибке во внешний сервис, тот группирует одинаковые ошибки, считает частоту и присылает уведомление. Разберём, как настроить это на PHP-проекте — от настроек интерпретатора до выбора между облаком и своим сервером.
Почему сырых логов на сервере не хватает
Логи — необходимый минимум, но как инструмент наблюдения за продом они слабы.
- Их никто не читает. Логи смотрят, когда уже пришла жалоба. То есть после того, как проблему заметил клиент.
- Нет группировки. Одна и та же ошибка, сработавшая 40 000 раз, — это 40 000 строк. Понять, что уникальных проблем всего три, по такому файлу тяжело.
- Нет контекста. В строке лога есть сообщение, файл и номер строки — но не URL, параметры запроса, ID пользователя, версия релиза и стек вызовов с кодом вокруг каждого кадра.
- Логи разъезжаются. На нескольких серверах, в контейнерах и под балансировщиком файл лежит в разных местах и умирает вместе с контейнером.
Трекер ошибок решает это одним приёмом: превращает поток событий в список проблем, отсортированный по важности. Про чтение самих логов на сервере есть отдельные заметки — шпаргалка по SSH-командам и разбор логов WordPress.
Шаг ноль: правильные настройки PHP на проде
Прежде чем подключать сторонний сервис, стоит привести в порядок сам интерпретатор. Минимальная боевая конфигурация в php.ini:
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/error.log
error_reporting = E_ALL
Два момента, которые часто делают неправильно. Первый: error_reporting на проде занижают до E_ALL & ~E_DEPRECATED & ~E_NOTICE, хотя логировать имеет смысл всё — deprecated-сообщения это ранний сигнал, что код сломается на следующей мажорной версии PHP. Второй: файл из error_log должен быть доступен на запись процессу PHP-FPM и ротироваться через logrotate, иначе однажды он съест диск.
Перехватить ошибки в самом коде можно через три точки входа: set_error_handler() для обычных ошибок, set_exception_handler() для непойманных исключений и register_shutdown_function() вместе с error_get_last() для фатальных. SDK трекеров внутри устроены именно так; писать это руками стоит, только если вы сознательно отказываетесь от готового решения.
Как устроен трекер ошибок
Терминология у всех инструментов примерно одинаковая, потому что де-факто стандартом стал протокол Sentry.
- Event (событие) — один факт ошибки с контекстом: стек, запрос, окружение.
- Issue (проблема) — группа одинаковых событий. Группировка идёт по «отпечатку» (fingerprint), который считается из типа исключения и верхних кадров стека. Именно поэтому 40 000 срабатываний превращаются в одну карточку со счётчиком.
- Release (релиз) — версия кода, при которой случилась ошибка. Без неё нельзя ответить на главный вопрос разбора инцидента: «это было всегда или приехало с последним деплоем?»
- Environment (окружение) —
production,staging,dev. Разделять обязательно, иначе шум со стейджа утопит боевые ошибки. - Breadcrumbs («хлебные крошки») — лента событий, предшествовавших ошибке: SQL-запросы, HTTP-вызовы, записи в лог. По ней восстанавливают, что именно делало приложение перед падением.
- DSN — строка-адрес проекта, по которой SDK понимает, куда слать события. В PHP его держат в переменных окружения.
Подключаем Sentry к PHP-проекту
Sentry — самый распространённый вариант, а его SDK понимают и альтернативы из раздела ниже, так что подключение не запирает вас в одном сервисе.
Установка и инициализация
Актуальная ветка PHP-SDK — 4.x (на момент написания последняя версия 4.31.0, вышла 27 августа 2026 года). Требуется PHP 7.2+ и расширения curl, json, mbstring.
composer require sentry/sentry
Инициализацию ставят как можно раньше — в точке входа, до загрузки основного кода приложения:
\Sentry\init([
'dsn' => $_ENV['SENTRY_DSN'],
'environment' => $_ENV['APP_ENV'] ?? 'production',
'release' => $_ENV['APP_RELEASE'] ?? null,
'sample_rate' => 1.0,
'traces_sample_rate' => 0.1,
'send_default_pii' => false,
]);
Дальше SDK сам перехватывает непойманные исключения и фатальные ошибки. Там, где исключение обрабатывается вручную, а знать о нём всё равно хочется, событие отправляют явно:
try {
$this->chargePayment($order);
} catch (\Throwable $e) {
\Sentry\captureException($e);
$order->markPaymentFailed();
}
Об опциях: sample_rate (по умолчанию 1.0) — доля отправляемых ошибок, снижать её стоит только на очень нагруженном проекте, иначе теряются редкие, но важные падения. traces_sample_rate (по умолчанию null, трассировка выключена) отвечает за замеры производительности — это отдельная квота, и 0.1 обычно достаточно. environment по умолчанию production, но задавать его явно надёжнее.
Laravel и Symfony
Для фреймворков есть обёртки, которые сами подцепляются к обработчику исключений, логгеру и трассировке запросов: sentry/sentry-laravel (ветка 4.x, Laravel 6–13; Laravel 11+ на PHP 8.2+ — начиная с 4.3.0) и sentry/sentry-symfony. Ставятся тем же composer require, настройки живут в config/sentry.php и config/packages/sentry.yaml. Отдельно инициализировать базовый SDK не нужно.
WordPress и WooCommerce
На сайте без Composer подключение делают через mu-plugin: кладут SDK в wp-content/mu-plugins/ и вызывают \Sentry\init() оттуда — так код отработает раньше плагинов и темы и поймает их ошибки тоже. Полезно прокинуть в контекст ID пользователя, активную тему и список плагинов: на типичном сайте половина ошибок приходит из чужого кода. Штатный WP_DEBUG_LOG при этом не отключайте — задачи разные, подробности в заметке про логи WordPress.
Что не надо отправлять наружу
Трекер получает содержимое запросов, а значит легко утащит на чужой сервер то, чего там быть не должно. Опция send_default_pii по умолчанию выключена (false) — не включайте её, не подумав: с ней в события попадают IP-адрес, cookies и заголовки, включая Authorization. Всё, что нужно вычистить дополнительно, убирают в колбэке before_send:
'before_send' => function (\Sentry\Event $event): ?\Sentry\Event {
$request = $event->getRequest();
unset($request['cookies'], $request['headers']['authorization']);
$event->setRequest($request);
return $event;
},
Тот же колбэк работает как фильтр: если вернуть null, событие не уйдёт вообще. Так глушат мусор — ботов, падения на несуществующих URL, известные исключения (для последних есть и более прямой путь — опция ignore_exceptions со списком классов). И помните про закон о персональных данных: отправка ПДн в зарубежное облако — трансграничная передача со всеми вытекающими требованиями, и одно это часто решает вопрос в пользу self-hosted.
Сколько это стоит
У облачного Sentry бесплатный план Developer даёт 5 000 ошибок в месяц, 5 ГБ логов, 5 млн спанов и одного пользователя. Team — 26 $ в месяц при годовой оплате, 50 000 ошибок и неограниченное число пользователей. Business — 80 $ в месяц, та же квота по ошибкам, но снимаются ограничения по дашбордам и добавляются SAML/SCIM. Превышение квоты оплачивается отдельно по модели pay-as-you-go.
Ключевой нюанс: считаются события, а не уникальные проблемы. Зацикленный крон, роняющий исключение раз в секунду, выедает месячную квоту бесплатного плана примерно за полтора часа. Поэтому before_send и лимиты на стороне приложения — способ не получить внезапный счёт.
Своё или облако
Все три варианта ниже говорят на одном протоколе, то есть в коде меняется только DSN.
- Self-hosted Sentry. Полная функциональность и никаких квот, но аппетиты серьёзные: официально требуется 4 ядра, 16 ГБ RAM плюс 16 ГБ swap (рекомендуется 32 ГБ) и 20 ГБ диска, Docker 19.03.6+ и Docker Compose 2.32.2+. Установка сильно упирается в дисковый ввод-вывод.
- GlitchTip. Лёгкая реализация того же API: минимум 256 МБ RAM в all-in-one сборке (рекомендуется 512 МБ), PostgreSQL 14+, опционально Redis или Valkey 7+, около 30 ГБ диска на миллион событий в месяц. Умеет заметно меньше Sentry, но для задачи «ловить ошибки и присылать алерты» этого хватает.
- Bugsink. Ещё один self-hosted трекер, совместимый с SDK Sentry, с прицелом на простую установку. Компромисс, если GlitchTip кажется аскетичным, а Sentry — тяжёлым.
Практическое правило: если проект один и трафик небольшой — берите бесплатный облачный план и не тратьте время. Если проектов много, данные чувствительные или квота бесплатного плана уже жмёт — GlitchTip на VPS за пару сотен рублей в месяц закрывает вопрос.
Алерты, которые не захочется выключить
Самый частый способ провалить мониторинг — уведомлять о каждом событии. Через неделю письма уходят в отдельную папку, и смысл теряется. Работают узкие условия:
- появилась новая проблема, которой не было раньше (это главный сигнал);
- проблема, помеченная как решённая, вернулась (регрессия после деплоя);
- частота выросла резко — например, больше 50 событий за 10 минут;
- ошибка затронула больше N уникальных пользователей.
Отдельное правило стоит завести на платёжный и заказной контур — там цена незамеченной ошибки выше. И помните, что часть «ошибок» — на самом деле проблемы производительности: таймауты запросов к базе прилетают в трекер как исключения, а лечатся не в PHP-коде — см. заметку про индексы MySQL и чек-лист ускорения WordPress.
Чек-лист
display_errors = Off,log_errors = On,error_reporting = E_ALL, ротация лога через logrotate.- Подключён SDK, инициализация стоит в самой ранней точке входа.
- Заданы
environmentиrelease; релиз обновляется автоматически при деплое. - DSN лежит в переменных окружения, а не в коде репозитория.
send_default_piiвыключен, чувствительные поля вычищены вbefore_send.- Шум отфильтрован: боты, 404, известные исключения — в
ignore_exceptions. - Алерты настроены на новые проблемы, регрессии и всплески, а не на каждое событие.
- Есть договорённость, кто и когда разбирает очередь, — иначе всё предыдущее бесполезно.
Вывод
Мониторинг ошибок делается за час, а окупается при первом же инциденте. Начать можно с малого: поставить SDK, задать окружение и релиз, включить одно правило «новая ошибка в production» и месяц просто смотреть, что приходит. Почти всегда выясняется, что на проде годами живут две-три ошибки, о которых никто не знал, — и чинятся они за вечер.
Частые вопросы
Лог фиксирует факт ошибки, но не превращает его в задачу. Трекер группирует одинаковые события в одну проблему, считает частоту и число затронутых пользователей, добавляет контекст (URL, параметры запроса, стек с кодом, релиз) и сам присылает уведомление — вместо того чтобы ждать, пока кто-то откроет файл.
Бесплатный план Developer даёт 5 000 ошибок в месяц и одного пользователя, Team — 26 $ в месяц за 50 000 ошибок, Business — 80 $. Считаются события, а не уникальные проблемы, поэтому один зацикленный крон способен выесть месячную квоту за часы. Для небольшого сайта бесплатного плана обычно хватает при условии, что шум отфильтрован через before_send.
Да, и сразу тремя способами. Self-hosted Sentry даёт полную функциональность, но требует 4 ядра, 16 ГБ RAM плюс swap и быстрый диск. GlitchTip — совместимая реализация того же API, которой хватает 256–512 МБ RAM и PostgreSQL 14+. Bugsink — ещё один лёгкий вариант. Во всех случаях в коде меняется только DSN.
Опция send_default_pii по умолчанию выключена, то есть IP, cookies и заголовки не отправляются. Всё остальное чувствительное вычищают в колбэке before_send, а ненужные события там же отсекают возвратом null. Если в запросах есть персональные данные, отправка в зарубежное облако считается трансграничной передачей — в этом случае разумнее поднять трекер на своём сервере.
Источники
- 1.Sentry для PHP — официальная документацияhttps://docs.sentry.io/platforms/php/
- 2.Sentry PHP SDK: опции конфигурацииhttps://docs.sentry.io/platforms/php/configuration/options/
- 3.Пакет sentry/sentry на Packagisthttps://packagist.org/packages/sentry/sentry
- 4.Пакет sentry/sentry-laravel на Packagisthttps://packagist.org/packages/sentry/sentry-laravel
- 5.Тарифы Sentryhttps://sentry.io/pricing/
- 6.Требования Self-Hosted Sentryhttps://develop.sentry.dev/self-hosted/
- 7.GlitchTip — установка и системные требованияhttps://glitchtip.com/documentation/install
- 8.Bugsink — документацияhttps://www.bugsink.com/docs/



