Skip to content

Latest commit

 

History

History
458 lines (334 loc) · 18.7 KB

13-logging.md

File metadata and controls

458 lines (334 loc) · 18.7 KB

Этап 12. Логирование и мониторинг

Зачем следить за логами

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

Без логов невозможно:

  • понять, почему пользователь видит ошибку 500;
  • найти, кто сканирует сервер в поисках уязвимостей;
  • выяснить, когда и почему «упал» сайт;
  • оценить нагрузку — сколько запросов в час получает сервер.

Привязка к проекту. В предыдущих главах мы развернули WordPress на nginx + PHP-FPM + MariaDB. Теперь научимся анализировать логи работающего сайта — инструментами из главы 05 (grep, awk, sort, uniq, wc), но уже на реальных данных.

Системный журнал: journalctl (расширение)

В главе 09 мы познакомились с journalctl и научились смотреть логи конкретного сервиса (-u), следить в реальном времени (-f) и фильтровать по времени (--since). Теперь расширим эти знания.

Приоритеты сообщений

Каждое сообщение в журнале имеет приоритет — уровень важности. Можно фильтровать по нему, чтобы видеть только ошибки и не тонуть в информационных сообщениях:

journalctl -p err                    # только ошибки и критичнее
journalctl -p warning                # предупреждения и критичнее
journalctl -p err --since today      # ошибки за сегодня

Таблица приоритетов (от самого серьёзного к информационному):

Приоритет Номер Описание
emerg 0 Система неработоспособна
alert 1 Требуется немедленное вмешательство
crit 2 Критическая ошибка
err 3 Ошибка
warning 4 Предупреждение
notice 5 Важное информационное сообщение
info 6 Информационное сообщение
debug 7 Отладочная информация

Флаг -p err покажет сообщения с приоритетами 0–3 (от emerg до err). Это самый полезный фильтр для поиска проблем.

Логи текущей загрузки

journalctl -b                        # логи с момента загрузки
journalctl -b -p err                 # ошибки с момента загрузки

Если сервер недавно перезагружался и что-то сломалось, -b покажет только сообщения текущей сессии — без «хвоста» от прошлых загрузок.

Размер журнала и ротация

Журнал systemd растёт со временем и может занять значительную часть диска. Проверим, сколько места он занимает:

journalctl --disk-usage
Archived and active journals take up 120.5M in the file system.

Если журнал стал слишком большим, можно ограничить его размер или возраст:

sudo journalctl --vacuum-size=500M   # оставить не более 500 МБ
sudo journalctl --vacuum-time=7d     # удалить записи старше 7 дней

Для постоянного ограничения размера отредактируйте файл /etc/systemd/journald.conf и задайте параметр:

SystemMaxUse=500M

Логи nginx

nginx ведёт свои собственные логи, отдельно от системного журнала. Это текстовые файлы, которые можно анализировать утилитами из главы 05.

Где лежат логи

Файл Содержание
/var/log/nginx/access.log Каждый HTTP-запрос к серверу
/var/log/nginx/error.log Ошибки nginx и PHP-FPM

Формат access.log

По умолчанию nginx использует формат combined. Каждая строка — один запрос:

192.168.1.50 - - [17/Mar/2026:10:15:32 +0300] "GET /wp-login.php HTTP/1.1" 200 4523 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"

Разберём по полям:

# Поле Пример Значение
1 IP-адрес клиента 192.168.1.50 Кто обратился
2 Идентификатор - Обычно пустой
3 Пользователь - Имя (при HTTP-авторизации)
4 Дата и время [17/Mar/2026:10:15:32 +0300] Когда
5 Запрос "GET /wp-login.php HTTP/1.1" Метод, URL, протокол
6 Код ответа 200 Результат (200, 404, 500...)
7 Размер ответа 4523 В байтах
8 Referrer "-" Откуда пришёл пользователь
9 User-Agent "Mozilla/5.0 ..." Браузер или бот

Знание формата — ключ к анализу. Поля разделены пробелами, а дата, запрос, referrer и user-agent заключены в кавычки или квадратные скобки.

Формат error.log

Ошибки выглядят иначе:

2026/03/17 10:20:45 [error] 1234#1234: *5 open() "/var/www/wordpress/favicon.ico" failed (2: No such file or directory), client: 192.168.1.50, server: _, request: "GET /favicon.ico HTTP/1.1"

Здесь видно: дату, уровень ошибки ([error]), описание проблемы (файл не найден) и детали запроса.

Анализ access.log с помощью coreutils

Вот где пригодятся навыки из главы 05. Мы будем анализировать реальные логи работающего WordPress, используя конвейеры из awk, sort, uniq, grep и wc.

Все примеры работают с файлом /var/log/nginx/access.log. Для чтения этого файла может потребоваться sudo.

Сколько запросов получил сервер

sudo wc -l /var/log/nginx/access.log

Топ IP-адресов (кто чаще всего обращается)

sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head

Разбор конвейера:

  1. awk '{print $1}' — извлечь первое поле (IP-адрес)
  2. sort — отсортировать (нужно для uniq)
  3. uniq -c — подсчитать повторения
  4. sort -rn — отсортировать по убыванию числа
  5. head — показать первые 10

Результат:

    342 192.168.1.50
    128 10.0.0.5
     45 192.168.1.1

Если один IP делает тысячи запросов — возможно, это бот или атака.

Распределение по кодам ответа

sudo awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

Девятое поле ($9) — код ответа. Результат:

    412 200
     23 301
     15 404
      3 500

Много 404 — возможно, сломаны ссылки или кто-то сканирует сервер. 500 — ошибки в PHP-коде или проблемы с базой данных.

Найти все ошибки 404 (страница не найдена)

sudo awk '$9 == 404 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

Здесь $9 == 404 фильтрует строки по коду, а $7 — URL запроса. Вы увидите, какие несуществующие страницы запрашивают чаще всего.

Найти серверные ошибки (500)

sudo grep '" 500 ' /var/log/nginx/access.log

Каждая строка с кодом 500 — повод разобраться. Посмотрите также error.log — там будет описание причины.

Самые популярные страницы

sudo awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head

Запросы за последний час

sudo awk -v d="$(date -d '1 hour ago' '+%d/%b/%Y:%H')" '$4 ~ d' /var/log/nginx/access.log | wc -l

Этот конвейер формирует строку с датой часовой давности и фильтрует лог по ней. Так можно отслеживать активность по часам.

Найти ботов

sudo grep -i 'bot\|crawl\|spider' /var/log/nginx/access.log | awk '{print $1}' | sort -u

Покажет IP-адреса поисковых ботов (Googlebot, Bingbot и т.д.).

Анализ error.log

Error.log содержит ошибки nginx, PHP-FPM и связанных компонентов. Типичные проблемы:

Ошибка Причина
No such file or directory Запрошенный файл не существует (404)
Permission denied Нет прав на чтение файла
upstream timed out PHP-FPM не ответил вовремя
connect() failed PHP-FPM не запущен или сокет недоступен

Посмотреть последние ошибки:

sudo tail -20 /var/log/nginx/error.log

Найти ошибки определённого типа:

sudo grep 'Permission denied' /var/log/nginx/error.log
sudo grep 'upstream' /var/log/nginx/error.log

Совет. Если сайт показывает ошибку 502 Bad Gateway, первое, что нужно проверить — работает ли PHP-FPM (systemctl status php*-fpm) и нет ли ошибок про upstream в error.log.

Ротация логов (logrotate)

Зачем нужна ротация

Логи растут непрерывно. Если access.log получает 10 000 записей в день, через год он будет весить несколько гигабайт. Ротация логов решает эту проблему: старый лог архивируется (сжимается), создаётся новый пустой файл, а самые старые архивы удаляются.

В Debian за это отвечает утилита logrotate, которая запускается автоматически (обычно раз в день через systemd-таймер или cron).

Конфигурация для nginx

cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        if [ -d /etc/nginx ]; then
            invoke-rc.d nginx rotate >/dev/null 2>&1
        fi
    endscript
}

Основные директивы:

Директива Значение
daily Ротация раз в день
rotate 14 Хранить 14 архивов (2 недели)
compress Сжимать старые логи (gzip)
delaycompress Не сжимать самый свежий архив
notifempty Не ротировать пустые файлы
create 0640 www-data adm Создать новый файл с указанными правами
postrotate Команда после ротации (сигнал nginx перечитать файлы)

После ротации в /var/log/nginx/ вы увидите файлы вроде:

access.log            — текущий лог
access.log.1          — вчерашний (ещё не сжат)
access.log.2.gz       — позавчерашний (сжат)
access.log.3.gz       — три дня назад
...

Практика: анализируем логи WordPress

Шаг 1. Смотрим системный журнал

Проверим, нет ли ошибок в сервисах нашего стека:

journalctl -u nginx -p err --since today
journalctl -u php*-fpm -p err --since today
journalctl -u mariadb -p err --since today

Если вывод пуст — ошибок нет, всё работает.

Шаг 2. Генерируем трафик для анализа

Чтобы в логах было что анализировать, сделаем несколько запросов:

curl -s http://localhost > /dev/null
curl -s http://localhost/wp-login.php > /dev/null
curl -s http://localhost/not-found-page > /dev/null
curl -s http://localhost/readme.html > /dev/null

Шаг 3. Анализируем access.log

# Сколько запросов
sudo wc -l /var/log/nginx/access.log

# Топ запрашиваемых URL
sudo awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head

# Коды ответов
sudo awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

# Есть ли 404?
sudo awk '$9 == 404 {print $7}' /var/log/nginx/access.log

Шаг 4. Проверяем error.log

sudo tail -10 /var/log/nginx/error.log

Вы должны увидеть ошибку для /not-found-page — nginx не нашёл файл и вернул 404.

Шаг 5. Проверяем ротацию

ls -la /var/log/nginx/
cat /etc/logrotate.d/nginx

Справочник команд

journalctl (расширение)

Команда Описание
journalctl -p err Только ошибки и критичнее
journalctl -p warning Предупреждения и критичнее
journalctl -b Логи с момента загрузки
journalctl --disk-usage Размер журнала на диске
sudo journalctl --vacuum-size=500M Ограничить размер 500 МБ
sudo journalctl --vacuum-time=7d Удалить записи старше 7 дней

Анализ логов nginx

Команда Описание
sudo wc -l /var/log/nginx/access.log Количество запросов
sudo awk '{print $1}' access.log | sort | uniq -c | sort -rn | head Топ IP-адресов
sudo awk '{print $9}' access.log | sort | uniq -c | sort -rn Распределение по кодам
sudo awk '$9 == 404 {print $7}' access.log Запросы с ошибкой 404
sudo awk '{print $7}' access.log | sort | uniq -c | sort -rn | head Популярные страницы
sudo tail -20 /var/log/nginx/error.log Последние ошибки

Практические задания

  1. Выполните journalctl -p err --since today. Есть ли ошибки в системе? Если есть — от каких сервисов?

  2. Проверьте размер системного журнала (journalctl --disk-usage). Если он больше 500 МБ, выполните sudo journalctl --vacuum-size=500M.

  3. Посмотрите последние 20 строк /var/log/nginx/access.log. Определите для каждой строки: IP клиента, запрошенный URL и код ответа.

  4. Подсчитайте количество запросов в access.log. Выведите топ-5 IP-адресов по количеству обращений. Есть ли подозрительно активные?

  5. Выведите распределение кодов ответа. Какой процент запросов завершился кодом 200? Есть ли коды 404 или 500?

  6. Найдите все запросы к /wp-login.php в access.log. С каких IP-адресов они приходят? (Подсказка: grep 'wp-login' access.log | awk '{print $1}' | sort | uniq -c.)

  7. Посмотрите error.log. Найдите ошибки типа No such file or directory. Какие файлы запрашивают, но не находят?

  8. Посмотрите конфигурацию ротации логов nginx (cat /etc/logrotate.d/nginx). Сколько архивов хранится? Как часто происходит ротация? Есть ли уже ротированные файлы в /var/log/nginx/?

Итоги

В этой главе вы:

  • расширили знания о journalctl: приоритеты сообщений, фильтрация ошибок, управление размером журнала;
  • разобрали формат логов nginx (access.log и error.log);
  • научились анализировать access.log с помощью coreutils: находить самые активные IP, популярные страницы, коды ошибок;
  • освоили диагностику по error.log: поиск ошибок прав доступа, отсутствующих файлов и проблем с PHP-FPM;
  • познакомились с ротацией логов (logrotate).

Часть II завершена. Вы прошли путь от установки пакетов до полноценного веб-сервера с мониторингом. В следующей части мы перейдём к основам DevOps — начнём с Git (контроль версий), а затем освоим Docker, Docker Compose и базовый CI/CD.