- Опубликовано: 6 июл 2026
- 17
Логи, которые говорят: учимся читать journalctl, nginx и syslog
Часть 2 из 14 · Серия «Сервер в бою»
В прошлой статье мы разбирали ночную аварию и договорились о главном правиле: сначала смотрим, потом трогаем. Но «смотреть» — это не медитировать на htop. Смотреть — это читать логи. Сервер вообще-то очень разговорчивый собеседник: он подробно записывает всё, что с ним происходит, — кто пришёл, что запросил, что сломалось и почему. Проблема в том, что говорит он на своём языке, в нескольких местах одновременно, и по большей части — о вещах, которые вам не интересны.
Эта статья — про то, как в этом потоке слышать главное. Где какие логи лежат, как их фильтровать, а не листать, и как применить всё это на практике: найдём причину ошибки 502 за пять минут, по секундомеру.
Карта местности: кто и куда пишет
В типичном VPS с Ubuntu/Debian и связкой nginx + PHP + MySQL логи живут в трёх мирах, и путаница между ними — причина половины мучений новичка.
Мир первый: systemd-journal. Всё, что запущено через systemd (а это почти всё: nginx, PHP-FPM, MySQL, sshd, cron), пишет свой стандартный вывод и сообщения о запуске/падении в единый бинарный журнал. Читается он только командой journalctl. Именно здесь лежат ответы на вопросы «почему сервис не стартует» и «кто его убил».
Мир второй: классические текстовые логи в /var/log. Наследие доброго старого syslog, живее всех живых:
| Файл | Что там |
|---|---|
/var/log/syslog |
общесистемный поток: ядро, cron, всякая всячина |
/var/log/auth.log |
входы по SSH, sudo, попытки брутфорса |
/var/log/kern.log |
сообщения ядра (OOM killer живёт здесь) |
/var/log/dpkg.log |
что и когда устанавливалось через apt |
Мир третий: собственные логи приложений. Сервисы, которым журнала мало, ведут свои файлы:
| Файл | Что там |
|---|---|
/var/log/nginx/access.log |
каждый HTTP-запрос: кто, что, каким кодом ответили |
/var/log/nginx/error.log |
ошибки nginx: не достучался до бэкенда, кривой конфиг |
/var/log/php8.3-fpm.log |
жизнь пулов PHP-FPM: перезапуски, нехватка воркеров |
/var/log/mysql/error.log |
старт/стоп MySQL, крэши, проблемы InnoDB |
storage/logs/laravel.log и т.п. |
ошибки уровня вашего кода — внутри проекта |
Запоминать таблицы не нужно. Нужно запомнить принцип маршрутизации, он спасает всегда: ошибка HTTP-уровня → nginx, ошибка «сервис умер/не стартует» → journalctl, ошибка в логике сайта → лог приложения, безопасность → auth.log. Сначала выбираем мир, потом уже грепаем.
journalctl: журнал, который умеет фильтровать за вас
Первая ошибка новичка — запустить journalctl без аргументов и утонуть в тысячах строк с начала времён. Journalctl — это не файл, это база данных с языком запросов. Несколько запросов покрывают 95% задач.
Логи конкретного сервиса:
journalctl -u nginx
...за последний час и без листания:
journalctl -u nginx --since "1 hour ago" --no-pager
--since понимает человеческий язык: "today", "yesterday", "2026-07-04 02:00", "10 min ago". Это главный фильтр вообще: авария случилась в 3:12 — вот и смотрите --since "03:00" --until "03:20", а не весь журнал.
Только ошибки, без информационного шума:
journalctl -p err -b
-p err — приоритет «ошибка и хуже», -b — только текущая загрузка. Это команда номер один для утреннего осмотра сервера: пусто — живём.
Живой хвост (как tail -f):
journalctl -u php8.3-fpm -f
Запустили в одном окне, в другом — воспроизвели проблему на сайте, и смотрите, что сервис скажет в прямом эфире. Простейший, но невероятно эффективный приём.
Прошлая загрузка — помните из первой статьи? Если сервер перезагрузился, разгадка в журнале до ребута:
journalctl -b -1 -e
И маленькая, но важная деталь: journald по умолчанию может жить в памяти и забывать всё после перезагрузки. Проверьте, что журнал персистентный:
mkdir -p /var/log/journal && systemctl restart systemd-journald
Одна команда — и улики больше не испаряются вместе с ребутом.
Логи nginx: детектив в две колонки
У nginx две книги: access.log — «кто приходил», error.log — «что пошло не так». Начинать почти всегда стоит со второй: она короче и честнее.
error.log. Типичная строка:
2026/07/04 03:12:41 [error] 812#812: *44 connect() to unix:/run/php/php8.3-fpm.sock failed
(111: Connection refused) while connecting to upstream, client: 203.0.113.7,
server: example.com, request: "GET /catalog HTTP/1.1"
Читается как предложение: в 03:12 nginx попытался передать запрос PHP-FPM через сокет — и получил отказ. Connection refused = за сокетом никто не слушает = PHP-FPM мёртв. Диагноз, время, виновник — всё в одной строке. Самые частые фразы этого лога и их перевод:
Connection refused— бэкенд не запущен или слушает не там;Resource temporarily unavailable/upstream timed out— бэкенд жив, но захлебнулся: все воркеры заняты (приветpm.max_children, доберёмся в девятой части);No space left on device— а вот и вчерашний забитый диск;Too many open files— упёрлись в лимиты, частый спутник наплыва ботов.
access.log. Формат по умолчанию:
203.0.113.7 - - [04/Jul/2026:03:12:41 +0300] "GET /catalog HTTP/1.1" 502 552
"-" "Mozilla/5.0 ..."
IP, время, запрос, код ответа, размер, реферер, юзер-агент. Поодиночке строки скучные, интересна статистика. И тут начинается настоящая магия однострочников.
Сколько каких кодов мы отдавали за сегодня:
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
48211 200
1302 301
977 404
412 502
Кто долбит сервер сильнее всех:
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
Если в топе один IP с десятками тысяч запросов — вы нашли либо бота-парсера, либо брутфорсера (и это мостик к следующей статье про fail2ban).
Какие именно URL падают в 502:
awk '$9 == 502 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
Связка sort | uniq -c | sort -rn — «сгруппируй и отсортируй по частоте» — это швейцарский нож анализа логов. Выучите её один раз, и любой access.log превращается из простыни в аналитический отчёт.
Грепать осмысленно: пять правил
Grep — инструмент простой, но есть разница между «грепать» и «грепать осмысленно».
1. Сужайте по времени, а не по слову. Не ищите error по всему логу за месяц — сначала отрежьте нужный интервал (--since в journalctl, или грепните по дате-часу: grep "04/Jul/2026:03:1" access.log), и только внутри него ищите причину. Ошибка недельной давности вас сейчас только запутает.
2. Ищите первую ошибку, а не последнюю. Аварии каскадны: упал MySQL → посыпался PHP → nginx завалил лог пятисотками. Тысячи строк про 502 — это следствия. Мотайте вверх, к моменту, когда всё ещё было хорошо: первая ошибка в цепочке и есть причина.
3. Добавляйте контекст. grep -B 5 -A 5 "Fatal" покажет по пять строк до и после совпадения — часто причина написана строкой выше самой ошибки.
4. Исключайте шум. grep -v — «всё, кроме»: отфильтруйте свои IP, health-чеки мониторинга, знакомый безвредный warning — и в остатке останется настоящее.
5. Не игнорируйте warning'и хронически. Сегодняшний warning — это завтрашний error, который вы уже приучили себя не замечать. Разберитесь один раз или осознанно подавите — но не тренируйте «баннерную слепоту» на собственных логах.
Практикум: находим причину 502 за пять минут
Сведём всё вместе. Ночь, сайт отдаёт 502 Bad Gateway. Секундомер пошёл.
Минута 1. 502 = «nginx не дозвался бэкенда». Значит, наш мир — error.log nginx:
tail -50 /var/log/nginx/error.log
Видим: connect() to unix:/run/php/php8.3-fpm.sock failed (111: Connection refused). Подозреваемый назван — PHP-FPM.
Минута 2. Спрашиваем systemd, что с ним:
systemctl status php8.3-fpm
Active: failed (Result: oom-kill) — или просто failed. Мёртв. Но почему — статус говорит не всегда, поэтому...
Минута 3. ...идём в журнал за последними словами покойного:
journalctl -u php8.3-fpm --since "30 min ago" --no-pager
Вариант А: Out of memory: Killed process ... (php-fpm8.3) — OOM killer, знакомый по первой статье. Вариант Б: в логе паника после недавнего Reloading — кто-то (вы, три часа назад) поправил конфиг с опечаткой, и перезапуск его добил. Вариант В: лог обрывается на write: No space left on device — диск.
Минута 4. Подтверждаем гипотезу фактами: free -h и dmesg -T | grep -i oom для варианта А, php-fpm8.3 -t (проверка конфига) для Б, df -h для В.
Минута 5. И только теперь — лечим: systemctl restart php8.3-fpm, проверяем сайт, записываем причину в заметки. Перезапуск на пятой минуте — с диагнозом на руках — это совсем не то же самое, что перезагрузка на первой.
Заметьте маршрут: симптом → error.log nginx → systemctl status → journalctl → подтверждение → лечение. Он один и тот же почти для любой аварии, меняются только имена сервисов.
Пока логи не съели диск
Ложка дёгтя напоследок: логи — единственный источник правды, и они же — классический пожиратель диска. Access.log нагруженного сайта спокойно набирает гигабайты в неделю, а journald без лимитов расползается на всё свободное место. Быстрая проверка, кто сколько занял:
du -sh /var/log/* | sort -rh | head
journalctl --disk-usage
Журналу можно тут же выставить потолок (SystemMaxUse=500M в /etc/systemd/journald.conf), а текстовыми логами заведует logrotate — но это уже сюжет пятой статьи серии, про внезапно закончившийся диск. Пока просто знайте: за разговорчивость сервера кто-то платит гигабайтами.
Логи — это не наказание и не архив на случай суда. Это сервер, который всё время пытается вам что-то сказать. Научитесь задавать ему правильные вопросы — journalctl -u, --since, sort | uniq -c — и большинство «загадочных» аварий перестанут быть загадочными ещё до того, как вы откроете второй терминал. А в следующей статье почитаем самый тревожный лог сервера — auth.log — и посмотрим, сколько незваных гостей стучится в ваш SSH прямо сейчас. Спойлер: сотни. И поставим на дверь fail2ban.
- 1 Сервер упал в три ночи: разбор полётов без паники
- 2 Логи, которые говорят: учимся читать journalctl, nginx и syslog вы здесь
- 3 Fail2ban и брутфорс: как перестать кормить ботов скоро
- 4 Мониторинг для одного сервера: Uptime Kuma, Netdata и здравый смысл скоро
- 5 Диск закончился внезапно: куда деваются гигабайты на VPS скоро
- 6 Обновления без страха: apt upgrade, который ничего не сломает скоро
- 7 Свой VPN на VPS за полчаса: WireGuard без магии скоро
- 8 Несколько сайтов на одном VPS: делим ресурсы без драки скоро
- 9 Тормозит сайт — виноват не хостинг: тюним nginx и PHP-FPM скоро
- 10 MySQL на маленьком VPS: почему база съедает всю память скоро
- 11 Кэширование на пальцах: Redis, FastCGI cache и когда хватит просто заголовков скоро
- 12 Cron, systemd-таймеры и скрипты: автоматизируем рутину сервера скоро
- 13 Деплой без FTP в 2026-м: git pull, хуки и простой CI/CD на своём VPS скоро
- 14 Переезд на другой VPS без даунтайма: чек-лист миграции скоро
Была статья полезной: