Как отфильтровать только уникальные ошибки в нескольких логах, используя grep?

Вопрос или проблема

Я пытаюсь использовать следующий шаблон на Ubuntu:

grep -Eri "warning|error|critical|severe|fatal" --color=auto

чтобы найти соответствующие ошибки в различных .log файлах рекурсивно в /var/log и его подпапках.

Проблема, с которой я сталкиваюсь, заключается в том, что это приводит к тому, что десятки тысяч строк совпадений печатаются при выполнении выражения. Я хотел бы как-то отфильтровать их хотя бы одним из следующих способов:

  1. Печатать, но пропускать совпадение, если существует более 3 одинаковых совпадений
  2. Показывать только уникальные совпадения (т.е. печатать одно из каждой найденной строки)

Могу ли я сделать это, передавая вывод в что-то? В настоящее время просмотр каждого лога на наличие ошибок занимает невероятно много времени, вот почему я пытаюсь это сделать. Но выражение, которое я использую, печатает так много информации, что оно само по себе тоже непригодно. Я пробовал передавать в ‘less’, но это убирает выделение, что затрудняет чтение, и не решает проблему с тем, что вывод слишком велик.

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

Вот пример строки ошибки в одном из множества логов, которые я ищу:

./artifactory/artifactory-service.log:20:2021-07-20T08:45:30.248Z [jfrt ] [ERROR] [.j.a.c.g.GrpcStreamObserver:97] [c-default-executor-1] - refreshing affected platform config stream - got an error

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

Кроме того, в связи с тем, как даты перечислены в логе. Было бы здорово отфильтровать нужные только конкретные даты, как я могу это сделать? Фильтрация по дате значительно ограничила бы вывод.

Это может быть:

find . -name '*.log' -type f -exec perl -lne '
  if(/^\S++ (.*(?:warning|error|critical|severe|fatal).*)/i && ++$seen{$1} <= 3) {
    print "$ARGV:$_"
  }
  undef %seen if eof' {} +

Где мы пропускаем ведущую метку времени с помощью \S++ и записываем количество вхождений остальной части строки в хэш %seen и пропускаем печать после третьего (сбрасывая счетчик для каждого нового файла).

Сопоставление с конкретной датой - это просто выполнение сопоставления по регулярному выражению на метке времени или, поскольку ваши метки времени используют стандартный формат ISO8601, сделать некоторое сравнение строк для диапазона дат:

find . -name '*.log' -type f -exec perl -lne '
  if(/^(\S++) (.*(?:warning|error|critical|severe|fatal).*)/i &&
     $1 ge "2021-07-19" && $1 lt "2021-07-21" && ++$seen{$2} <= 3) {
    print "$ARGV:$_"
  }
  undef %seen if eof' {} +

Ответ или решение

Чтобы отфильтровать только уникальные ошибки из нескольких логов с использованием grep на Ubuntu, можно использовать комбинацию команд и утилит, таких как find, grep, и awk или perl. Давайте подробно рассмотрим, как это сделать, учитывая ваши требования.

Задача

Вы пытались найти ошибки в логах, используя следующую команду:

grep -Eri "warning|error|critical|severe|fatal" --color=auto /var/log

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

Решение

1. Отображение до 3 совпадений для каждой уникальной строки

Одним из способов решения вашей задачи является использование Perl для анализа логов, как в приведенном ниже примере. Этот подход позволяет отслеживать количество совпадений и печатать не более трех строк с одинаковым содержанием.

find /var/log -name '*.log' -type f -exec perl -lne '
  if(/^\S++ (.*(?:warning|error|critical|severe|fatal).*)/i && ++$seen{$1} <= 3) {
    print "$ARGV: $_"
  }
  undef %seen if eof' {} +

Разбор команды:

  • find /var/log -name '*.log' -type f: Находит все файлы .log в каталоге /var/log и его подкаталогах.
  • -exec perl -lne ... {} +: Для каждого найденного файла выполняет указанный скрипт Perl.
  • if(/^\S++ (.*(?:warning|error|critical|severe|fatal).*)/i && ++$seen{$1} <= 3): Проверяет строку на наличие определенных ключевых слов. Используется хэш %seen для учета количества совпадений. Если совпадение уже достигло 3 раз, оно не будет напечатано.
  • print "$ARGV: $_": Выводит путь к файлу и строку, которая соответствует условиям.

2. Фильтрация по диапазону дат

Если вы хотите фильтровать по конкретным датам, вы можете изменить регулярное выражение для проверки диапазона дат. Например, если необходимо отобразить только ошибки, произошедшие между 19 и 21 июля 2021 года, можно использовать следующий скрипт:

find /var/log -name '*.log' -type f -exec perl -lne '
  if(/^(\S++) (.*(?:warning|error|critical|severe|fatal).*)/i &&
     $1 ge "2021-07-19" && $1 lt "2021-07-21" && ++$seen{$2} <= 3) {
    print "$ARGV: $_"
  }
  undef %seen if eof' {} +

Объяснение изменений:

  • ($1 ge "2021-07-19" && $1 lt "2021-07-21"): Здесь происходит сравнение даты (первое слово в строке) с заданным диапазоном. Это сравнение будет возвращать true только для строк, соответствующих этому диапазону.

Заключение

С помощью этой комбинации find, perl и регулярных выражений можно эффективно фильтровать и обрабатывать логи, чтобы извлечь необходимые данные с минимальным шумом. Этот метод позволяет вам не только контролировать количество выводимых совпадений, но и ограничивать их по времени, что значительно упрощает процесс анализа логов. Это особенно полезно в ситуациях с большим количеством записей, где необходимо быстро находить и устранять проблемы.

Оцените материал
Добавить комментарий

Капча загружается...