Посчитать ошибки по сервисам в awk: агрегируем журнал за один проход

Из выгрузки журнала нужно получить короткий отчёт: сколько событий пришло от каждого сервиса и сколько среди них ошибок. Цепочка grep по каждому имени перечитывает файл много раз, а имена сервисов приходится сначала извлечь. awk позволяет пройти вход один раз, накапливая показатели по ключу в массивах.

Сначала договоримся о формате. В учебной выгрузке каждая строка содержит ровно два поля: имя сервиса и статус OK либо ERROR, разделённые пробельными символами. Это уже подготовленный текстовый отчёт. Для JSON-журнала сначала нужен парсер JSON, иначе пробелы внутри сообщения будут создавать ложные поля и случайные статусы.

Считаем две разные величины

Пример создаёт events.txt и выводит итог в стабильном порядке. Выполняйте его в отдельной рабочей папке.

printf '%s\n' 'api OK' 'worker ERROR' 'api ERROR' 'api OK' > events.txt
awk '
NF != 2 || ($2 != "OK" && $2 != "ERROR") {
    bad++
    next
}
{
    total[$1]++
    if ($2 == "ERROR") errors[$1]++
}
END {
    if (bad) exit 2
    for (service in total)
        printf "%s\t%d\t%d\n", service, total[service], errors[service] + 0
}' events.txt > counts.unsorted && LC_ALL=C sort counts.unsorted

Для api получится три события и одна ошибка, для worker — одно событие и одна ошибка. Последнее слагаемое +0 делает отсутствующий элемент массива числовым нулём. Сервис без ERROR всё равно попадёт в результат, потому что итоговый цикл проходит по total, а не по errors.

Число событий и число ошибок намеренно стоят в разных колонках. Если позже добавите процент, знаменателем будет total конкретного сервиса. Деление на число строк всего файла ответит на другой вопрос: доля данного сервиса в общей массе ошибок. Такое смешение легко получить в лаконичном shell-отчёте, поэтому назовите колонки до добавления арифметики.

Ошибочная строка не должна исчезнуть незаметно

Здесь любая строка неправильного формата увеличивает bad, а итоговый выход получает статус 2. Итог не печатается, и sort не запускается благодаря &&. Файл counts.unsorted будет создан перенаправлением даже при ошибке; вызывающий код должен ориентироваться на статус команды, а не на существование файла.

Для эксплуатации полезно дополнить форматный отчёт номерами плохих строк. Это отдельный диагностический поток: он не должен смешиваться с табличным stdout. В GNU awk можно писать в /dev/stderr; если нужен переносимый вариант, формируйте отдельный файл диагностики либо запускайте отдельную проверку формата до агрегации. Выбор зависит от среды, где будет жить скрипт.

В примере пустые строки считаются неправильными. Если экспорт по контракту содержит пустые разделители, явно пропустите их до проверки NF. Важно согласовать это правило с производителем выгрузки, иначе внезапно усечённая строка тоже начнёт выглядеть допустимой пустотой.

Почему сортировка идёт после подсчёта

Порядок обхода ассоциативного массива awk не задаёт стабильного порядка отчёта. Без sort одинаковые показатели могут печататься в разной последовательности, создавая шум в diff и письмах. Сортировка короткого итога обычно дешевле, чем предварительная сортировка всего журнала. LC_ALL=C делает порядок технических идентификаторов воспроизводимым между окружениями.

Массивы растут по числу различных сервисов, а не по числу событий. Это хороший выбор для длинной выгрузки с ограниченным набором имён. Если ключом случайно сделать request_id, картина меняется: почти каждая строка создаёт новый элемент. Перед переносом на большой вход проверьте, что выбранный ключ действительно имеет нужный уровень детализации.

В курсе Linux и Bash автоматизация связана с диагностикой сервиса и чтением журналов. Откройте программу и продолжите к повторяемому сбору отчётов: с проверкой формата, обработкой ошибок и результатом, который удобно сравнивать между запусками.

Ещё по теме

Продолжить на практике

  • Linux и Bash для DevOps-инженеров ↗

    Linux для работы с серверами: процессы, права, systemd, сеть, диски и логи. Автоматизируйте повседневные задачи на Bash и разберите типичные сбои сервиса — от занятого порта до переполненного диска.