0Pricing
DevOps Bootcamp · Урок

Масштабный разбор веб-журналов и журналов приложений

Извлекайте коды состояния, задержки и поля клиентов из журналов доступа с помощью grep, cut и awk

«Масштабный разбор веб-журналов и журналов приложений» — бесплатный урок DevOps Bootcamp на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения DevOps Bootcamp, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс DevOps Bootcamp содержит 4 уроков всего.

Что такое журнал доступа к веб-серверу

Каждый HTTP-сервер — Apache, Nginx, Caddy — записывает одну строку в журнал доступа для каждого запроса. Понимание структуры этих строк — основа любого анализа журналов.

Типичная строка объединённого формата журнала (CLF) выглядит так:

  • IP-адрес клиента — кто выполнил запрос
  • Временная метка — когда это произошло
  • Строка запроса — метод, путь и протокол
  • Код состояния — ответ HTTP (200, 404, 500…)
  • Отправленные байты — размер тела ответа
  • Referer — исходная страница
  • User-Agent — строка браузера или бота

Пример строки из /var/log/nginx/access.log:

192.168.1.10 - alice [11/Jun/2026:14:32:01 +0000] "GET /api/orders HTTP/1.1" 200 1482 "-" "curl/7.88.1"

При большом объёме трафика эти файлы разрастаются до миллионов строк в день. Цель этого урока — эффективно извлекать из них поля, фильтровать и агрегировать их с помощью стандартных инструментов BASH.

Просмотр текущего журнала с помощью tail и grep

Перед созданием любого конвейера изучите журнал, чтобы понять его структуру. tail позволяет наблюдать поток данных в реальном времени, а grep сразу отбирает нужные строки.

Распространённые варианты:

  • tail -n 1000 access.log — последние 1000 строк
  • tail -f access.log — отслеживание в реальном времени
  • tail -f access.log | grep '" 5' — только ошибки 5xx по мере их появления

Важно помнить, что grep сопоставляет шаблон со всей строкой, поэтому существенное значение имеет привязка шаблона. Сопоставление ' 500 ' (с пробелами) предотвращает случайное совпадение с путем URL, содержащим строку 500.

#!/usr/bin/env bash
# Watch only HTTP 5xx errors arriving in real time
tail -f /var/log/nginx/access.log \
  | grep --line-buffered '" 5[0-9][0-9] '

Извлечение кода состояния с помощью cut

cut разделяет каждую строку по разделителю и выводит выбранные поля. В объединённом формате журнала код состояния находится в поле 9 при разделении по пробелам, но кавычки вокруг строки запроса означают, что безопаснее вести отсчёт от известного разделителя.

Надёжный приём: поскольку строка запроса всегда заключена в кавычки, код состояния всегда является первым элементом после закрывающей кавычки поля запроса. Команда cut -d'"' -f3 выделяет всё после кавычки запроса, а затем вторая команда cut -d' ' -f2 выбирает код состояния.

Такое двухэтапное применение cut — классический приём для журналов CLF: быстро и без внешних зависимостей.

#!/usr/bin/env bash
# Print only the HTTP status code from each log line
# Input format: ... "GET /path HTTP/1.1" 200 1482 ...
cut -d'"' -f3 /var/log/nginx/access.log \
  | cut -d' ' -f2 \
  | sort \
  | uniq -c \
  | sort -rn

Подсчёт кодов состояния с помощью awk

awk мощнее, чем cut, поскольку может сохранять состояние между строками. Идиоматичный способ подсчёта вхождений — ассоциативный массив с ключами, соответствующими интересующему значению.

В CLF поле $9 (нумерация начинается с 1, разделитель — пробел) содержит код состояния. awk обрабатывает каждую строку, увеличивает счётчик, а затем выводит отсортированную сводку в блоке END.

Почему стоит предпочесть awk конструкции cut | sort | uniq -c? Потому что awk выполняет всё за один проход, не сортируя предварительно весь файл, что критически важно, когда журнал занимает сотни гигабайт.

#!/usr/bin/env bash
# Count HTTP status codes in a single awk pass
awk '{ count[$9]++ }
END {
  for (status in count)
    printf "%6d  %s\n", count[status], status
}' /var/log/nginx/access.log \
  | sort -rn

Фильтрация ошибок и извлечение IP-адресов клиентов

Одна из самых распространённых операционных задач — определить, какие IP-адреса клиентов создают больше всего ошибок. Здесь объединяются фильтрация (только строки с ошибками) и извлечение поля (IP-адрес в поле 1).

Стратегия конвейера:

  • Используйте awk, чтобы за один шаг отфильтровать строки по диапазону кодов состояния и извлечь IP-адрес — не выполняйте отдельный проход с помощью grep
  • Передайте результат в sort | uniq -c | sort -rn | head, чтобы быстро получить список лидеров

Этот шаблон достаточно быстр, чтобы обрабатывать файл журнала размером 10 ГБ на одном сервере, не загружая файл в память.

#!/usr/bin/env bash
# Top 10 IPs generating HTTP 4xx or 5xx errors
awk '$9 ~ /^[45][0-9][0-9]$/ { print $1 }' \
    /var/log/nginx/access.log \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -10

Разбор задержки ответа в журналах приложений

Серверы приложений (Rails, Gunicorn, Express с morgan и другие) часто записывают длительность запроса. Nginx можно настроить на добавление $request_time в качестве дополнительного поля в конце каждой строки.

Пример пользовательского формата журнала Nginx в nginx.conf:

log_format timed '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" rt=$request_time';

Если задержка записывается в журнал, можно использовать awk, чтобы вычислять среднее значение, максимум и приближённые процентили для миллионов запросов, не загружая данные в базу данных.

#!/usr/bin/env bash
# Compute average and max request_time from Nginx timed log
# Assumes last field is rt=<seconds> e.g. rt=0.042
awk '{
  # Extract numeric value after rt=
  n = split($NF, a, "=")
  if (n == 2 && a[1] == "rt") {
    t = a[2] + 0
    sum += t
    count++
    if (t > max) max = t
  }
}
END {
  if (count > 0)
    printf "Requests: %d  Avg: %.4fs  Max: %.4fs\n", count, sum/count, max
}' /var/log/nginx/timed_access.log

Построение гистограммы задержки с помощью awk

Одно среднее значение скрывает задержки в хвосте распределения. Гистограмма показывает распределение: выполняются ли большинство запросов быстро, а несколько — очень медленно (длинный хвост), или распределение равномерно.

Секрет заключается в том, чтобы отнести каждое значение к округлённому диапазону с помощью целочисленной арифметики внутри awk. Умножение на 1000 (перевод секунд в миллисекунды) с последующим целочисленным делением даёт аккуратные границы диапазонов.

В результате получается текстовая гистограмма, которую можно читать прямо в терминале. Для быстрого исследования это часто быстрее, чем отправлять данные в Grafana.

#!/usr/bin/env bash
# Latency histogram (50ms buckets) from Nginx timed log
awk '{
  n = split($NF, a, "=")
  if (n == 2 && a[1] == "rt") {
    ms = int(a[2] * 1000)      # convert to ms
    bucket = int(ms / 50) * 50 # round down to 50ms boundary
    hist[bucket]++
  }
}
END {
  for (b in hist)
    printf "%6dms  %d\n", b, hist[b]
}' /var/log/nginx/timed_access.log \
  | sort -n

Извлечение User-Agent и обнаружение ботов

Поле User-Agent (поле 6 при разделении по ") идентифицирует клиентов. Сканеры, сборщики данных и вредоносные боты часто искажают метрики и увеличивают число ошибок. Их фильтрация даёт более точное представление о трафике реальных пользователей.

Распространённые признаки ботов: bot, crawler, spider, curl, python-requests, Googlebot, Bingbot.

Используйте grep -iv (инвертированный поиск без учёта регистра), чтобы исключить известных ботов, или используйте awk, чтобы разделить строку по " и сопоставить поле UA напрямую.

#!/usr/bin/env bash
# Count top 15 User-Agent strings, excluding known bots
awk -F'"' '{ print $6 }' /var/log/nginx/access.log \
  | grep -iv -e 'bot' -e 'crawler' -e 'spider' -e 'curl' \
              -e 'python' -e 'wget' -e 'Go-http-client' \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -15

Агрегирование трафика по конечным точкам

Знание о том, какие конечные точки получают больше всего трафика и создают больше всего ошибок, помогает расставлять приоритеты при оптимизации и планировании пропускной способности. Путь запроса находится внутри заключённого в кавычки поля запроса.

Разделите строку по ", возьмите поле 2 (строку запроса), затем удалите метод и протокол, чтобы выделить путь. Для API с параметрами пути, такими как /users/12345, может также потребоваться нормализовать идентификаторы до вида /users/:id с помощью sed или более сложного шаблона awk.

#!/usr/bin/env bash
# Top 20 requested endpoints (method + path, no query string)
awk -F'"' '{ print $2 }' /var/log/nginx/access.log \
  | awk '{ print $1, $2 }' \
  | sed 's|/[0-9][0-9]*\b|/:id|g' \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -20

Связывание ошибок с конечными точками с помощью awk

Самый мощный анализ за один проход одновременно объединяет несколько полей: конечную точку, код состояния и, при необходимости, задержку. Ассоциативные массивы awk с ключами, составленными из нескольких значений, делают такой анализ простым и быстрым.

Приведённый ниже шаблон за один проход подсчитывает ошибки 5xx для каждой конечной точки: временные файлы не нужны, промежуточная сортировка выполняется только в самом конце. Такой подход используется в промышленных скриптах наблюдаемости, когда нужно получить ответ менее чем за минуту при работе с большим журналом.

#!/usr/bin/env bash
# Count 5xx errors per endpoint path in a single pass
awk -F'"' '{
  # $2 = request line e.g. "GET /api/orders HTTP/1.1"
  # $0 in original space-split: $9 = status
  split($0, fields, " ")
  status = fields[9]
  if (status ~ /^5/) {
    split($2, req, " ")
    path = req[2]
    # Normalise numeric IDs
    gsub(/\/[0-9]+/, "/:id", path)
    errors[path]++
  }
}
END {
  for (p in errors)
    printf "%6d  %s\n", errors[p], p
}' /var/log/nginx/access.log \
  | sort -rn \
  | head -20

Обработка архивированных и сжатых журналов

На большинстве серверов журналы ротируются ежедневно. Старые файлы сжимаются с помощью gzip и получают имена access.log.1.gz, access.log.2.gz и так далее. Стандартные инструменты не могут читать их напрямую, но есть два удобных подхода:

  • zcat — распаковывает данные в стандартный вывод, после чего их можно передать в конвейер
  • zgrep — выполняет поиск grep непосредственно внутри файлов gzip, не распаковывая их

Чтобы проанализировать полную неделю журналов, включающую как несжатые, так и сжатые файлы, используйте подстановку процесса или объедините данные с помощью zcat. Приведённый ниже фрагмент обрабатывает последние 7 ротированных файлов и текущий журнал в одном вызове awk — временные файлы не требуются.

#!/usr/bin/env bash
# Aggregate status codes across a week of rotated logs
# Handles both plain and gzip-compressed rotation files

LOG_DIR="/var/log/nginx"

{
  cat  "${LOG_DIR}/access.log" 2>/dev/null
  zcat "${LOG_DIR}/access.log".*.gz 2>/dev/null
} | awk '
{ count[$9]++ }
END {
  for (s in count)
    printf "%6d  %s\n", count[s], s
}' | sort -rn

В каком поле awk находится код состояния HTTP в объединённом формате журнала?

Вы пишете однострочную команду awk, чтобы отфильтровать только ответы HTTP 4xx из стандартного журнала доступа Nginx в объединённом формате журнала (поля разделены пробелами, строка запроса заключена в кавычки). Какой номер поля правильно соответствует коду состояния HTTP?

Итоги урока: конвейеры анализа журналов

В этом уроке Вы создали полный набор инструментов для анализа журналов веб-приложений и приложений в больших масштабах, используя только стандартные утилиты BASH.

Основные рассмотренные приёмы:

  • Сначала изучите структуру — объединённый формат журнала имеет предсказуемую структуру полей; зная её, Вы сможете надёжно разделять записи с помощью cut -d'"' или ссылок на поля в awk.
  • Извлечение кода состояния — awk '{ count[$9]++ }' подсчитывает все коды за один проход; используйте фильтр $9 ~ /^5/ для ошибок сервера.
  • Анализ задержки — разбирайте пользовательское поле rt= с помощью awk, чтобы вычислять средние и максимальные значения, а также интервалы гистограммы без внешних инструментов.
  • Анализ клиентов и ботов — разделяйте по " с помощью -F'"', чтобы получить доступ к полю User-Agent; перед объединением пропускайте данные через grep -iv, исключая ботов.
  • Нормализация конечных точек — используйте gsub(/\/[0-9]+/, "/:id") внутри awk, чтобы сворачивать пути с параметрами перед подсчётом.
  • Ротация журналов — объединяйте cat и zcat во вложенной оболочке, чтобы передать все файлы ротации в один проход конвейера.

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

Часто задаваемые вопросы

Урок «Масштабный разбор веб-журналов и журналов приложений» бесплатный?

Да — полный текст урока «Масштабный разбор веб-журналов и журналов приложений» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс DevOps Bootcamp, подпишись на CoddyKit PRO. Курс DevOps Bootcamp содержит 4 уроков всего.

Чему я научусь в уроке «Масштабный разбор веб-журналов и журналов приложений»?

Извлекайте коды состояния, задержки и поля клиентов из журналов доступа с помощью grep, cut и awk Ты практикуешь DevOps Bootcamp с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать DevOps Bootcamp?

Предыдущий опыт не требуется. DevOps Bootcamp на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Масштабный разбор веб-журналов и журналов приложений»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке DevOps Bootcamp?

Да. Каждый урок DevOps Bootcamp включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Масштабный разбор веб-журналов и журналов приложений
  2. Отслеживание журналов в реальном времени и потоковые оповещения
  3. Запросы к journald с journalctl в скриптах
  4. Вычисление показателей и гистограмм из потоков журналов
← Назад к DevOps Bootcamp