Come aggiungere un timestamp al registro degli script bash?


94

Ho uno script in esecuzione costante che ho generato in un file di registro:

script.sh >> /var/log/logfile

Vorrei aggiungere un timestamp prima di ogni riga aggiunta al registro. Piace:

Sat Sep 10 21:33:06 UTC 2011 The server has booted up.  Hmmph.

C'è qualche jujitsu che posso usare?


2
Vedi questa missione. serverfault.com/questions/80749/… . Una risposta di coppia si applicherebbe qui.
Zoredache,

Per una soluzione awk / gawk, consultare: stackoverflow.com/questions/21564/…
Utente

Ecco un'implementazione completa della registrazione per bash: github.com/codeforester/base/blob/master/lib/stdlib.sh
codeforester

Risposte:


88

È possibile reindirizzare l'output dello script tramite un ciclo che precede la data e l'ora correnti:

./script.sh | while IFS= read -r line; do printf '%s %s\n' "$(date)" "$line"; done >>/var/log/logfile

Se lo utilizzerai molto, è facile fare una funzione bash per gestire il loop:

adddate() {
    while IFS= read -r line; do
        printf '%s %s\n' "$(date)" "$line";
    done
}

./thisscript.sh | adddate >>/var/log/logfile
./thatscript.sh | adddate >>/var/log/logfile
./theotherscript.sh | adddate >>/var/log/logfile

3
@Nils è un trucco per evitare readdi tagliare gli spazi all'inizio e e della linea. Imposta IFS (Internal Field Separator di bash, fondamentalmente un elenco di caratteri di spazi bianchi) su vuoto per il readcomando.
Gordon Davisson,

2
... e -r ignora il carattere di escape "\". Questo dovrebbe funzionare davvero in tutti i casi, un ottimo esempio di scripting.
Nils,

7
@Nils non è completamente a prova di proiettile, poiché alcune implementazioni di echointerpretano sequenze di escape. Se vuoi davvero che non interferisca con il contenuto (oltre all'aggiunta di date), sostituisci il echocomando conprintf "%s %s\n" "$(date)" "$line"
Gordon Davisson,

4
Potresti essere interessato ad una data / data / ora conforme a ISO-8601 : date -u +"%Y-%m-%dT%H:%M:%SZ"o forse più carina date +"%Y-%m-%d %T".
Pablo A

1
mentre questo script funziona come previsto, genera un nuovo processo (esegui date) per ogni linea di registro, che può essere un enorme svantaggio a seconda del tuo computer e della quantità di registri. Preferirei usare tsse disponibile, vedi la risposta di @willem
Michael Schaefers,

56

Vedi tsdal moreutilspacchetto Ubuntu :

command | ts

Oppure, se $commandil buffering automatico (richiede il expect-devpacchetto):

unbuffer command | ts

18

Il comando date fornirà tali informazioni

date -u
Sat Sep 10 22:39:24 UTC 2011

così puoi

echo $(date -u) "Some message or other"

è quello che volevi?


L'uso del comando date era una specie di cosa avevo in mente, ma non posso davvero aggiungerlo allo script stesso, quindi quello che sto cercando è un modo per cambiare questa riga: "script.sh >> / var / log / logfile "per aggiungere la data.
Antonius Bloch,

In tal caso, reindirizza l'output dello script su una pipe denominata e fai in modo che un demone ascolti l'output che prende l'output dello script e aggiunge una data prima di scriverlo in un file di registro. Probabilmente puoi modificare lo script che ho scritto qui per farlo. Lo farei perché mi interessa, ma è tardi nel Regno Unito e domani inizierò presto.
utente9517

12

È possibile semplicemente ripetere l'output del comando nel file di registro. vale a dire,

echo "`date -u` `./script.sh`" >> /var/log/logfile

Funziona veramente :)

Esempio:

[sparx@E1]$ ./script.sh 
Hello Worldy
[sparx@E1]$ echo "`date -u` `./script.sh`" >> logfile.txt
[sparx@E1]$ cat logfile.txt 
Mon Sep 12 20:18:28 UTC 2011 Hello Worldy
[sparx@E1]$ 

Hmm non funziona per me.
Antonius Bloch,

Cosa ottieni quando esegui il comando?
SparX,

8
Ciò mette un timestamp prima dell'intero output di '' ./script.sh '', non prima di ogni riga.
Clacke

8

Crea un config.shfile

#!/usr/bin/env bash
LOGFILE="/path/to/log.log"
TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"`

Quando è necessario inviare per utilizzare il file di registro

#!/usr/bin/env bash
source /path/to/config.sh

echo "$TIMESTAMP Say what you are doing" >> $LOGFILE

do_what_you_want >> $LOGFILE

Il file di registro sarà simile

2013-02-03 18:22:30 Say what you are doing

Quindi sarà facile ordinare per data


8
Il tuo '' config.sh '' eseguirà '' date '' esattamente una volta, su '' source ... / config.sh ''.
Clacke

5

Intendi come:

(date && script.sh) >> /var/log/logfile

Mio Dio, gente, tutti stanno facendo la sostituzione del segno di spunta, named pipe, ecc. Basta racchiudere sia il comando data che lo script tra parentesi! Il tipo che ha la funzione ha un caso legittimo se c'è un output multi-linea e il registro deve avere un bell'aspetto con la data su ogni riga, ma la maggior parte di queste soluzioni sono eccessive che non usano la semantica della shell.
cjc,

8
Ciò aggiungerà il timestamp solo una volta per esecuzione di script.sh. L'OP ha bisogno di un timestamp per riga.
Dave Forgac,

1
anche se questo non risponde alla domanda OP, ho comunque trovato utili informazioni.
Utente

4

Prova questo

timestamp()
{
 date +"%Y-%m-%d %T"
}

Chiamare questa funzione timestamp in ogni comando echo:

echo "$(timestamp): write your log here" >> /var/log/<logfile>.log

@ shazbot: Grazie per la modifica, è stato un errore di battitura, non l'ho notato.
Sanjay Yadav,

4

La risposta accettata https://serverfault.com/a/310104 può essere un po 'lenta, se devono essere elaborate molte linee, con il sovraccarico di avviare il dateprocesso che consente circa 50 linee al secondo in Ubuntu e solo circa 10 -20 in Cygwin.

Quando si bashpuò presumere che un'alternativa più veloce sarebbe l' printfintegrato con il suo identificatore di %(...)Tformato. Confrontare

>> while true; do date; done | uniq -c
     47 Wed Nov  9 23:17:18 STD 2016
     56 Wed Nov  9 23:17:19 STD 2016
     55 Wed Nov  9 23:17:20 STD 2016
     51 Wed Nov  9 23:17:21 STD 2016
     50 Wed Nov  9 23:17:22 STD 2016

>> while true; do printf '%(%F %T)T\n'; done | uniq -c
  20300 2016-11-09 23:17:56
  31767 2016-11-09 23:17:57
  32109 2016-11-09 23:17:58
  31036 2016-11-09 23:17:59
  30714 2016-11-09 23:18:00

1

Potresti notare che awk corre veloce,

gawk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }'

yes |head -5000000 |gawk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 }' |uniq -c
 461592 [2017-02-28 19:46:44] y
 488555 [2017-02-28 19:46:45] y
 491205 [2017-02-28 19:46:46] y
 498568 [2017-02-28 19:46:47] y
 502605 [2017-02-28 19:46:48] y
 494048 [2017-02-28 19:46:49] y
 493299 [2017-02-28 19:46:50] y
 498005 [2017-02-28 19:46:51] y
 502916 [2017-02-28 19:46:52] y
 495550 [2017-02-28 19:46:53] y
  73657 [2017-02-28 19:46:54] y

Ma sed corre molto più veloce,

sed -e "s/^/$(date -R) /"

yes |head -5000000 |sed -e "s/^/$(date -R) /" |uniq -c
5000000 Tue, 28 Feb 2017 19:57:00 -0500 y

Tuttavia, a un esame più attento, il set non sembra cambiare il tempo,

vmstat 1 | sed -e "s/^/$(date -R) /"

1
questo perché sta valutando "s/^/$(date -R) /"e eseguendo la data una volta prima di sed. A Sed viene passata una stringa statica.
Evan Benn,

Bash semplice: yes | head -5000000 | while read line; do echo $((SECONDS)); done | uniq -cche è molto più lento di gawk. tsl'utilità ha prestazioni simili a quelle del loop bash.
Akhan,

Perl: yes |head -5000000 |perl -ne 'print localtime."\t".$_' |uniq -cche è leggermente più lento di awk.
Akhan,

1

Di seguito è riportato il contenuto del mio file di registro

xiongyu@ubuntu:~/search_start_sh$ tail restart_scrape.log 

2017-08-25 21:10:09 scrape_yy_news_main.py got down, now I will restart it

2017-08-25 21:10:09 check_yy_news_warn.py got down, now I will restart it

2017-08-25 21:14:53 scrape_yy_news_main.py got down, now I will restart it

alcuni dei contenuti della mia shell sono i seguenti

log_file="restart_scrape.log"
TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"`
echo "$TIMESTAMP $search_py_file got down, now I will restart it" | tee -a $log_file 

1

Questo script stampa l'output nel terminale e salva anche nel file di registro.

#!/bin/bash

MY_LOG=/var/log/output.log

echolog(){
    if [ $# -eq 0 ]
    then cat - | while read -r message
        do
                echo "$(date +"[%F %T %Z] -") $message" | tee -a $MY_LOG
            done
    else
        echo -n "$(date +'[%F %T %Z]') - " | tee -a $MY_LOG
        echo $* | tee -a $MY_LOG
    fi
}

echolog "My script is starting"
whoami | echolog

Uscita campione:

[2017-10-29 19:46:36 UTC] - My script is starting
[2017-10-29 19:46:36 UTC] - root

il tuo primo comando data dovrebbe usare virgolette singole, non doppie virgolette.
Jason Harrison,

1

Un'altra opzione è quella di impostare una funzione da chiamare ogni volta che si desidera generare dati nel proprio codice:

PrintLog(){
  information=$1
  logFile=$2
  echo "$(date +'%Y-%m-%d %H:%M:%S" $information} >> $logFile
}

Quindi ogni volta nel tuo codice vuoi inviarlo alla chiamata del file di registro

PrintLog "Stuff you want to add..." ${LogFileVariable}

Vai tranquillo....


0

Pipe a "sed":

script.sh | sed "s|^|$('date') :: |" >> /var/log/logfile

per quanto riguarda l'altra risposta, questa è solo una volta la data di esecuzione. sed non aggiorna il tempo per riga.
Evan Benn,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.