Linux считывает файл журнала и фильтрует, чтобы получать сообщения журнала одного типа только один раз.

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

grep -E 'level=error' server.log | sort --unique

Но это также дает мне «информационные» и «предупреждающие» сообщения.

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

grep 'error' server.log | uniq -f 1

аргумент -f 1 должен пропустить поле метки времени, поскольку оно всегда будет уникальным.

Например, мои сообщения журнала:

.
.
.
11-03-2020 11:53:32" level=info msg="Starting up" file="etc/load/startwith.txt"
11-03-2020 11:53:33" level=info msg="Started" file="etc/load/startwith.txt"
11-03-2020 11:54:29" level=warning msg="Some fields missing" file="etc/load/startwith.php" 
11-03-2020 11:54:47" level=info msg="Started the process" file="etc/load/startwith.php" 
11-03-2020 11:54:51" level=info msg="Connecting to database" file="etc/db/dbinfo.php" 
11-03-2020 11:54:53" level=error msg="Database connection failed" file="etc/db/dbinfo.php"  
11-03-2020 13:26:22" level=info msg="Started back-up process" file="etc/load/startwith.php" 
11-03-2020 13:26:23" level=info msg="Starting up" file="etc/load/startwith.txt"
11-03-2020 13:26:26" level=error msg="Start up failed" file="etc/db/startwith.php" 
11-03-2020 13:26:27" level=info msg="Starting up" file="etc/load/startwith.txt"
11-03-2020 13:26:31" level=error msg="Start up failed" file="etc/db/startwith.php"
11-03-2020 13:26:32" level=info msg="Starting up" file="etc/load/startwith.txt"
11-03-2020 13:26:35" level=warning msg="Duplicate fields found" file="etc/load/startwith.php" 
11-03-2020 13:26:36" level=info msg="Started the process" file="etc/load/startwith.php" 
11-03-2020 13:26:37" level=info msg="Connecting to database" file="etc/db/dbinfo.php"
11-03-2020 13:26:38" level=info msg="Success. Connected to the database" file="etc/db/db-success.php"
11-03-2020 13:26:38" level=info msg="Inserting data to database" file="etc/db/dboperation.php"
11-03-2020 13:26:39" level=warning msg="Null fields found" file="etc/db/dboperation.php"
11-03-2020 13:26:39" level=info msg="Data inserted" file="etc/db/dboperation.php"
11-03-2020 13:26:39" level=info msg="Disconnected" file="etc/db/dboperation.php"
11-03-2020 13:26:43" level=info msg="Inserting data to database" file="etc/db/dboperation.php"
11-03-2020 13:26:43" level=error msg="Required data missing" file="etc/db/dboperation.php"
11-03-2020 13:26:44" level=info msg="Inserting data to database" file="etc/db/dboperation.php"
11-03-2020 13:26:44" level=error msg="Required data missing" file="etc/db/dboperation.php"
.
.
.

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

11-03-2020 11:54:53" level=error msg="Database connection failed" file="etc/db/dbinfo.php" 
11-03-2020 13:26:31" level=error msg="Start up failed" file="etc/db/startwith.php"
11-03-2020 13:26:44" level=error msg="Required data missing" file="etc/db/dboperation.php"

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


person Ros    schedule 12.03.2020    source источник
comment
Отвечает ли это на ваш вопрос? awk для печати уникальных строк на основе поля столбца:   -  person KamilCuk    schedule 12.03.2020
comment
Извините, это не отвечает на мои вопросы, поскольку он сравнивает (следовательно, исключает) точные дубликаты строк, включая метку времени, тогда как я хочу сравнивать только после метки времени.   -  person Ros    schedule 12.03.2020
comment
Итак... awk '!seen[$4]++'... Он использует , в качестве разделителя полей и использует первое поле. Поэтому используйте другой разделитель полей и другое поле....   -  person KamilCuk    schedule 12.03.2020


Ответы (2)


Только что:

awk '/error/ && !seen[$4]++'

Или используйте кавычки в качестве разделителя, чтобы включить полное сообщение msg="this text", например:

awk -F'"' '/error/ && !seen[$3]++'

Вы можете сделать это без awk - grep error, затем nl числовых строк, затем sort -u отсортировать уникальные по полю с помощью msg=, затем повторно отсортировать по номерам строк и удалить номера строк с помощью cut. В качестве альтернативы msg="part" можно извлечь с помощью sed, чтобы упростить токенизацию sort. Как это:

grep error | sed 's/.* msg="\([^"]*\)"/\1\t&/' | nl -w1 |
sort -t $'\t' -u -k2,2 | sort -n -k1 | cut -f3-
person KamilCuk    schedule 12.03.2020
comment
не могли бы вы описать, что делает !seen[$4]++? - person Ros; 12.03.2020
comment
Это описано здесь - person KamilCuk; 12.03.2020
comment
Спасибо, Камиль, если бы я хотел отфильтровать все три типа сообщений (информация, предупреждение, ошибка) в большом файле журнала, как бы я изменил эту команду '/error/ && !seen[$4]++'? - person Ros; 12.03.2020
comment
awk '/error/' похож на grep error. Просто удалите часть '/error/ && - person KamilCuk; 12.03.2020

Команда uniq -f 1 разбивается пробелами. Это не имеет никакого эффекта, потому что строка после первого пробела, времени, уникальна.

Вместо этого используйте uniq -s 20. Это игнорирует первые 20 символов.

Это работает для отметок времени, поскольку в большинстве случаев они имеют фиксированную длину.

person ceving    schedule 12.03.2020