web-dev-qa-db-fra.com

extraire les 10 dernières minutes du fichier journal

En essayant de trouver un moyen simple de regarder les événements récents (de moins de 10 minutes), j'ai essayé ceci:

awk "/^$(date --date="-10 min" "+%b %_d %H:%M")/{p++} p" /root/test.txt

mais ça ne marche pas comme prévu ...

Les fichiers de log sont sous forme:

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...
9
user1204671

Vous pouvez faire correspondre la plage de dates en utilisant une simple comparaison de chaînes, par exemple:

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

Par exemple, si d1='Dec 18 10:19' et d2='Dec 18 10:27', le résultat sera:

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

Ou en utilisant awk si vous le souhaitez:

awk -v d1="$d1" -v d2="$d2" '$0 > d1 && $0 < d2 || $0 ~ d2'
4
janos

Voici le bel outil Gamme que vous souhaitez de -10 à maintenant

sed -n "/^$(date --date='10 minutes ago' '+%b %_d %H:%M')/,\$p" /var/log/blaaaa
10
Zeus

C'est un travail (commun) pour Perl !:

Simple et efficace:

Perl -MDate::Parse -ne 'print if/^(.{15})\s/&&str2time($1)>time-600' /path/log

Cette version imprime l’événement des 10 dernières minutes, jusqu’àmaintenant, à l’aide de la fonction time.

Vous pouvez tester ceci avec:

Sudo cat /var/log/syslog |
  Perl -MDate::Parse -ne '
    print if /^(\S+\s+\d+\s+\d+:\d+:\d+)\s/ && str2time($1) > time-600'

Notez que la première représentation utilise uniquement les 15 premiers caractères de chaque ligne}, alors que la seconde construction utilise des détails plus détaillés regexp.

En tant que script Perl: 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

Strictement: extrait les 10 dernières minutes du fichier journal

Signification non relative à l'heure actuelle, mais à la dernière entrée du fichier journal:

Il existe deux méthodes pour récupérer fin de période:

date -r logfile +%s
tail -n1 logfile | Perl -MDate::Parse -nE 'say str2time($1) if /^(.{15})/'

Si logiquement, l'heure de dernière modification du fichier journal doit être l'heure de la dernière entrée.

Donc, la commande pourrait devenir:

Perl -MDate::Parse -ne 'print if/^(.{15})\s/&&str2time($1)>'$(
    date -r logfile +%s)

ou vous pouvez prendre les dernière entrée comme référence:

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

La deuxième version semble plus forte, mais n’a accès au fichier qu’une fois.

En tant que script Perl, cela pourrait ressembler à:

#!/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;
}

Mais si vous voulez vraiment utiliser bash

Il y a un très rapide pure bash script:

Warning : Cette utilisation récente bashismes, nécessite $BASH_VERSION 4.2 ou supérieur.

#!/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

Stockez ce script et exécutez:

cat >last10min.sh
chmod +x last10min.sh
Sudo cat /var/log/syslog | ./last10min.sh

Strictement: extrait les 10 dernières minutes du fichier journal

Remplacez simplement la ligne 10, mais vous devez placer le nom de fichier dans le script et ne pas l'utiliser comme filtre:

#!/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
7
F. Hauri

Dans bash , vous pouvez utiliser la commande date pour analyser les horodatages. Le spécificateur de format "% s" convertit la date donnée en nombre de secondes depuis le 1970-01-01 00:00:00 UTC . Cet entier simple est facile et précis pour faire de l'arithmétique de base.

Si vous souhaitez enregistrer les messages des 10 dernières minutes de l’heure réelle:

now10=$(($(date +%s) - (10 * 60)))

while read line; do
    [ $(date -d "${line:0:15}" +%s) -gt $now10 ] && printf "$line\n"
done < logfile

Notez que l'expression ${line:0:15} est un bash paramètre de développement qui donne les 15 premiers caractères de la ligne, c'est-à-dire l'horodatage lui-même.

Si vous souhaitez que les messages du journal des 10 dernières minutes soient relatifs à la fin du journal:

$ 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
$ 

Voici une légère amélioration des performances par rapport à ce qui précède:

$ { 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
$ 

Cela suppose que les entrées du journal sont dans un ordre chronologique strict. Une fois que nous avons fait correspondre l’horodatage en question, nous quittons la boucle for, puis nous utilisons simplement cat pour vider les entrées restantes.

1
Digital Trauma

En python, vous pouvez faire comme suit:

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

Placez les lignes du fichier dans une pile (une liste python). ouvrez le dernier élément et obtenez la différence entre les éléments de date successifs jusqu'à ce que la différence soit inférieure à 600 secondes.

Courant sur votre entrée, je reçois ce qui suit:

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
0
mkc

Une solution Ruby (testée sur Ruby 1.9.3)

Vous pouvez passer des jours, des heures, des minutes ou des secondes en tant que paramètre et il recherchera l'expression et le fichier spécifié (ou le répertoire, auquel cas il ajoutera '/ *' au nom):

Dans votre cas, appelez simplement le script comme suit: $ 0 -m 10 "expression" log_file

Remarque: si vous connaissez l'emplacement de 'Ruby', changez le Shebang (première ligne du script), Pour des raisons de sécurité.

#! /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
0
simi