Respuestas:
$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log
Cómo funciona:
cat
lee el archivo llamado input.log
y simplemente lo imprime en su flujo de salida estándar.
Normalmente, la salida estándar está conectada a un terminal, pero este pequeño script contiene, |
por lo que Shell redirige la salida estándar de cat
a la entrada estándar de sed
.
sed
lee los datos (a medida que los cat
produce), los procesa (de acuerdo con el script proporcionado con la -e
opción) y luego los imprime en su salida estándar. El script "s/^/$(date -R) /"
significa reemplazar cada inicio de línea por un texto generado por date -R
comando (la construcción general para el comando de reemplazo es:) s/pattern/replace/
.
Luego, de acuerdo con >>
bash
redirige la salida sed
a un archivo llamado output.log
( >
significa reemplazar el contenido del archivo y >>
significa agregar al final).
El problema se $(date -R)
evalúa una vez cuando ejecuta el script para que inserte la marca de tiempo actual al comienzo de cada línea. La marca de tiempo actual puede estar lejos de un momento en que se generó un mensaje. Para evitarlo, debe procesar los mensajes tal como están escritos en el archivo, no con un trabajo cron.
La redirección de flujo estándar descrita anteriormente se llama tubería . Puede redirigirlo no solo |
entre comandos en el script, sino a través de un archivo FIFO (también conocido como pipe ). Un programa escribirá en el archivo y otro leerá los datos y los recibirá como los primeros envíos.
Elige un ejemplo:
$ 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
Cómo funciona:
mkfifo
crea una tubería con nombre
while true; do sed ... ; done
ejecuta un bucle infinito y en cada iteración se ejecuta sed
con la redirección foo.log.fifo
a su entrada estándar; sed
bloquea la espera de datos de entrada y luego procesa un mensaje recibido y lo imprime a la salida estándar redirigida a foo.log
.
En este punto, debe abrir una nueva ventana de terminal porque el bucle ocupa el terminal actual.
echo ... > foo.log.fifo
imprime un mensaje a su salida estándar redirigida al archivo fifo y lo sed
recibe y procesa y escribe en un archivo normal.
La nota importante es el Fifo al igual que cualquier otra tubería no tiene sentido si uno de sus lados no está conectado a ningún proceso. Si intenta escribir en una tubería, el proceso actual se bloqueará hasta que alguien lea los datos al otro lado de la tubería. Si desea leer desde una tubería, el proceso se bloqueará hasta que alguien escriba datos en la tubería. El sed
bucle en el ejemplo anterior no hace nada (duerme) hasta que lo haga echo
.
Para su situación particular, simplemente configure su aplicación para escribir mensajes de registro en el archivo fifo. Si no puede configurarlo, simplemente elimine el archivo de registro original y cree un archivo fifo. Pero tenga en cuenta nuevamente que si el sed
bucle muere por alguna razón, su programa se bloqueará al intentar ingresar al write
archivo hasta que alguien lo haga read
desde el quinto.
El beneficio es la marca de tiempo actual evaluada y adjuntada a un mensaje a medida que el programa lo escribe en el archivo.
tailf
Para que la escritura en el registro y el procesamiento sean más independientes, puede usar dos archivos normales con tailf
. Una aplicación escribirá un mensaje en un archivo sin procesar y otro proceso leerá nuevas líneas (siga las escrituras de forma asincrónica) y procesará los datos con la escritura en el segundo archivo.
Tomemos un ejemplo:
# 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
Cómo funciona:
Ejecute el tailf
proceso que seguirá las escrituras bar.raw.log
e imprimirá a la salida estándar redirigida al while read ... echo
bucle infinito . Este bucle realiza dos acciones: leer datos desde la entrada estándar a una variable de búfer llamada line
y luego escribir la marca de tiempo generada con los siguientes datos almacenados en el búfer bar.log
.
Escribe algunos mensajes al bar.raw.log
. Debe hacer esto en una ventana de terminal separada porque la primera estará ocupada por la tailf
que seguirá las escrituras y hará su trabajo. Bastante sencillo.
La ventaja es que tu aplicación no se bloquearía si matas tailf
. Las desventajas son las marcas de tiempo menos precisas y la duplicación de archivos de registro.
tailf
, agregó la forma correcta de usarlo. En realidad, el camino con tailf
parece ser más elegante, pero salí del quinto camino con la esperanza de que sea útil para alguien.
Puede usar el ts
script perl de moreutils
:
$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
Modificado de la respuesta de Dmitry Vasilyanov.
En el script bash, puede redirigir y ajustar la salida con marca de tiempo línea por línea sobre la marcha.
Cuándo usar:
tailf
para el archivo de registro como dijo Dmitry Vasilyanov.Un ejemplo llamado 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"
Y el resultado:
$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar
Cómo funciona
exec &>
Redireccionar stdout y stderr al mismo lugar>( ... )
salidas de tubería a un comando interno asíncronoPor ejemplo:
marcar la hora de la tubería e iniciar sesión en el archivo
#!/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
O imprima la marca de tiempo e inicie sesión en 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
luego guardarlos en la /etc/crontab
configuración
* * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
Utilicé ts
esta manera para obtener una entrada con una marca de tiempo en un registro de errores para un script que utilizo para llenar Cacti con estadísticas de un host remoto.
Para probar Cacti, utilizo rand
para agregar algunos valores aleatorios que utilizo para los gráficos de temperatura para controlar la temperatura de mis sistemas.
Pushmonstats.sh es un script que recopila estadísticas de temperatura del sistema de mi PC y las envía a una Raspberry Pi en la que se ejecuta Cacti. Hace algún tiempo, la red estaba atascada. Solo obtuve tiempos de espera de SSH en mi registro de errores. Desafortunadamente, no hay entradas de tiempo en ese registro. No sabía cómo agregar una marca de tiempo a una entrada de registro. Entonces, después de algunas búsquedas en Internet, me topé con esta publicación y esto es lo que hice usando ts
.
Para probarlo, utilicé una opción desconocida para rand
. Lo que le dio un error a stderr. Para capturarlo, lo redirijo a un archivo temporal. Luego uso cat para mostrar el contenido del archivo y canalizarlo ts
, agregar un formato de hora que encontré en esta publicación y finalmente registrarlo en el archivo de error. Luego borro el contenido del archivo temporal, de lo contrario obtengo entradas dobles para el mismo error.
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
Esto da lo siguiente en mi registro de errores:
2014-03-22-19:17:53.823720 rand: unknown option -- '-l'
Quizás esta no sea una forma muy elegante de hacerlo, pero funciona. Me pregunto si hay un enfoque más elegante.