Парсинг логов: разбор журналов серверов и приложений с примерами

Парсинг логов серверов и приложений: форматы access/error-логов, регулярные выражения, готовые инструменты и примеры разбора.

КP
Команда Parsing.agency
Сбор данных под задачи бизнеса
Опубликовано: 22 марта 2025

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

Чем логи отличаются от обычного текста

Главная особенность — в пределах одного источника формат строки строго стабилен. Веб-сервер пишет каждую строку по одному шаблону, и это позволяет разбирать миллионы строк одним регулярным выражением. Но есть и осложнения. Логи бывают огромными — гигабайты в день, поэтому их нельзя читать целиком в память, только потоково. Они ротируются — старые файлы переименовываются и сжимаются в .gz, и парсер должен уметь читать сжатые архивы. И в них встречаются многострочные записи — например, трассировка ошибки (stack trace) занимает десятки строк, относящихся к одному событию.

Стандартные форматы веб-логов

Чаще всего разбирают access-логи Nginx и Apache в «combined» формате. Одна строка выглядит так:

code
192.168.1.10 - - [01/Jun/2026:13:55:36 +0300] "GET /product/101 HTTP/1.1" 200 4523 "https://example.com/" "Mozilla/5.0 ..."

Здесь по порядку: IP-адрес, дата и время, метод и путь запроса, код ответа, размер ответа, реферер и User-Agent. Поскольку формат фиксирован, его удобно разобрать одним регулярным выражением с именованными группами.

Python

Базовый и самый гибкий подход — регулярное выражение плюс потоковое чтение. Именованные группы делают результат читаемым.

python
import re
import gzip

LOG_RE = re.compile(
    r'(?P<ip>\S+) \S+ \S+ \[(?P<time>[^\]]+)\] '
    r'"(?P<method>\S+) (?P<path>\S+) [^"]*" '
    r'(?P<status>\d{3}) (?P<size>\S+) '
    r'"(?P<referer>[^"]*)" "(?P<agent>[^"]*)"'
)

def open_log(path):
    # прозрачно читаем и обычные, и сжатые файлы
    return gzip.open(path, "rt") if path.endswith(".gz") else open(path, "r")

with open_log("access.log") as f:
    for line in f:
        m = LOG_RE.match(line)
        if not m:
            continue
        row = m.groupdict()
        if row["status"] != "200":
            print(row["status"], row["method"], row["path"], row["ip"])

Когда нужно не просто разобрать, а проанализировать — посчитать топ страниц, распределение кодов ответа, трафик по часам — разобранные строки удобно сложить в pandas и работать с ними как с таблицей.

python
import pandas as pd

records = [m.groupdict() for line in open_log("access.log")
           if (m := LOG_RE.match(line))]
df = pd.DataFrame(records)

# топ-10 самых запрашиваемых страниц
print(df["path"].value_counts().head(10))

# доля ошибок 5xx
errors = df[df["status"].str.startswith("5")]
print(len(errors) / len(df))

Отсюда же результат легко выгрузить в CSV или Excel для отчёта.

Командная строка: быстрый разбор без кода

Для разовой задачи часто быстрее всего обойтись утилитами Unix. awk разбивает строку на поля и считает на лету.

bash
# топ-10 IP по числу запросов
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head

# все запросы, завершившиеся ошибкой 404
awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -rn

# суммарный объём отданного трафика (9-е поле — размер)
awk '{sum += $10} END {print sum/1024/1024 " MB"}' access.log

Связка grep + awk + sort + uniq решает большинство ad-hoc вопросов к логам и не требует ничего писать.

Готовые анализаторы

Если задача — общая аналитика веб-трафика, а не извлечение конкретных полей, изобретать парсер не нужно. GoAccess читает access-логи Nginx/Apache и строит интерактивный отчёт в терминале или в браузере в реальном времени. Для сложных пайплайнов с разнородными источниками применяют стек на основе Logstash с его наборами шаблонов grok — это, по сути, библиотека готовых именованных регулярных выражений под популярные форматы логов.

Структурированные логи (JSON-логи)

Современные приложения всё чаще пишут логи не строками, а в виде JSON — по объекту на строку (формат JSON Lines). Такой лог разбирать намного проще и надёжнее: не нужны хрупкие регулярные выражения, каждая строка просто десериализуется.

python
import json

with open("app.log") as f:
    for line in f:
        event = json.loads(line)
        if event.get("level") == "ERROR":
            print(event["timestamp"], event["message"])

Если есть возможность влиять на то, как пишутся логи, переход на JSON Lines радикально упрощает их последующий разбор — это стоит закладывать в проект заранее.

С чем приходят сложности

Самые частые проблемы — это разный формат времени (логи из разных систем используют разные часовые пояса и шаблоны дат, которые нужно приводить к единому виду), многострочные ошибки (stack trace ломает построчный разбор, и записи приходится «склеивать» по признаку начала новой записи) и объём (анализ дневного лога в гигабайты требует потоковой обработки и фильтрации на лету, а не загрузки в память).

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