Уровни логирования Nginx: error_log, access_log и отладка 502/504 | AdminWiki

Уровни логирования Nginx: error_log, access_log и отладка 502/504

01 октября 2026 12 мин. чтения

В Nginx восемь уровней логирования ошибок: debug, info, notice, warn, error, crit, alert, emerg. Уровень задаётся вторым параметром директивы error_log, первый параметр - путь к файлу, stderr или syslog-сервер. Если уровень не указан, используется значение по умолчанию - error.

Уровни перечислены в порядке возрастания важности и кумулятивны: при установке определённого уровня в лог попадают все сообщения этого уровня и уровней большей важности. Запись error_log /var/log/nginx/error.log warn; даёт warn, error, crit, alert и emerg, а notice, info и debug в файл не попадут. Выбор уровня - это выбор порога: всё, что ниже него, отбрасывается ещё до записи на диск.

Директива error_log работает в блоках http, server и location. Значит, можно держать общий лог на уровне warn, а для проблемного хоста или пути временно поднять детализацию до info или debug, не трогая остальной конфиг.

Как устроены уровни логирования в Nginx и что писать в error_log

Синтаксис простой: error_log <путь> <уровень>. Вместо файла допустимы stderr и syslog:server=unix:/dev/log. Уровень наследуется из родительского блока и переопределяется в дочернем: директива в location перекрывает директиву в server, а та перекрывает http.

Таблица уровней: от debug до emerg

УровеньЧто попадает в логТипичное сообщениеКогда включать
debugВнутренние события: разбор конфига, выбор location, работа апстримов, таймерыhttp upstream request: /api, upstream: 127.0.0.1:8081Только для точечной отладки, на минуты
infoИнформационные сообщения о ходе обработкиclient closed connectionРазбор поведения конкретного хоста
noticeНормальные, но заметные событияsignal process started, reloading configurationКонтроль применений конфига
warnПредупреждения, работа продолжаетсяupstream server temporarily disabledРабочий уровень на проде
errorОшибки обработки запросаconnect() failed (111: Connection refused) while connecting to upstreamЗначение по умолчанию, годится для большинства серверов
critКритические условия, требуется быстрое вмешательствосбой при выделении памяти под запросПрод, алерты на уровне системы
alertДействие нужно немедленноне удалось открыть лог-файл или pid-файлПрод, жёсткий алертинг
emergСистема неработоспособнастарт воркеров невозможенПрод, аварийный алертинг

Уровень debug доступен только в сборке с флагом --with-debug. Проверьте её перед тем, как править конфиг:

nginx -V 2>&1 | grep -- --with-debug

Если команда ничего не вывела, директива debug в конфиге приведёт к ошибке разбора при nginx -t, и вы узнаете об этом до применения. Отдельный случай - отладка одного клиента: директива debug_connection 192.0.2.10; включает отладочный лог для отдельных клиентских соединений, для остальных используется уровень, заданный директивой error_log. Отлаживаемые соединения задаются адресом IPv4 или IPv6, а также именем хоста.

Где можно задавать error_log: http, server, location

Разделять логи по хостам удобно, когда на одной машине живёт десяток сайтов. Общий файл быстро превращается в свалку, где grep по одному домену затягивается.

http {
    error_log /var/log/nginx/error.log warn;

    server {
        server_name api.example.com;
        error_log /var/log/nginx/api-error.log info;

        location /health {
            error_log off;
        }
    }
}

Здесь глобальный уровень warn, для API-хоста - info, а для health-check логирование ошибок отключено. Правило работает и в обе стороны: если в http стоит info, а в конкретном server нужен тихий режим, укажите там error или warn.

Два предостережения. Первое: слишком дробное разделение усложняет поиск, потому что вам придётся помнить, какой хост в какой файл пишет. Второе: локирование location стоит держать только для тех путей, где вы реально ищете проблему. Подробный разбор приоритетов location, proxy_pass и rewrite есть в материале об отладке маршрутизации Nginx.

Как включить debug-логирование для отдельного виртуального хоста без перезапуска сервера

Полный debug на все хосты в проде - плохая идея: объём логов растёт на порядки, а диск и I/O страдают. Точечное включение внутри одного блока server решает задачу без влияния на остальные сайты.

  1. Проверьте сборку: nginx -V 2>&1 | grep -- --with-debug.
  2. В блоке server нужного хоста добавьте отдельный файл и уровень debug: error_log /var/log/nginx/example.com-debug.log debug;
  3. Проверьте синтаксис: nginx -t.
  4. Примените graceful reload: systemctl reload nginx или nginx -s reload.
  5. Воспроизведите проблему и смотрите лог: tail -f /var/log/nginx/example.com-debug.log.
  6. После отладки верните warn или error и снова выполните reload.
server {
    listen 80;
    server_name example.com;

    error_log /var/log/nginx/example.com-debug.log debug;
    access_log /var/log/nginx/example.com-access.log main;
}

Проверка сборки и безопасный graceful reload

Разница между reload и restart принципиальна. Restart убивает воркеры и рвёт активные соединения, reload передаёт новую конфигурацию без обрыва уже открытых запросов: старые воркеры дорабатывают текущие сессии, новые поднимаются с обновлённым конфигом. Такой же механизм применяют при обновлении upstream, поэтому менять логи на живом сервере безопасно в рабочее время - этот подход описан в разборе деплоя без простоя.

Перед применением сохраните копию рабочего конфига, чтобы откатиться одной командой:

cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
nginx -t
systemctl reload nginx

Как не утопить прод в debug-логах

Debug-запись генерируется на каждый запрос и на каждое внутреннее событие: поиск location, разрешение апстрима, таймеры. На хосте с сотнями RPS файл вырастает в гигабайты за часы.

  • Включайте debug для одного server или одного location, а не глобально.
  • Держите период отладки в минутах, затем возвращайте рабочий уровень.
  • Направляйте debug в отдельный файл, а не в общий error.log, иначе рабочие сообщения утонут в отладочных.
  • Следите за размером: ls -lh /var/log/nginx/ и du -h /var/log/nginx/ покажут рост.
  • Не включайте debug на локации со статикой и высоким RPS.
  • Ставьте права 640 и владельца root:adm: в debug-сообщениях видны внутренние детали, IP и адреса бэкендов.

Настройка access_log: формат, разделение и уровни

Директива access_log записывает запросы, а структуру записи определяет log_format. Уровней у access_log нет, но их роль выполняет фильтрация по коду ответа, и об этом ниже. Формат combined идёт из коробки, для отладки его расширяют полями апстрима.

log_format: какие поля добавить для отладки

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
                'rt=$request_time uct=$upstream_connect_time '
                'uht=$upstream_header_time urt=$upstream_response_time '
                'ua=$upstream_addr us=$upstream_status';

Что даёт каждое поле:

  • $request_time - полное время обработки запроса на стороне Nginx.
  • $upstream_connect_time - время установки соединения с бэкендом.
  • $upstream_header_time и $upstream_response_time - время до первого байта заголовков и до полного ответа бэкенда.
  • $upstream_addr и $upstream_status - адрес и код ответа бэкенда.

Пустой $upstream_status означает, что запрос до апстрима не дошёл: отдала статика, сработало правило deny или соединение отвалилось раньше. Разница между $request_time и $upstream_response_time показывает, где теряется время: у клиента или у бэкенда. Набор метрик для алертов по этим величинам разобран в шпаргалке по метрикам Nginx.

Не пишите в access_log заголовки Authorization, Cookie и access_token. Формат лога - это файл на диске, который читают дежурные, копируют в тикеты и отправляют в внешние системы; токен в такой записи превращается в утечку.

Разделение access_log по кодам ответа через map

Самый частый сценарий: общий лог разрастается, а найти в нём пятисотые ошибки нужно за секунды. Условное логирование в Nginx включается директивой access_log с параметром if= и позволяет исключать из access-лога неважные записи. Директива map превращает код ответа в признак и позволяет писать в отдельный файл только проблемные запросы.

http {
    map $status $loggable {
        ~^[23] 0;
        default 1;
    }

    server {
        server_name example.com;
        access_log /var/log/nginx/example.com-access.log main;
        access_log /var/log/nginx/example.com-errors.log main if=$loggable;
    }
}

В example.com-errors.log попадут коды 4xx и 5xx, в основной файл - всё остальное. map вычисляется на каждый запрос, накладные расходы при этом минимальны. Тот же приём работает для разделения по хостам: достаточно объявить отдельный access_log внутри каждого server.

Для health-check, статики и внутренних проб логирование запросов отключают директивой access_log off; внутри location - это убирает шум из файлов.

Отладка 502 и 504 ошибок с помощью логов

502 Bad Gateway означает, что Nginx не получил корректный ответ от апстрима: бэкенд не слушает порт, упал, вернул битый ответ или соединение оборвалось. 504 Gateway Timeout означает, что бэкенд не ответил в отведённое время. Диагноз ставится по строкам в error.log и подтверждается полями апстрима в access.log.

Что искать в error_log при 502

Маркеры, по которым причина определяется за минуту:

  • connect() failed (111: Connection refused) while connecting to upstream - бэкенд не слушает порт: процесс упал, не стартовал или слушает другой порт.
  • no live upstreams while connecting to upstream - все серверы группы помечены как недоступные после серии неудач.
  • upstream prematurely closed connection while reading response header from upstream - бэкенд оборвал соединение до отправки заголовков.
  • recv() failed (104: Connection reset by peer) while reading response header from upstream - соединение сброшено на стороне бэкенда.
grep -i 'upstream' /var/log/nginx/error.log | tail -50

Если в логе видно, что недоступен один адрес из группы, посмотрите на конфигурацию апстрима и параметры отказоустойчивости:

upstream backend {
    server 127.0.0.1:8081 max_fails=3 fail_timeout=10s;
    server 127.0.0.1:8082 max_fails=3 fail_timeout=10s;
}

Параметр proxy_next_upstream задаёт, какие ответы считать ошибочными и когда передавать запрос следующему серверу. В рабочем конфиге перечисляют error, timeout, invalid_header и коды 5xx: error timeout invalid_header http_500 http_502 http_503 http_504 - полный набор для этого сценария приведён в материале о деплое Go-сервиса за Nginx. Держите оба инстанса в upstream постоянно, а их состояние переключайте через systemd: остановленный инстанс просто не принимает трафик. Бинарники удобно раскладывать по версиям-папкам, например /opt/myapp/releases/2026-09-15-1234/myapp, а симлинки currentA и currentB держать для конкретного инстанса. Юнит myapp@.service с параметром %i как порт-плейсхолдер позволяет запускать systemctl start myapp@8081.service и systemctl start myapp@8082.service независимо. Тогда запись в error.log про 127.0.0.1:8081 сразу показывает, какой инстанс выпал.

Что искать в error_log при 504

Маркеры таймаутов:

  • upstream timed out (110: Connection timed out) while reading response header from upstream - бэкенд не прислал заголовки в отведённое время.
  • upstream timed out (110: Connection timed out) while reading upstream - заголовки пришли, а тело ответа задерживается.

Сообщение while reading response header from upstream связано с чтением ответа от upstream, то есть с proxy_read_timeout. Значение по умолчанию proxy_send_timeout - 60s; для proxy_connect_timeout и proxy_read_timeout сверяйтесь с документацией вашей версии Nginx, так как в использованных источниках их значения по умолчанию прямо не подтверждены.

ПараметрЧто ограничиваетЗначение по умолчанию
proxy_connect_timeoutУстановку соединения с бэкендомуточните по документации версии
proxy_send_timeoutОтправку запроса бэкенду60s
proxy_read_timeoutОжидание ответа после отправки запросауточните по документации версии

Поднимать значение без анализа причины - плохая практика: если бэкенд отвечает медленно из-за тяжёлого запроса в БД или внешнего API, увеличение таймаута просто превратит быстрый 504 в долгий 504 и займёт воркеры. Сначала проверьте сам эндпоинт, минуя Nginx, и замерьте тайминги:

curl -o /dev/null -s -w 'dns=%{time_namelookup} connect=%{time_connect} ttfb=%{time_starttransfer} total=%{time_total}\n' http://127.0.0.1:8081/api/health

Связка access_log и error_log для точного диагноза

Одна запись в access.log и одна в error.log в одну секунду дают точный ответ, кто виноват. В access_log видно $status 502 и $upstream_addr 127.0.0.1:8081, а в error.log в то же время - connect() failed к 127.0.0.1:8081. Вывод: упал конкретный инстанс, а не балансировщик и не сеть.

Если в log_format добавлен $request_id, сквозная связка строится по нему. Без этой переменной ищите по времени и адресу апстрима:

grep ' 502 ' /var/log/nginx/example.com-access.log | tail -20
grep '2026-10-01 12:3' /var/log/nginx/error.log

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

Nginx не умеет ротировать свои логи самостоятельно: он открывает файл при старте и продолжает писать через этот дескриптор, пока что-то не попросит его переоткрыть. Удаление или переименование открытого лог-файла проблему не решает: Nginx продолжит писать по файловому дескриптору в уже несуществующий файл, место на диске не освободится. Рабочий механизм - logrotate с сигналом USR1, который заставляет Nginx закрыть дескрипторы и открыть файлы заново. Сигнал SIGUSR1 отправляется мастер-процессу.

/var/log/nginx/*.log {
    daily
    rotate 14
    missingok
    notifempty
    compress
    delaycompress
    sharedscripts
    postrotate
        kill -USR1 $(cat /var/run/nginx.pid 2>/dev/null) 2>/dev/null || true
    endscript
}

Разбор строк: daily и rotate 14 дают двухнедельную историю, compress и delaycompress сжимают всё, кроме свежего файла, sharedscripts запускает postrotate один раз на все совпавшие файлы, а notifempty не трогает пустые логи. Путь к pid-файлу зависит от сборки: встречаются /var/run/nginx.pid и /run/nginx.pid, проверьте свой через nginx -t или в конфиге.

logrotate -d /etc/logrotate.d/nginx
logrotate -f /etc/logrotate.d/nginx

Первая команда делает dry-run и показывает, что будет сделано, вторая принудительно запускает ротацию - удобно, чтобы проверить конфиг до наступления следующего цикла.

Базовые приёмы анализа логов: команды, которые экономят время

Пять команд закрывают большинство дежурных задач без установки тяжёлых систем.

awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
awk '$9 ~ /^5/ {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
tail -f /var/log/nginx/error.log
grep -c ' 502 ' /var/log/nginx/access.log
du -sh /var/log/nginx/

Первая выводит топ кодов ответа, вторая - самые частые URL с 5xx, третья показывает живые ошибки, четвёртая считает 502 за всё время в файле, пятая следит за объёмом каталога. Команды с полем $9 рассчитаны на формат combined, где $remote_addr стоит первым, а код ответа идёт девятым полем; если вы изменили порядок полей в log_format, скорректируйте номер колонки.

Для больших объёмов базовых команд мало: смотрите в сторону goaccess, отправки в Loki или ELK. Разбор готовых регулярных выражений для поиска медленных запросов, всплесков 404 и признаков сканирования приведён в статье о практическом анализе логов Nginx и Apache. Если в логах видны 403 по конкретным адресам, причина обычно в deny, geo или зонах limit_req - их разбор есть в аудите блокировок IP.

Логи содержат IP-адреса, user-agent, а при неаккуратном log_format и токены. Ограничьте доступ: chmod 640 и владелец root:adm, никакого чтения для посторонних пользователей.

Чек-лист: логирование Nginx в проде

  • error_log на уровне warn или error в блоке http.
  • Отдельный error_log для проблемного хоста на время отладки.
  • debug включается точечно и на минуты, сборка проверена через nginx -V.
  • access_log использует формат с $request_time и полями $upstream_*.
  • 4xx и 5xx вынесены в отдельный файл через map и if=$loggable.
  • health-check и внутренние пробы закрыты директивой access_log off.
  • logrotate настроен, postrotate отправляет USR1, dry-run пройден.
  • Права на файлы логов 640, владелец root:adm.
  • В логи не попадают Authorization, Cookie и токены.
  • После отладки уровень возвращён к рабочему значению.
  • Конфиг применён через systemctl reload nginx, без разрыва активных соединений.

Начните с двух действий: приведите error_log к уровню warn в http и добавьте $request_time с $upstream_response_time в log_format. Дальше ротация через logrotate с USR1 и разделение 4xx/5xx, и у вас появится база, по которой 502 и 504 разбираются за минуты, а не за час чтения одного гигантского файла.

Поделиться: