Извлечь последние 10 минут из файла журнала
Попытка найти простой способ просмотра последних событий (менее 10 минут), я пробовал это:
awk "/^$(date --date="-10 min" "+%b %_d %H:%M")/{p++} p" /root/test.txt
но он работает не так, как ожидалось...
Файлы журнала находятся в форме:
Dec 18 09:48:54 Blah
Dec 18 09:54:47 blah bla
Dec 18 09:55:33 sds
Dec 18 09:55:38 sds
Dec 18 09:57:58 sa
Dec 18 09:58:10 And so on...
Ответы
Ответ 1
Вы можете сопоставить диапазон дат с помощью простого сравнения строк, например:
d1=$(date --date="-10 min" "+%b %_d %H:%M")
d2=$(date "+%b %_d %H:%M")
while read line; do
[[ $line > $d1 && $line < $d2 || $line =~ $d2 ]] && echo $line
done
Например, если d1='Dec 18 10:19'
и d2='Dec 18 10:27'
, то вывод будет:
Dec 18 10:19:16
Dec 18 10:19:23
Dec 18 10:21:03
Dec 18 10:22:54
Dec 18 10:27:32
Или используя awk
, если хотите:
awk -v d1="$d1" -v d2="$d2" '$0 > d1 && $0 < d2 || $0 ~ d2'
Ответ 2
Это (общее) задание для perl:
Простой и эффективный:
perl -MDate::Parse -ne 'print if/^(.{15})\s/&&str2time($1)>time-600' /path/log
Эта версия печатает последние 10 минут события, до сейчас, используя time
функцию.
Вы можете проверить это с помощью:
sudo cat /var/log/syslog |
perl -MDate::Parse -ne '
print if /^(\S+\s+\d+\s+\d+:\d+:\d+)\s/ && str2time($1) > time-600'
Обратите внимание, что первое представление использует только первые 15 символов из каждой строки, а вторая конструкция использует более подробное регулярное выражение.
Как perl script: last10m.pl
#!/usr/bin/perl -wn
use strict;
use Date::Parse;
print if /^(\S+\s+\d+\s+\d+:\d+:\d+)\s/ && str2time($1) > time-600
Строго: извлечь последние 10 минут из файла журнала
Значение не относится к текущему времени, а к последней записи в файле журнала:
Существует два способа получения конца периода:
date -r logfile +%s
tail -n1 logfile | perl -MDate::Parse -nE 'say str2time($1) if /^(.{15})/'
Если логически, последнее время модификации файла журнала должно быть временем последней записи.
Таким образом, команда может стать:
perl -MDate::Parse -ne 'print if/^(.{15})\s/&&str2time($1)>'$(
date -r logfile +%s)
или вы можете взять последнюю запись в качестве ссылки:
perl -MDate::Parse -E 'open IN,"<".$ARGV[0];seek IN,-200,2;while (<IN>) {
$ref=str2time($1) if /^(\S+\s+\d+\s+\d+:\d+:\d+)/;};seek IN,0,0;
while (<IN>) {print if /^(.{15})\s/&&str2time($1)>$ref-600}' logfile
Вторая версия кажется более сильной, но доступ к файлу только один раз.
Как perl script, это может выглядеть так:
#!/usr/bin/perl -w
use strict;
use Date::Parse;
my $ref; # The only variable I will use in this.
open IN,"<".$ARGV[0]; # Open (READ) file submited as 1st argument
seek IN,-200,2; # Jump to 200 character before end of logfile. (This
# could not suffice if log file hold very log lines! )
while (<IN>) { # Until end of logfile...
$ref=str2time($1) if /^(\S+\s+\d+\s+\d+:\d+:\d+)/;
}; # store time into $ref variable.
seek IN,0,0; # Jump back to the begin of file
while (<IN>) {
print if /^(.{15})\s/&&str2time($1)>$ref-600;
}
Но если вы действительно хотите использовать bash
Существует очень быстрый чистый bash script:
Предупреждение. Для использования последних базизмов требуется $BASH_VERSION
4.2 или выше.
#!/bin/bash
declare -A month
for i in {1..12};do
LANG=C printf -v var "%(%b)T" $(((i-1)*31*86400))
month[$var]=$i
done
printf -v now "%(%s)T" -1
printf -v ref "%(%m%d%H%M%S)T" $((now-600))
while read line;do
printf -v crt "%02d%02d%02d%02d%02d" ${month[${line:0:3}]} \
$((10#${line:4:2})) $((10#${line:7:2})) $((10#${line:10:2})) \
$((10#${line:13:2}))
# echo " $crt < $ref ??" # Uncomment this line to print each test
[ $crt -gt $ref ] && break
done
cat
Сохраните этот script и запустите:
cat >last10min.sh
chmod +x last10min.sh
sudo cat /var/log/syslog | ./last10min.sh
Строго: извлечь последние 10 минут из файла журнала
Просто замените строку 10, но вы должны поместить имя файла в script и не использовать его как фильтр:
#!/bin/bash
declare -A month
for i in {1..12};do
LANG=C printf -v var "%(%b)T" $(((i-1)*31*86400))
month[$var]=$i
done
read now < <(date -d "$(tail -n1 $1|head -c 15)" +%s)
printf -v ref "%(%m%d%H%M%S)T" $((now-600))
export -A month
{
while read line;do
printf -v crt "%02d%02d%02d%02d%02d" ${month[${line:0:3}]} \
$((10#${line:4:2})) $((10#${line:7:2})) $((10#${line:10:2})) \
$((10#${line:13:2}))
[ $crt -gt $ref ] && break
done
cat
} <$1
Ответ 3
Вот хороший инструмент
диапазон - любой, который вы хотите от -10 до настоящего времени
sed -n "/^$(date --date='10 minutes ago' '+%b %_d %H:%M')/,\$p" /var/log/blaaaa
Ответ 4
В bash, вы можете использовать команда date
для анализа временных меток. Спецификатор "% s" преобразует указанную дату в число секунд с 1970-01-01 00:00:00 UTC. Это простое целое легко и точно выполняет основную арифметику.
Если вы хотите получать сообщения журнала за последние 10 минут фактического времени:
now10=$(($(date +%s) - (10 * 60)))
while read line; do
[ $(date -d "${line:0:15}" +%s) -gt $now10 ] && printf "$line\n"
done < logfile
Обратите внимание, что выражение ${line:0:15}
представляет собой расширение bash, которое дает первые 15 символов строки, то есть сама метка времени.
Если вы хотите получать сообщения журнала за последние 10 минут относительно конца журнала:
$ lastline=$(tail -n1 logfile)
$ last10=$(($(date -d "$lastline" +%s) - (10 * 60)))
$ while read line; do
> [ $(date -d "${line:0:15}" +%s) -gt $last10 ] && printf "$line\n"
> done < logfile
Dec 18 10:19:16
Dec 18 10:19:23
Dec 18 10:21:03
Dec 18 10:22:54
Dec 18 10:27:32
$
Ниже приведено небольшое улучшение производительности по сравнению с предыдущим:
$ { while read line; do
> [ $(date -d "${line:0:15}" +%s) -gt $last10 ] && printf "$line\n" && break
> done ; cat ; } < logfile
Dec 18 10:19:16
Dec 18 10:19:23
Dec 18 10:21:03
Dec 18 10:22:54
Dec 18 10:27:32
$
Это предполагает, что записи журнала находятся в строгом хронологическом порядке. Как только мы сопоставим указанную временную метку, мы выходим из цикла for, а затем просто используем cat
, чтобы сбрасывать оставшиеся записи.
Ответ 5
В python вы можете сделать следующее:
from datetime import datetime
astack=[]
with open("x.txt") as f:
for aline in f:
astack.append(aline.strip())
lasttime=datetime.strptime(astack[-1], '%b %d %I:%M:%S')
for i in astack:
if (lasttime - datetime.strptime(i, '%b %d %I:%M:%S')).seconds <= 600:
print i
Поместите строки из файла в стек (список python). вытащите последний элемент и получите разницу между последовательными элементами даты, пока вы не получите разницу менее чем за 600 секунд.
Запуск на вашем входе, я получаю следующее:
Dec 18 10:19:16
Dec 18 10:19:23
Dec 18 10:21:03
Dec 18 10:22:54
Dec 18 10:27:32
Ответ 6
Решение Ruby (проверено на ruby 1.9.3)
В качестве параметра вы можете передавать дни, часы, минуты или секунды, и он будет искать выражение и указанный файл (или каталог, и в этом случае он добавит "/*" к имени):
В вашем случае просто вызовите script следующим образом: $0 -m 10 "выражение" log_file
Примечание. Также, если вы знаете, что местоположение "рубина" меняет shebang (первая строка script),
по соображениям безопасности.
#! /usr/bin/env ruby
require 'date'
require 'pathname'
if ARGV.length != 4
$stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
exit 1
end
begin
total_amount = Integer ARGV[1]
rescue ArgumentError
$stderr.print "error: parameter 'time' must be an Integer\n"
$stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
end
if ARGV[0] == "-m"
gap = Rational(60, 86400)
time_str = "%b %d %H:%M"
elsif ARGV[0] == "-s"
gap = Rational(1, 86400)
time_str = "%b %d %H:%M:%S"
elsif ARGV[0] == "-h"
gap = Rational(3600, 86400)
time_str = "%b %d %H"
elsif ARGV[0] == "-d"
time_str = "%b %d"
gap = 1
else
$stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
exit 1
end
pn = Pathname.new(ARGV[3])
if pn.exist?
log = (pn.directory?) ? ARGV[3] + "/*" : ARGV[3]
else
$stderr.print "error: file '" << ARGV[3] << "' does not exist\n"
$stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
end
search_str = ARGV[2]
now = DateTime.now
total_amount.times do
now -= gap
system "cat " << log << " | grep '" << now.strftime(time_str) << ".*" << search_str << "'"
end