¿Cómo agregar una marca de tiempo al registro de script bash?

95

Tengo un script en ejecución constante que envío a un archivo de registro:

script.sh >> /var/log/logfile

Me gustaría agregar una marca de tiempo antes de cada línea que se agrega al registro. Me gusta:

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

¿Hay algún jujitsu que pueda usar?

Antonius Bloch
fuente
2
Ver esta búsqueda. serverfault.com/questions/80749/… . Una respuesta de pareja se aplicaría aquí.
Zoredache
Para obtener una solución awk / gawk, consulte: stackoverflow.com/questions/21564/…
Usuario
Aquí hay una implementación integral de registro para bash: github.com/codeforester/base/blob/master/lib/stdlib.sh
codeforester

Respuestas:

88

Puede canalizar la salida del script a través de un bucle que prefija la fecha y hora actuales:

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

Si va a usar esto mucho, es fácil hacer una función bash para manejar el ciclo:

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
Gordon Davisson
fuente
3
@Nils es un truco para evitar readrecortar espacios en blanco al principio y de la línea. Establece IFS (separador de campo interno de bash, básicamente una lista de caracteres de espacios en blanco) para vaciar el readcomando.
Gordon Davisson
2
... y -r ignora el carácter de escape "\". Esto realmente debería funcionar en todos los casos: una gran cantidad de secuencias de comandos.
Nils
77
@Nils no es completamente a prueba de balas, ya que algunas implementaciones de echointerpretar secuencias de escape. Si realmente desea que no interfiera con el contenido (aparte de agregar fechas), reemplace el echocomando conprintf "%s %s\n" "$(date)" "$line"
Gordon Davisson
44
Es posible que le interese una fecha / hora que cumpla con ISO-8601 : date -u +"%Y-%m-%dT%H:%M:%SZ"o tal vez más bonita date +"%Y-%m-%d %T".
Pablo A
1
Si bien este script funciona como se esperaba, genera un nuevo proceso (ejecución date) para cada línea de registro, que puede ser un inconveniente masivo dependiendo de su máquina y la cantidad de registros. Prefiero sugerir usarlo tssi está disponible, vea la respuesta de @willem
Michael Schaefers
57

Ver tsdesde el moreutilspaquete de Ubuntu :

command | ts

O, si $commandel almacenamiento en búfer automático (requiere expect-devpaquete):

unbuffer command | ts
Willem
fuente
18

El comando date proporcionará esa información

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

así que puedes

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

Es eso lo que querías ?

usuario9517
fuente
Usar el comando date fue algo que tenía en mente, pero realmente no puedo agregar eso al script en sí, así que lo que estoy buscando es una forma de cambiar esta línea: "script.sh >> / var / log / logfile "para agregar la fecha.
Antonius Bloch
En ese caso, redirija la salida de su script a una tubería con nombre y haga que un demonio escuche la salida que toma la salida del script y agrega una fecha antes de escribirla en un archivo de registro. Probablemente pueda modificar el script que escribí aquí para hacer esto. Lo haría porque me interesa, pero es tarde en el Reino Unido y tengo un comienzo temprano mañana.
user9517
12

Simplemente puede hacer eco de las salidas del comando en el archivo de registro. es decir,

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

Realmente funciona :)

Ejemplo:

[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]$ 
SparX
fuente
Hmm no está funcionando para mí.
Antonius Bloch
¿Qué obtienes cuando ejecutas el comando?
SparX
8
Eso pone una marca de tiempo antes de la salida completa de '' ./script.sh '', no antes de cada línea.
clacke
8

Hacer un config.sharchivo

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

Cuando necesita enviar para usar el archivo de registro

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

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

do_what_you_want >> $LOGFILE

El archivo de registro se verá así

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

Entonces será fácil ordenar por fecha

scls
fuente
8
Su '' config.sh '' ejecutará '' date '' exactamente una vez, en '' source ... / config.sh ''.
clacke
5

Te refieres a:

(date && script.sh) >> /var/log/logfile
cjc
fuente
Dios mío, gente, todo el mundo está haciendo el reemplazo de las garrapatas, tuberías con nombre, etc. ¡Solo encierra el comando de fecha y el script en parens! El tipo que tiene la función tiene un caso legítimo si hay salida de varias líneas y el registro debe verse bonito con la fecha en cada línea, pero la mayoría de estas soluciones son excesivas que no usan semántica de shell.
cjc
8
Eso solo agregará la marca de tiempo una vez por ejecución de script.sh. El OP necesita una marca de tiempo por línea.
Dave Forgac
1
aunque esto no responde a la pregunta de OP, aún encuentro información útil.
Usuario
4

Prueba esto

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

Llame a esta función de marca de tiempo en cada comando echo:

echo "$(timestamp): write your log here" >> /var/log/<logfile>.log
Sanjay Yadav
fuente
@ Shazbot: Gracias por editar, fue un error tipográfico, no me di cuenta.
Sanjay Yadav
4

La respuesta aceptada https://serverfault.com/a/310104 puede ser un poco lenta, si se tienen que procesar muchas líneas, con la sobrecarga de iniciar el dateproceso permitiendo aproximadamente 50 líneas por segundo en Ubuntu, y solo alrededor de 10 -20 en Cygwin.

Cuando bashse puede suponer, una alternativa más rápida sería la printfintegrada con su %(...)Tespecificador de formato. Comparar

>> 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
kdb
fuente
1

Puedes notar que awk corre rápido,

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

Pero, sed corre mucho más rápido,

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

Sin embargo, en una inspección más cercana, el conjunto no parece cambiar el tiempo,

vmstat 1 | sed -e "s/^/$(date -R) /"
ChuckCottrill
fuente
1
Esto se debe a que bash está evaluando "s/^/$(date -R) /"y ejecutando la fecha una vez antes de sed. Sed pasa una cuerda estática.
Evan Benn
Plain bash: yes | head -5000000 | while read line; do echo $((SECONDS)); done | uniq -cque es mucho más lento que gawk. tsLa utilidad tiene un rendimiento similar al bash loop.
akhan
Perl: yes |head -5000000 |perl -ne 'print localtime."\t".$_' |uniq -cque es un poco más lento que awk.
akhan
1

El siguiente es el contenido de mi archivo de 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

algunos de mis contenidos de shell son los siguientes

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 
Jayhello
fuente
1

Este script imprime el resultado en la terminal y también lo guarda en el archivo de 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

Salida de muestra:

[2017-10-29 19:46:36 UTC] - My script is starting
[2017-10-29 19:46:36 UTC] - root
Seff
fuente
su primer comando de fecha debe usar comillas simples, no comillas dobles.
Jason Harrison
1

Otra opción es configurar una función para llamar cada vez que desee generar datos en su código:

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

Luego, cada vez en su código, desea enviarlo a la llamada del archivo de registro

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

Pan comido....

Josh Williams
fuente
0

Pipe a "sed":

script.sh | sed "s|^|$('date') :: |" >> /var/log/logfile
Eubide
fuente
En cuanto a la otra respuesta, esta es solo una fecha de ejecución bash una vez. sed no está actualizando el tiempo por línea.
Evan Benn