ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Логи без боли: access/error, PHP‑FPM, GoAccess, ротация логов и алерты

Логи — лучший друг админа, пока их не становится слишком много. Разберем, где искать access/error, как навести порядок в PHP‑FPM, поставить GoAccess, настроить ротацию через logrotate и добавить базовые алерты по 5xx и медленным запросам. Практика и готовые конфиги.
Логи без боли: access/error, PHP‑FPM, GoAccess, ротация логов и алерты

Зачем вообще заморачиваться с логами

Логи — это первоисточник правды о том, как реально живет ваш стек: веб-сервер (Nginx или Apache), PHP‑FPM и системные службы. По логам вы поймете, почему пользователи видят 502, что съедает CPU, кто стучится ботами к админке, и на какие страницы приходится пик трафика. Но без дисциплины и инструментов логи быстро превращаются в шум и заполненный диск. Разберем практический минимум: где и что логируется, как навести порядок в файлах, оперативно смотреть статистику через GoAccess, аккуратно крутить ротацию в logrotate и включить простые алерты, которые действительно срабатывают вовремя.

Что и где логируется в стеке

В типовой конфигурации Linux‑сервера логи складываются в каталог /var/log. Веб‑серверы пишут access/error в отдельные файлы, PHP‑FPM ведет свой журнал по пулам, а системный журнал — через journald или rsyslog. Главное — понимать, какие события искать в каждом месте:

  • Nginx: access.log — все HTTP‑запросы; error.log — ошибки уровня процесса и запроса (включая upstream timeout/502/504).
  • Apache: access_log и error_log, аналогично Nginx.
  • PHP‑FPM: error_log из PHP (notice/warning/fatal) и slowlog по пулам; опционально access.log для FPM.
  • Система: journalctl по сервисам (nginx.service, php-fpm.service, apache2.service).

Если вы используете VDS с небольшим диском, следите за объемом: некомпрессированные access‑логи активного сайта съедают гигабайты. Решение — грамотная ротация, компрессия и, по возможности, агрегация логов.

Пример конфигурации Nginx с разнесением access и error логов по сайтам

Nginx: настраиваем access/error осознанно

Начнем с Nginx. По умолчанию формат access‑лога похож на combined (как в Apache). Для анализа удобно либо оставить совместимый формат (чтобы использовать готовые пресеты в GoAccess), либо перейти на JSON — он проще парсится внешними системами. Однако, если цель — быстрый разбор в консоли, хватит и стандартного combined.

# /etc/nginx/nginx.conf (фрагмент)
log_format combined '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent"';

# Глобально можно отключить логи и включать их в server{} прицельно
access_log off;
error_log /var/log/nginx/error.log warn;

Для каждого сайта лучше разнести логи в свои файлы и задавать уровень ошибок отдельный. Это упрощает анализ и уменьшает шум.

# /etc/nginx/sites-enabled/example.conf (фрагмент)
server {
    server_name example.com;

    access_log /var/log/nginx/example.access.log combined;
    error_log  /var/log/nginx/example.error.log notice;

    location ~* \.(jpg|jpeg|png|gif|css|js|ico|woff2?)$ {
        # Для статики логи редко нужны — снижает шум и I/O
        access_log off;
        expires 7d;
    }

    location / {
        proxy_pass http://127.0.0.1:8080;
    }
}

Полезно уметь повышать детализацию на время дебага: error_log ... info|debug. Но не держите debug в проде — лог заполняется лавинообразно. По теме кэша и статики см. разбор заголовков и вариативности: Cache-Control и ETag. А если вы настраиваете заголовки безопасности, пригодится шпаргалка: HTTP Security Headers.

Apache: LogFormat, CustomLog, ErrorLog

В Apache подход похожий. Основное — настроить LogFormat и привязать его к CustomLog, а также разнести логи по виртуальным хостам. При необходимости — отключить логирование статических директорий через SetEnvIf.

# /etc/apache2/apache2.conf (фрагмент)
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

# /etc/apache2/sites-enabled/example.conf (фрагмент)
<VirtualHost *:80>
    ServerName example.com
    CustomLog /var/log/apache2/example.access_log combined
    ErrorLog  /var/log/apache2/example.error_log

    # Отключим шум от статики
    SetEnvIf Request_URI "\.(gif|jpg|png|css|js|ico|woff2?)$" no_log
    CustomLog /var/log/apache2/example.access_log combined env=!no_log
</VirtualHost>

Если выбираете или комбинируете веб‑серверы — сверяйтесь с нашим сравнением: Nginx vs Apache.

PHP‑FPM: error_log, slowlog и (опционально) access.log

За многие «таинственные» 502 отвечает не веб‑сервер, а backend. В случае PHP это PHP‑FPM. Включите slowlog, чтобы видеть, какие скрипты тормозят, и доступный access.log пула — для времени запроса и статуса.

# /etc/php/*/fpm/pool.d/www.conf (фрагмент)
; Путь для ошибок от PHP в рамках пула
php_admin_value[error_log] = /var/log/php-fpm/www.error.log
php_admin_flag[log_errors] = on

; Лог медленных скриптов
slowlog = /var/log/php-fpm/www.slow.log
request_slowlog_timeout = 3s

; Доступный лог пула (не путать с Nginx access)
access.log = /var/log/php-fpm/www.access.log
access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}dms %{kilo}Mkb"

Отдельно убедитесь, что в php.ini включен вывод ошибок в файл, а не в браузер, и задан лимит размера лога, если используется ротация через logrotate.

# /etc/php/*/fpm/php.ini (фрагмент)
log_errors = On
error_log = /var/log/php-fpm/php-error.log

Эта связка даст вам прозрачность: фронт (Nginx/Apache) показывает HTTP‑картину, FPM — где и почему тормозит или падает PHP‑код.

GoAccess: честный анализ трафика за минуты

GoAccess — удобный консольный инструмент для интерактивной аналитики access‑логов. Он понимает формат COMBINED из Nginx/Apache, умеет группировать по хостам, урлам, коду ответа, рефереру, времени ответа, IP и многое другое. Прелесть в том, что не нужно поднимать тяжелый стек — поставили пакет, указали путь к логу, получили живую панель.

# Установка (Debian/Ubuntu)
sudo apt update
sudo apt install goaccess

# Базовый запуск для Nginx/Apache в формате combined
sudo goaccess /var/log/nginx/example.access.log --log-format=COMBINED --time-format=%T --date-format=%d/%b/%Y

Если формат ваших логов отличается, укажите шаблон вручную. Для Nginx удобно оставить combined, чтобы не возиться с токенами. В интерактивном интерфейсе стрелками выбираете метрики, с помощью фильтров обрезаете шум (например, исключаете здоровье‑чеки или пути статики). Практический прием: держите отдельный access‑лог для админки/личного кабинета и анализируйте его отдельно — проще поймать всплески 401/403 и груминг‑атаки на пароли.

Интерактивная консольная панель GoAccess для анализа веб‑трафика

Ротация логов через logrotate: без сюрпризов

Ротация нужна, чтобы файлы не росли бесконечно, а старые архивировались и удалялись по сроку. Классический инструмент — logrotate. Он запускается ежедневно из cron и читает конфиги из /etc/logrotate.d. Для Nginx рецептура простая: дневная ротация, компрессия, хранение 14 архивов, и после ротации — сигнал на перезагрузку, чтобы процесс начал писать в новый файл.

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    rotate 14
    missingok
    notifempty
    compress
    delaycompress
    dateext
    create 0640 www-data adm
    sharedscripts
    postrotate
        systemctl reload nginx.service || true
    endscript
}

Для Apache принцип тот же. Учитывайте, что некоторые демоны не любят copytruncate (это когда файл копируют и обнуляют без сигнала процессу) — надежнее переслать reload или соответствующий сигнал (например, USR1 в Nginx).

# /etc/logrotate.d/apache2
/var/log/apache2/*log {
    daily
    rotate 14
    missingok
    notifempty
    compress
    delaycompress
    dateext
    create 0640 root adm
    sharedscripts
    postrotate
        systemctl reload apache2.service || true
    endscript
}

Для PHP‑FPM тоже задайте ротацию. Если у вас несколько пулов — используйте маску.

# /etc/logrotate.d/php-fpm
/var/log/php-fpm/*.log {
    daily
    rotate 14
    missingok
    notifempty
    compress
    delaycompress
    dateext
    create 0640 www-data adm
    postrotate
        systemctl reload php-fpm.service || true
    endscript
}

Проверяйте ротацию без ожидания ночного запуска: sudo logrotate -d /etc/logrotate.conf (dry‑run) и sudo logrotate -f /etc/logrotate.conf (форс). Если после удаления старых логов диск почему‑то не освобождается — возможно, процесс держит удаленный файл открытым. Диагностика: sudo lsof | grep deleted | grep log. На тарифах виртуальный хостинг ротация обычно преднастроена провайдером; на VDS контроль за логами полностью на вас.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Алерты по логам: просто и эффективно

Алерт должен срабатывать мало и метко. Лучше два качественных триггера, чем десять болтливых.

Минимально достаточный набор для веб‑стека: всплеск 5xx по фронту, рост 499/408 (клиентские разрывы, признак сетевых проблем или перегруза), медленные запросы по PHP‑FPM. Ниже — пример простого скрипта, который раз в минуту считает 5xx и ошибки upstream в текущем минутном окне, и при превышении порога шлет письмо локальному администратору. Для продвинутых сценариев подключите системный логгер или внешние системы мониторинга, но начать стоит с простого.

# /usr/local/bin/log-alerts.sh
#!/usr/bin/env bash
set -euo pipefail

NGINX_ACCESS="/var/log/nginx/example.access.log"
NGINX_ERROR="/var/log/nginx/example.error.log"
NOW_MIN=$(date +"%d/%b/%Y:%H:%M")

# Считаем 5xx за текущую минуту
CNT_5XX=$(grep "$NOW_MIN" "$NGINX_ACCESS" | awk '$9 ~ /^5/ {c++} END {print c+0}')
# Ищем upstream timeout/502 в error.log
CNT_UPSTREAM=$(grep "$NOW_MIN" "$NGINX_ERROR" | grep -E "upstream.*(timeout|502|504)" | wc -l || true)

THRESH_5XX=10
THRESH_UP=3

if [ "$CNT_5XX" -ge "$THRESH_5XX" ] || [ "$CNT_UPSTREAM" -ge "$THRESH_UP" ]; then
  MSG="$(hostname) nginx issues: 5xx=$CNT_5XX upstream_err=$CNT_UPSTREAM"
  echo "$MSG" | mail -s "ALERT: nginx errors" root
fi

Подключаем как systemd‑таймер, чтобы запускать каждую минуту. Это надежнее, чем cron, и проще собирать логи исполнения через journalctl.

# /etc/systemd/system/log-alerts.service
[Unit]
Description=NGINX log alerts

[Service]
Type=oneshot
ExecStart=/usr/local/bin/log-alerts.sh

# /etc/systemd/system/log-alerts.timer
[Unit]
Description=Run log alerts every minute

[Timer] 
Persistent=true

[Install]
WantedBy=timers.target

# Активируем
sudo systemctl daemon-reload
sudo systemctl enable --now log-alerts.timer

А что с медленными запросами PHP‑FPM? Раз в N минут проверяйте прирост строк в slowlog и шлите уведомление, если превышен порог. Также полезно алертить на резкий рост max_children reached в php-fpm — это узкое место пула.

# /usr/local/bin/fpm-slowlog-alert.sh
#!/usr/bin/env bash
set -euo pipefail
SLOWLOG="/var/log/php-fpm/www.slow.log"
THRESH=5
LAST_MIN=$(date +"%b %d %H:%M")
CNT=$(grep "$LAST_MIN" "$SLOWLOG" | wc -l || true)
if [ "$CNT" -ge "$THRESH" ]; then
  echo "$(hostname) php-fpm slowlog entries: $CNT" | mail -s "ALERT: php-fpm slow requests" root
fi

Снижаем шум и экономим диск

  • Отключайте access‑лог для статики и health‑check путей.
  • Снижайте уровень error_log до warn или notice в штатной эксплуатации, повышайте до info/debug только на время расследования.
  • Используйте gzip‑компрессию в logrotate с отложенной компрессией (delaycompress), чтобы не трогать свежий файл.
  • Разделяйте логи по сайтам/пулам. Один огромный access.log труднее анализировать и ротация на нем больнее.
  • Периодически проверяйте «подвисшие» удаленные логи через lsof. Это частая причина забитого диска после ротации.

Чтение и быстрая диагностика

  • tail -F /var/log/nginx/error.log — следим за ошибками в реальном времени; ключ -F устойчив к ротации.
  • grep -R "upstream timed out" /var/log/nginx — быстрый поиск типовых проблем.
  • awk '{print $9}' access.log | sort | uniq -c | sort -nr — частота кодов ответа.
  • journalctl -u php-fpm.service -S -10m — последние 10 минут системы по FPM.
  • zcat /var/log/nginx/access.log.1.gz | goaccess - --log-format=COMBINED — ретроспективный анализ сжатого лога без распаковки на диск.

Практические пресеты форматов

Хотите видеть время ответа в миллисекундах в Nginx access‑логе — добавьте $request_time. Это мгновенно показывает медленные ручки API и перегретые страницы.

# /etc/nginx/nginx.conf (фрагмент)
log_format timed '$remote_addr - $remote_user [$time_local] "$request" '
                 '$status $body_bytes_sent "$http_referer" '
                 '"$http_user_agent" $request_time';

Применяем к нужному сайту:

server {
    access_log /var/log/nginx/example.access.log timed;
}

В PHP‑FPM уже есть %{mili}d и %{kilo}M в access.format — ими удобно пользоваться для триажа узких мест без профайлера.

Чек‑лист по логам для продакшена

  • Nginx/Apache access/error разнесены по сайтам, статике отключен access_log.
  • В access‑логе есть $request_time (или аналог), в PHP‑FPM включены slowlog и access.log пула.
  • logrotate настроен: daily, compress, rotate 14, dateext, postrotate с reload сервиса.
  • Готовы быстрые команды анализа (goaccess для access, grep/awk для ошибок).
  • Есть хотя бы два алерта: всплеск 5xx и рост медленных PHP‑запросов.
  • Периодическая проверка диска и «удаленных, но занятых» логов через lsof.

Ошибки, которые встречаются чаще всего

Частая проблема — включили детальный debug в Nginx и забыли выключить. Через пару дней диск заполнен, сайт встал, паника. Другая классика — copytruncate в logrotate для сервиса, который плохо переносит обнуление файла. В результате теряются строки или процесс продолжает писать в старый дескриптор. Третья — «один общий лог на все», что делает расследование инцидента в разы сложнее и мешает точной ротации. И наконец, алерты без порогов и дедупликации: если вы получаете десятки писем в час, скоро перестанете их читать.

Итоги

Логи — фундамент мониторинга и отладки. Отдельные файлы на каждый сайт и пул, понятные форматы с временем ответа, быстрая аналитика через GoAccess, аккуратная ротация логов через logrotate и несколько простых, но точных алертов — этого достаточно, чтобы превратить хаос в управляемую систему. Начните с минимального набора из этой статьи, а дальше подключайте централизованный сбор и более сложные метрики по мере роста проекта. Главное — логи должны работать на вас, а не против.

Поделиться статьей

Вам будет интересно

memfd_create и «пропавшая» память: почему du не сходится с df и как найти deleted files через /proc, lsof и smaps OpenAI Статья написана AI (GPT 5)

memfd_create и «пропавшая» память: почему du не сходится с df и как найти deleted files через /proc, lsof и smaps

Если df показывает занятое место, а du «не видит» файлы, или RAM уходит без ясных причин, часто виноваты deleted files или shmem/m ...
Linux PSI: как измерять pressure CPU, памяти и диска и ловить latency spikes OpenAI Статья написана AI (GPT 5)

Linux PSI: как измерять pressure CPU, памяти и диска и ловить latency spikes

Linux PSI (Pressure Stall Information) показывает, сколько времени задачи реально «ждали» CPU, память или I/O. Разберём /proc/pres ...
Kubernetes: Pod завис в ContainerCreating — диагностика CNI, FailedMount и переполнения image filesystem OpenAI Статья написана AI (GPT 5)

Kubernetes: Pod завис в ContainerCreating — диагностика CNI, FailedMount и переполнения image filesystem

Pod может долго висеть в ContainerCreating из‑за сетевого плагина (CNI not initialized), проблем со storage (FailedMount, attach t ...