Jak dodać znacznik czasu do dziennika skryptu bash?


94

Mam stale działający skrypt, który wypisuję do pliku dziennika:

script.sh >> /var/log/logfile

Chciałbym dodać znacznik czasu przed każdą linią dołączaną do dziennika. Lubić:

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

Czy mogę użyć jujitsu?


2
Zobacz to questin. serverfault.com/questions/80749/… . Stosuje się tutaj kilka odpowiedzi.
Zoredache,

Aby znaleźć rozwiązanie awk / gawk, patrz: stackoverflow.com/questions/21564/…
Użytkownik

Oto pełna implementacja logowania do bash: github.com/codeforester/base/blob/master/lib/stdlib.sh
codeforester

Odpowiedzi:


88

Możesz przesyłać dane wyjściowe skryptu przez pętlę poprzedzającą bieżącą datę i godzinę:

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

Jeśli będziesz często tego używać, łatwo jest wykonać funkcję bash do obsługi pętli:

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 Jest sztuczką, aby zapobiec readprzycinaniu białych znaków na początku i na linii. Ustawia IFS (wewnętrzny separator pól bash, w zasadzie listę białych znaków) na pusty dla readpolecenia.
Gordon Davisson,

2
... i -r ignoruje znak zmiany znaczenia „\”. To powinno naprawdę działać we wszystkich przypadkach - świetna porcja skryptu.
Nils

7
@ Nils nie jest całkowicie kuloodporny, ponieważ niektóre implementacje echointerpretują sekwencje specjalne. Jeśli naprawdę chcesz, aby nie bałaganił treści (poza dodawaniem dat), zamień echopolecenie naprintf "%s %s\n" "$(date)" "$line"
Gordon Davisson,

4
Możesz być zainteresowany datą / datownikiem zgodnym z ISO-8601 : date -u +"%Y-%m-%dT%H:%M:%SZ"a może ładniejszym date +"%Y-%m-%d %T".
Pablo A

1
chociaż ten skrypt działa zgodnie z oczekiwaniami, odradza się nowy proces (wykonanie date) dla każdej linii dziennika, co może być ogromną wadą w zależności od komputera i ilości dzienników. Wolę użyć, tsjeśli jest dostępny, patrz odpowiedź od @willem
Michael Schaefers

56

Zobacz tsz moreutilspakietu Ubuntu :

command | ts

Lub jeśli $commandautomatyczne buforowanie (wymaga expect-devpakietu):

unbuffer command | ts

18

Polecenie date dostarczy te informacje

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

więc możesz

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

czy tego właśnie chciałeś?


Używanie polecenia date było czymś, co miałem na myśli, ale tak naprawdę nie mogę dodać tego do samego skryptu, więc szukam sposobu na zmianę tego wiersza: „script.sh >> / var / log / logfile ”, aby dodać datę.
Antonius Bloch,

W takim przypadku przekieruj wyjście skryptu do nazwanego potoku i niech demon nasłuchuje wyników, które pobierają dane skryptu i dodają datę przed zapisaniem go do pliku dziennika. Prawdopodobnie możesz zmodyfikować skrypt, który tu napisałem , aby to zrobić. Zrobiłbym to, ponieważ mnie interesuje, ale w Wielkiej Brytanii jest późno i jutro mam wczesny start.
user9517 10.09.11

12

Możesz po prostu wysłać dane wyjściowe polecenia do pliku dziennika. to znaczy,

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

To naprawdę działa :)

Przykład:

[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 dla mnie nie działa.
Antonius Bloch,

Co otrzymujesz po wykonaniu polecenia?
SparX

8
To umieszcza znacznik czasu przed całym wyjściem pliku „./script.sh”, a nie przed każdą linią.
clacke

8

Zrób config.shplik

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

Kiedy musisz wysłać do pliku dziennika użyj

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

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

do_what_you_want >> $LOGFILE

Plik dziennika będzie wyglądał

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

Dzięki temu sortowanie według daty będzie łatwe


8
Twoja „config.sh” uruchomi „date” dokładnie raz, na „source ... / config.sh”.
clacke

5

Masz na myśli:

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

Mój Boże, ludzie, wszyscy robią zamianę tykania, nazwane potoki itp. Wystarczy umieścić zarówno datę, jak i skrypt w parens! Facet, który ma tę funkcję, ma uzasadniony przypadek, jeśli istnieje wyjście wieloliniowe, a dziennik musi wyglądać ładnie z datą w każdym wierszu, ale większość z tych rozwiązań to przesada, która nie używa semantyki powłoki.
cjc

8
To doda znacznik czasu tylko raz na wykonanie script.sh. OP potrzebuje znacznika czasu na linię.
Dave Forgac,

1
chociaż to nie odpowiada na pytanie OP, wciąż uważam, że to przydatne informacje.
Użytkownik

4

Spróbuj tego

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

Wywołaj tę funkcję znacznika czasu w każdym poleceniu echa:

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

@ shazbot: Dzięki za edycję, to był błąd literowy, nie zauważyłem.
Sanjay Yadav,

4

Akceptowana odpowiedź https://serverfault.com/a/310104 może być nieco wolna, jeśli trzeba przetworzyć wiele linii, a narzut związany z uruchomieniem dateprocesu pozwala na około 50 linii na sekundę w Ubuntu i tylko około 10 -20 w Cygwin.

Gdy bashmożna założyć, szybszą alternatywą byłoby printfwbudowanie ze %(...)Tspecyfikatorem formatu. Porównać

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

Możesz zauważyć, że awk działa szybko,

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

Ale sed działa znacznie szybciej,

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

Jednak po bliższym przyjrzeniu się zestaw nie wydaje się zmieniać czasu,

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

1
Wynika to z tego, że bash ocenia "s/^/$(date -R) /"i uruchamia datę raz przed sedem. Sed przechodzi ciąg statyczny.
Evan Benn,

Zwykły bash: yes | head -5000000 | while read line; do echo $((SECONDS)); done | uniq -cktóry jest znacznie wolniejszy niż gawk. tsnarzędzie ma podobną wydajność jak pętla bash.
akhan

Perl: yes |head -5000000 |perl -ne 'print localtime."\t".$_' |uniq -cktóry jest nieco wolniejszy niż awk.
akhan

1

Poniżej znajduje się zawartość mojego pliku dziennika

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

część mojej zawartości powłoki jest jak poniżej

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

Ten skrypt drukuje dane wyjściowe w terminalu, a także zapisuje w pliku dziennika.

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

Przykładowe dane wyjściowe:

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

Twoje pierwsze polecenie daty powinno używać pojedynczych cudzysłowów, a nie podwójnych.
Jason Harrison

1

Inną opcją jest ustawienie funkcji wywoływania za każdym razem, gdy chcesz wyprowadzać dane w kodzie:

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

Następnie za każdym razem w kodzie chcesz wysłać go do wywołania pliku dziennika

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

Bułka z masłem....


0

Potok „sed”:

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

jeśli chodzi o drugą odpowiedź, jest to tylko jednorazowa data uruchomienia bash. sed nie aktualizuje czasu na linię.
Evan Benn,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.