Ajout d'un timestamp avec des lignes de file journal

J'ai un file journal et je dois append des horodatages à chaque ligne à mesure qu'ils sont ajoutés. Je cherche donc un script qui ajoute des horodatages à chaque input dans les lignes de log et qui pourrait fonctionner comme un travail cron.

Quelqu'un pourrait-il m'aider avec l'idée de créer ce script?

La voie générale

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

Comment ça marche:

  1. cat lit le file appelé input.log et l'imprime juste à son stream de sortie standard.

    Normalement, la sortie standard est connectée à un terminal, mais ce petit script contient | donc shell redirige la sortie standard du cat vers l'input standard de sed .

  2. sed lit datatables (comme le cat produit), les traite (selon le script fourni avec l'option -e ) et les imprime ensuite à sa sortie standard. Le script "s/^/$(date -R) /" signifie replace chaque début de ligne par un text généré par la command date -R (la command de construction générale pour replace est: s/pattern/replace/ ).

  3. Ensuite, selon >> bash redirige la sortie de sed vers un file appelé output.log ( > signifie replace le contenu du file et >> signifie append à la fin).

Le problème est le $(date -R) évalué une fois lorsque vous exécutez le script afin qu'il insère 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 éviter cela, vous devez traiter les messages tels qu'ils sont écrits dans le file, pas avec un travail cron.

FIFO

La redirection de stream standard décrite ci-dessus appelée pipe . Vous pouvez le redirect non seulement avec | entre les commands du script, mais à travers un file FIFO (aka named pipe ). Un programme écrira dans le file et un autre lira datatables et les recevra comme les 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 marche:

  1. mkfifo crée un canal nommé

  2. while true; do sed ... ; done while true; do sed ... ; done exécute une boucle infinie et à chaque itération il fonctionne sed avec la redirection de foo.log.fifo à son input standard; sed en attente de données d'input , puis traite un message reçu et l'imprime à la sortie standard redirigée vers foo.log .

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

  3. echo ... > foo.log.fifo imprime un message à sa sortie standard redirigé vers le file fifo et sed reçoit et traite et écrit dans un file normal.

La note importante est le fifo comme n'importe quel autre tuyau n'a aucun sens si l'un de ses côtés n'est relié à aucun process. Si vous essayez d'écrire dans un tuyau, le process en cours bloquera jusqu'à ce que quelqu'un puisse lire datatables de l'autre côté du tuyau. Si vous voulez lire à partir d'un tuyau, le process bloquera jusqu'à ce que quelqu'un écrive des données dans le tuyau. La boucle sed dans l'exemple ci-dessus ne fait rien (dort) jusqu'à ce que vous echo .

Pour votre situation particulière, il suffit de configurer votre application pour écrire des messages de journal dans le file fifo. Si vous ne pouvez pas le configurer, supprimez simplement le file journal d'origine et créez un file fifo. Mais notez encore que si la boucle sed mourra pour une raison quelconque – votre programme sera bloqué en essayant d' write dans le file jusqu'à ce que quelqu'un read partir du fifo.

L'avantage est l'horodatage actuel évalué et joint à un message lorsque le programme l'écrit dans le file.

Traitement asynchronous avec tailf

Pour rendre l'écriture dans le journal et le traitement plus indépendant, vous pouvez utiliser deux files réguliers avec tailf . Une application écrira un message dans un file brut et un autre process lira de nouvelles lignes (suit pour écrire de façon asynchronous) et traitera datatables en écrivant dans le second file.

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 marche:

  1. Exécutez le process tailf qui suivra les écritures dans bar.raw.log et les imprimerez sur la sortie standard redirigée vers l'infini while read ... echo boucle d' while read ... echo . Cette boucle exécute deux actions: lire datatables de l'input standard vers une variable tampon appelée line , puis écrire l'horodatage généré avec datatables mises en memory tampon suivantes dans le bar.log .

  2. Ecrivez des messages dans le bar.raw.log . Vous devez le faire dans une window de terminal séparée car le premier sera occupé par tailf qui suivra les écritures et fera son travail. Assez facile.

Les avantages sont que votre application ne bloquerait pas si vous tuez tailf . Les inconvénients sont des horodatages less précis et la duplication des files journaux.

Vous pouvez utiliser le script ts perl de moreutils :

 $ echo test | ts %F-%H:%M:%.S 2012-11-20-13:34:10.731562 test 

Modifié de la réponse de Dmitry Vasilyanov.

Dans le script bash, vous pouvez redirect et envelopper la sortie avec horodatage ligne par ligne à la volée.

Quand utiliser:

  • Pour les tâches de script bash, insérez la ligne avant le script principal
  • Pour les travaux non script, créez un script pour appeler le programme.
  • Pour le contrôle des services par système, il est préférable d'utiliser tailf pour le file 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 marche

  1. exec &> Redirige stdout et stderr au même endroit
  2. >( ... ) des sorties de tuyau à une command interne asynchronous
  3. Le rest fonctionne comme Dmitry Vasilyanov élargi.

Par exemple:

  • timestamp de pipe et connectez-vous au file

     #!/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 à la sortie standard

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

     * * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log 

J'ai utilisé ts cette façon pour get une input avec un horodatage dans un journal des erreurs pour un script que j'utilise pour get Cacti rempli de statistics d'un hôte distant.

Pour tester Cacti j'utilise rand pour append quelques valeurs randoms que j'utilise pour les charts de température pour surveiller la température de mes systèmes.

Pushmonstats.sh est un script qui collecte les statistics de température du système de mon PC et l'envoie à un Raspberry Pi sur lequel Cacti fonctionne. Il y a quelque time, le réseau était bloqué. Je n'ai eu que des timeouts d'attente SSH dans mon journal des erreurs. Malheureusement, aucune input de time dans ce journal. Je ne savais pas comment append un horodatage à une input de journal. Donc, après quelques searchs 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 file temporaire. Ensuite, j'utilise chat pour montrer le contenu du file et le pipe à ts , append un format d'heure que j'ai trouvé sur ce post et enfin le connecter au file d'erreur. Ensuite, j'efface le contenu du file temporaire, sinon j'obtiens des inputs 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' 

Peut-être que ce n'est pas une façon très élégante de le faire, mais cela fonctionne. Je me request s'il y a une approche plus élégante.