Slowlog PHP-FPM показывает стек вызовов запроса в момент, когда он работает дольше заданного порога, — это снимок одного застрявшего места выполнения, а не непрерывная трассировка. Инструмента для чтения этого снимка и отделения медленного кода от медленной базы данных в каталоге раньше не было.
Что именно пишет slowlog и чего не пишет
Если запрос работает дольше request_slowlog_timeout, PHP-FPM один раз делает дамп трассировки стека — какая функция и строка исполнялись на момент снятия снимка. Это не похоже на обычный error_log: там видно сообщение после завершения скрипта, а slowlog показывает происходящее в реальном времени, пока процесс ещё жив.
Snapshot не содержит истории вызовов во времени — только один срез на момент таймаута. Разбор того, что и как попадает в error_log и в вывод фаталов, отдельно описан в статье про логи PHP.
Включение slowlog в конфигурации пула
Директивы задаются в конфигурации пула, рядом с остальными параметрами PHP-FPM, разобранными в статье про пулы и воркеры PHP-FPM. request_slowlog_timeout определяет порог, после которого снимается трассировка, а slowlog — путь к файлу.
Порог стоит выбирать по ожидаемому времени ответа конкретного эндпоинта: для обычной страницы 5 секунд уже подозрительно долго, а для фонового тяжёлого отчёта тот же порог сработает на каждом запросе и забьёт лог бесполезными трассировками.
Как читать трассировку
В записи видно цепочку вызовов функций с номерами строк — сверху самый внешний вызов, снизу текущая, застрявшая функция. Если внизу вызов функции работы с файлами, сетью или базой данных, скорее всего скрипт ждёт внешний ресурс, а не считает что-то процессором.
- Внешний HTTP-запрос к API без таймаута или с большим таймаутом
- Запрос к базе данных без индекса — характерная цепочка вызовов PDO или mysqli
- Цикл с N+1 запросами, где каждая итерация ждёт отдельный ответ от базы
- Регулярное выражение с катастрофическим backtracking на длинной строке
- Ожидание файловой блокировки, которую держит другой процесс
Таблица основных параметров slowlog
Значения в таблице — ориентир для старта, а не универсальный стандарт: порог подбирается по профилю конкретного эндпоинта.
| Параметр | Что делает | Типичный старт |
|---|---|---|
| slowlog | путь к файлу трассировок | /var/log/php8.3-fpm-slow.log |
| request_slowlog_timeout | порог, после которого пишется трассировка | 5s |
| request_terminate_timeout | принудительное завершение зависшего запроса | 30s |
| slowlog_timeout_depth | глубина стека в записи трассировки | 128 |
Slowlog против slow query log MySQL
Если трассировка упирается в вызов PDO или mysqli, это признак медленного запроса к базе, а не медленного PHP-кода. В этом случае точный диагноз даёт slow query log самой базы — включение и разбор описаны в статье про MySQL Slow Query Log, где видно сам SQL и число просканированных строк.
Ротация: slowlog растёт быстро при низком пороге на нагруженном сайте, поэтому файл стоит включить в logrotate так же, как обычные логи Nginx и PHP, иначе он займёт всё свободное место на диске.
slowlog = /var/log/php8.3-fpm-slow.log
request_slowlog_timeout = 5s
request_terminate_timeout = 30sЧастые вопросы
Можно ли держать slowlog включённым постоянно в продакшене?
Да, если порог выбран разумно: запись делается только когда запрос превышает порог, поэтому постоянно включённый slowlog с порогом 5 секунд не нагружает сервер на обычных быстрых запросах.
Почему трассировка пустая или обрывается на первой строке?
Чаще всего это слишком маленькое значение slowlog_timeout_depth: оно ограничивает глубину стека, и если застрявший вызов находится глубже заданного предела, трассировка просто обрывается.
В трассировке видна функция curl_exec, что делать?
Это внешний HTTP-запрос без таймаута или с большим таймаутом — нужно добавить или уменьшить таймаут самого запроса, а не увеличивать request_slowlog_timeout, который только прячет симптом.