Показать только записи последнего часа файла журнала

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

У меня есть большой файл журнала access.log с записями, например:

192.11.111.111 - - [05/Mar/2021:00:00:02 +0100] "GET ..."
192.250.14.80 - - [05/Mar/2021:00:00:09 +0100] "GET ..."
12.249.66.42 - - [05/Mar/2021:00:00:13 +0100] "GET ..."

Как я могу получить/отфильтровать записи только за последний час?

#!/bin/bash

age() { python -c '
import sys
from datetime import datetime
print(int((datetime.now() - datetime.strptime(sys.argv[1], sys.argv[2])).seconds))
' "$@"
}

tac access.log | while IFS= read line; do
    date=$(cut -d' ' -f4 <<< "$line")
    age=$(age "$date" "[%d/%b/%Y:%H:%M:%S")
    [ $age -gt 3600 ] && break
    printf '%s\n' "$line"
done

Объяснение:

  • Создать функцию для получения возраста строки даты, для этого я использую python datetime модуль.
  • Затем перебрать строки файла в обратном порядке с помощью tac log | while ...
  • Получить строку даты с помощью cut.
    • нужно удалить -d' ', если ваш разделитель – табуляция вместо пробела
    • или использовать awk '{print $4}' <<< "$line" вместо этого
    • или использовать read ip some thing date tz else <<< "$line").
  • Получить возраст с помощью функции, определенной на первом шаге
  • Когда секунды > 3600 (=1 час), остановить цикл (break)
  • В противном случае, напечатать строку (или сделать что угодно)

Некоторые дополнительные заметки:

  • Поскольку я игнорирую часовой пояс (+0100), это предполагает, что мы находимся в том же часовом поясе, что и файл журнала. Если у вас есть файлы журналов из других часовых поясов, вам, возможно, потребуется улучшить функцию age. Смотрите здесь и читайте дату как cut -d' ' -f4,5.

  • Вы, очевидно, можете сделать всё это с помощью python, однако чтение файла в обратном порядке, не читая весь файл кажется не таким уж простым, и мне просто нравится tac.

  • Вы можете использовать date и bc или что-то подобное для вычисления возраста (как это сделано здесь), но поскольку ваш формат даты не поддерживается “из коробки”, это будет трудно. ИМХО, datetime.strptime идеально подходит для этого.

  • Хорошая вещь в функции age: она очень повторно используема для любых видов задач. Просто вызовите её так же, как strptime может её прочитать и она документирована здесь.

    age "date_string" "format"
    

Сначала извлеките строки даты/времени из квадратных скобок:

$ awk -F'[][]' '{print $2}' access.log
05/Mar/2021:00:00:02 +0100
05/Mar/2021:00:00:09 +0100
05/Mar/2021:00:00:13 +0100

Используйте пару команд sed, чтобы сделать их подходящими для разбора командой date:

$ awk -F'[][]' '{print $2}' access.log | sed 's/:/ /' | sed 's|\/| |g'
05 Mar 2021 00:00:02 +0100
05 Mar 2021 00:00:09 +0100
05 Mar 2021 00:00:13 +0100

Теперь прочитайте каждую строку и преобразуйте их в секунды:

$ awk -F'[][]' '{print $2}' access.log | sed 's/:/ /' | sed 's|\/| |g' | while read seconds; do date -d "
$seconds" +%s; done
1614898802
1614898809
1614898813

Текущая дата в секундах:

$ date +%s
1718689281

Найти разницу между этим последним числом и числами вывода тривиально, и я оставлю эту часть вам.

С помощью perl:

perl -MTime::Piece -ne '
  BEGIN {$t = time - 3600}
  print if m{\[\d+/.*?\]} &&
        Time::Piece->strptime($&, "[%d/%b/%Y:%H:%M:%S %z]") >= $t' access.log

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

Поскольку временные метки находятся в местном времени (как указывают те смещения часового пояса +0100) и не в UTC, вы не можете просто произвести простое лексикографическое сравнение (после преобразования сокращений месяц в числа), так как это будет сбой около изменений перехода на летнее/зимнее время, поэтому нужно сделать преобразование обратно в момент времени, как мы делаем это здесь с Time::Piece‘s strptime().

Чтобы найти временную метку в строке, мы ищем первый [<цифры>/<что угодно как можно короче>] вместо просто [<что угодно как можно короче>] (m{\[.*?\]}), на случай если поле удаленного хоста, например, может быть IPv6-адресом, представленным как [hhhh:....].

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

Как отфильтровать записи последнего часа из файла журнала

Ведение журнала является важной частью управления сервером и анализа сетевой активности, и у вас есть задача отфильтровать записи из файла access.log, чтобы показать только те, которые были созданы в течение последнего часа. Рассмотрим несколько подходов к этой задаче, используя различные инструменты и языки программирования.

1. Использование Bash и Python

Благодаря простоте и доступности командной оболочки Bash можно создать скрипт для выполнения данной задачи. Ниже представлен алгоритм, который использует Python для расчета возраста временных меток.

#!/bin/bash

# Функция для вычисления возраста временной метки
age() { python -c '
import sys
from datetime import datetime
print(int((datetime.now() - datetime.strptime(sys.argv[1], sys.argv[2])).seconds))
' "$@"
}

# Чтение файла с конца и фильтрация записей
tac access.log | while IFS= read line; do
    date=$(echo "$line" | cut -d' ' -f4)
    age=$(age "$date" "[%d/%b/%Y:%H:%M:%S")
    [ $age -gt 3600 ] && break
    printf '%s\n' "$line"
done

Объяснение работы скрипта:

  1. Функция age: Используется для вычисления разницы во времени между текущим моментом и указанной временной меткой. Библиотека datetime в Python позволяет легко обрабатывать временные данные.
  2. Чтение файла с конца: Команда tac переворачивает порядок строк, что позволяет быстрее находить записи последнего часа.
  3. Получение временной метки: С помощью cut извлекаем дату и время из каждой строки журнала.
  4. Фильтрация: Если возраст больше одного часа (3600 секунд), мы прерываем цикл и не выводим дальнейшие строки.

2. Использование AWK и sed

Другой способ — воспользоваться мощью AWK и sed для обработки текста. Это может быть полезно, если вы хотите избежать использования Python.

awk -F'[][]' '{print $2}' access.log | sed 's/:/ /' | sed 's|\/| |g' | while read seconds; do 
    date -d "$seconds" +%s
done

Объяснение:

  1. Извлечение временных меток: Используя awk, мы извлекаем только временные метки из журнала и с помощью sed подготавливаем их для дальнейшей обработки.
  2. Преобразование в секунды: Каждая временная метка преобразуется в формат секунд с использованием команды date.

3. Использование Perl

Можно также использовать Perl, который может быть более эффективным при работе с текстовыми данными, как показано ниже:

perl -MTime::Piece -ne '
  BEGIN {$t = time - 3600}
  print if m{\[\d+/.*?\]} && Time::Piece->strptime($&, "[%d/%b/%Y:%H:%M:%S %z]") >= $t' access.log

Объяснение:

  • Импорт модуля Time::Piece: Модуль позволяет легко парсить временные метки и сравнивать их с текущим временем.
  • Начальное время: Устанавливаем метку времени с учетом последнего часа для фильтрации.
  • Печать строк: Для каждой строки проверяется, соответствует ли временная метка критериям, и если да, строка выводится.

Заключение

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

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

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