Ajout d'un horodatage avec des lignes de fichier journal

11

J'ai un fichier journal et je dois ajouter des horodatages à chaque ligne au fur et à mesure qu'ils sont ajoutés. Je recherche donc un script qui ajoute des horodatages à chaque entrée dans les lignes de journal et qui pourrait fonctionner comme un travail cron.

Kratos
la source

Réponses:

18

La voie générale

$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log

Comment ça fonctionne:

  1. catlit le fichier appelé input.loget l'imprime simplement dans son flux de sortie standard.

    Normalement, la sortie standard est connectée à un terminal, mais ce petit script contient |donc le shell redirige la sortie standard de catvers l'entrée standard de sed.

  2. sedlit les données (au fur et à mesure catqu'elles les produisent), les traite (selon le script fourni avec l' -eoption), puis les imprime sur sa sortie standard. Le script "s/^/$(date -R) /"signifie remplacer chaque début de ligne par un texte généré par la date -Rcommande (la construction générale de la commande replace est:) s/pattern/replace/.

  3. Ensuite, selon >> bashredirige la sortie de sedvers un fichier appelé output.log( >signifie remplacer le contenu du fichier et >>signifie ajouter à la fin).

Le problème est $(date -R)évalué une fois lorsque vous exécutez le script, il insérera donc l' horodatage actuel au début de chaque ligne. L'horodatage actuel peut être loin d'un moment où un message a été généré. Pour l'éviter, vous devez traiter les messages au fur et à mesure qu'ils sont écrits dans le fichier, et non avec une tâche cron.

FIFO

La redirection de flux standard décrite ci-dessus s'appelle pipe . Vous pouvez le rediriger non seulement avec |entre les commandes du script, mais via un fichier FIFO (alias nommé pipe ). Un programme écrit dans le fichier et un autre lit les données et les reçoit lors des premiers envois.

Choisissez un exemple:

$ mkfifo foo.log.fifo
$ while true; do cat foo.log.fifo | sed -e "s/^/$(date -R) /" >> foo.log; done;

# have to open a second terminal at this point
$ echo "foo" > foo.log.fifo
$ echo "bar" > foo.log.fifo
$ echo "baz" > foo.log.fifo

$ cat foo.log      

Tue, 20 Nov 2012 15:32:56 +0400 foo
Tue, 20 Nov 2012 15:33:27 +0400 bar
Tue, 20 Nov 2012 15:33:30 +0400 baz

Comment ça fonctionne:

  1. mkfifo crée un tube nommé

  2. while true; do sed ... ; doneexécute une boucle infinie et à chaque itération, elle s'exécute sedavec redirection foo.log.fifovers son entrée standard; sed bloque l'attente des données d'entrée , puis traite un message reçu et l'imprime sur la sortie standard redirigée vers foo.log.

    À ce stade, vous devez ouvrir une nouvelle fenêtre de terminal car la boucle occupe le terminal actuel.

  3. echo ... > foo.log.fifoimprime un message sur sa sortie standard redirigée vers le fichier fifo et le sedreçoit et le traite et l'écrit dans un fichier normal.

La note importante est le fifo, tout comme n'importe quel autre tuyau n'a aucun sens si l'un de ses côtés n'est connecté à aucun processus. Si vous essayez d'écrire dans un tuyau, le processus en cours se bloquera jusqu'à ce que quelqu'un lise les données de l'autre côté du tuyau. Si vous souhaitez lire à partir d'un canal, le processus se bloquera jusqu'à ce que quelqu'un écrive des données dans le canal. La sedboucle de l'exemple ci-dessus ne fait rien (s'endort) jusqu'à ce que vous le fassiez echo.

Pour votre situation particulière, il vous suffit de configurer votre application pour écrire des messages de journal dans le fichier fifo. Si vous ne pouvez pas le configurer, supprimez simplement le fichier journal d'origine et créez un fichier fifo. Mais notez à nouveau que si la sedboucle meurt pour une raison quelconque - votre programme sera bloqué lors de la tentative d'accès au writefichier jusqu'à ce que quelqu'un readvienne du fifo.

L'avantage est l' horodatage actuel évalué et joint à un message pendant que le programme l'écrit dans le fichier.

Traitement asynchrone avec tailf

Pour rendre l'écriture dans le journal et le traitement plus indépendants, vous pouvez utiliser deux fichiers standard avec tailf. Une application écrira un message dans un fichier brut et un autre processus lira de nouvelles lignes (suivez pour écrire de manière asynchrone) et traitera les données avec l'écriture dans le deuxième fichier.

Prenons un exemple:

# will occupy current shell
$ tailf -n0 bar.raw.log | while read line; do echo "$(date -R) $line" >> bar.log; done;

$ echo "foo" >> bar.raw.log
$ echo "bar" >> bar.raw.log
$ echo "baz" >> bar.raw.log

$ cat bar.log

Wed, 21 Nov 2012 16:15:33 +0400 foo
Wed, 21 Nov 2012 16:15:36 +0400 bar
Wed, 21 Nov 2012 16:15:39 +0400 baz

Comment ça fonctionne:

  1. Exécutez le tailfprocessus qui suivra les écritures bar.raw.loget imprimez-les sur la sortie standard redirigée vers la while read ... echoboucle infinie . Cette boucle effectue deux actions: lire les données de l'entrée standard dans une variable tampon appelée linepuis écrire l'horodatage généré avec les données tamponnées suivantes dans le bar.log.

  2. Écrivez quelques messages au bar.raw.log. Vous devez le faire dans une fenêtre de terminal séparée car la première sera occupée par tailflaquelle suivra les écritures et fera son travail. Assez facile.

L'avantage est que votre application ne bloquera pas si vous tuez tailf. Les inconvénients sont des horodatages moins précis et la duplication des fichiers journaux.

Dmitry Vasilyanov
la source
Merci .Fifo semble très approprié pour mon utilisation. Mon programme écrit déjà dans un journal file.txt mais ce fichier doit être redirigé vers votre foo.log.fifo.Mais en faisant $ tailf file.txt> foo.log.fifo se bloque ! Alors, comment puis-je rediriger mon contenu dans un fichier journal de mise à jour vers ce foo.log.fifo afin qu'il puisse également ajouter l'heure.
Kratos
Je viens d'améliorer la réponse. Expliqué pourquoi fifo bloque votre tailf, a ajouté la bonne façon de l'utiliser. En fait, le chemin avec tailfsemble être plus élégant, mais j'ai quitté le chemin fifo avec l'espoir qu'il sera utile pour quelqu'un.
Dmitry Vasilyanov
L'utilisation de votre réponse sed pour annoter la sortie de vmstat donne un horodatage qui ne change pas, vmstat 1 | sed -e "s / ^ / $ (date -R) /"
ChuckCottrill
6

Vous pouvez utiliser le tsscript perl à partir de moreutils:

$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
Stéphane Chazelas
la source
2

Modifié de la réponse de Dmitry Vasilyanov.

Dans le script bash, vous pouvez rediriger et encapsuler la sortie avec l'horodatage ligne par ligne à la volée.

Quand utiliser:

  • Pour les travaux de script bash, insérez la ligne avant le script principal
  • Pour les travaux sans script, créez un script pour appeler le programme.
  • Pour le contrôle des services par le système, il est préférable d'utiliser tailfle fichier journal comme l'a dit Dmitry Vasilyanov.

Un exemple nommé foo.sh:

#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)

echo "foo"
sleep 1
echo "bar" >&2
sleep 1
echo "foobar"

Et le résultat:

$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar

Comment ça fonctionne

  1. exec &> Rediriger stdout et stderr au même endroit
  2. >( ... ) diriger les sorties vers une commande interne asynchrone
  3. La partie restante fonctionne à mesure que Dmitry Vasilyanov se développe.

Par exemple:

  • horodatage du tube et journal dans un fichier

    #!/bin/bash        
    exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
    
    echo "some script commands"
    /path-to/some-thrid-party-programs
    
  • Ou imprimez l'horodatage et connectez-vous à stdout

    #!/bin/bash        
    exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line"; done;)
    
    echo "some script commands"
    /path-to/some-thrid-party-programs
    

    puis enregistrez-les dans le /etc/crontabréglage

    * * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
    
Jethro Yu
la source
1

J'ai utilisé tscette méthode pour obtenir une entrée avec un horodatage dans un journal d'erreurs pour un script que j'utilise pour obtenir Cacti rempli de statistiques d'un hôte distant.

Pour tester Cacti, j'utilise randpour ajouter des valeurs aléatoires que j'utilise pour les graphiques de température pour surveiller la température de mon système.

Pushmonstats.sh est un script qui collecte les statistiques de température du système de mon PC et les envoie à un Raspberry Pi sur lequel Cacti s'exécute. Il y a quelque temps, le réseau était bloqué. J'ai seulement obtenu des délais d'expiration SSH dans mon journal d'erreurs. Malheureusement, aucune entrée d'heure dans ce journal. Je ne savais pas comment ajouter un horodatage à une entrée de journal. Donc, après quelques recherches sur Internet, je suis tombé sur ce post et c'est ce que j'ai fait en utilisant ts.

Pour le tester, j'ai utilisé une option inconnue pour rand. Ce qui a donné une erreur à stderr. Pour le capturer, je le redirige vers un fichier temporaire. Ensuite, j'utilise cat pour afficher le contenu du fichier et le rediriger vers ts, ajouter un format d'heure que j'ai trouvé sur ce post et enfin l'enregistrer dans le fichier d'erreur. Ensuite, j'efface le contenu du fichier temporaire, sinon j'obtiens des entrées doubles pour la même erreur.

Crontab:

* * * * * /home/monusr/bin/pushmonstats.sh 1>> /home/monusr/pushmonstats.log 2> /home/monusr/.err;/bin/cat /home/monusr/.err|/usr/bin/ts %F-%H:%M:%.S 1>> /home/monusr/pushmonstats.err;> /home/monusr/.err

Cela donne ce qui suit dans mon journal des erreurs:

2014-03-22-19:17:53.823720 rand: unknown option -- '-l'

Ce n'est peut-être pas une façon très élégante de le faire, mais cela fonctionne. Je me demande s'il existe une approche plus élégante.

Franc
la source