Detectar la rotación del archivo de registro (mientras se mira el archivo de registro para su modificación)

Uso el siguiente código para rastrear los inicios de sesión de ssh:

def follow(thefile): thefile.seek(0,2) while True: line = thefile.readline() if not line: time.sleep(0.1) continue yield line if __name__ == '__main__': logfile = open('/var/log/auth.log', 'r') loglines = follow(logfile) for line in loglines: print 'do something here' 

Me he dado cuenta de que este script de repente deja de funcionar después de un par de días. No recibo ningún error, no finaliza, simplemente deja de funcionar, como si readline() nunca regresara.

Así que echo 'test' >> auth.log.1 un echo 'test' >> auth.log.1 y esto, de hecho, termina siendo procesado por el script, porque hace tiempo que auth.log pasó a llamarse auth.log.1

¿Cómo puedo hacer un seguimiento de la rotación de un registro de este tipo y ajustarlo en consecuencia?

Usando la respuesta de e4c5, terminé con este código, que también resuelve el problema de llamar a readline() varias veces por segundo.

Durante la primera invocación, se salta al final del archivo y espera las modificaciones. Cuando el archivo se mueve, vuelve a abrir el archivo y lee todo el contenido, luego comienza a esperar.

 import os import time import traceback import threading import inotify.adapters logfile = b'/var/log/auth.log' #logfile = b'logfile.log' ################################################################## def process(line, history=False): if history: print '=', line.strip('\n') else: print '>', line.strip('\n') ################################################################## from_beginning = False notifier = inotify.adapters.Inotify() while True: try: #------------------------- check if not os.path.exists(logfile): print 'logfile does not exist' time.sleep(1) continue print 'opening and starting to watch', logfile #------------------------- open file = open(logfile, 'r') if from_beginning: for line in file.readlines(): process(line, history=True) else: file.seek(0,2) from_beginning = True #------------------------- watch notifier.add_watch(logfile) try: for event in notifier.event_gen(): if event is not None: (header, type_names, watch_path, filename) = event if set(type_names) & set(['IN_MOVE_SELF']): # moved print 'logfile moved' notifier.remove_watch(logfile) file.close() time.sleep(1) break elif set(type_names) & set(['IN_MODIFY']): # modified for line in file.readlines(): process(line, history=False) except (KeyboardInterrupt, SystemExit): raise except: notifier.remove_watch(logfile) file.close() time.sleep(1) #------------------------- except (KeyboardInterrupt, SystemExit): break except inotify.calls.InotifyError: time.sleep(1) except IOError: time.sleep(1) except: traceback.print_exc() time.sleep(1) ################################################################## 

Puedes echar un vistazo al inodo, del archivo.

 import os inode = os.stat('/var/log/auth.log').st_ino 

Cuando el inodo cambia, el archivo ha sido girado.

Esto se hace mejor con inotify, ya que no quiere seguir sondeando el sistema de archivos para preguntar si las cosas han cambiado durante cada iteración del bucle. Eso es mucho desperdicio de IO. inotify le notificará cuando ocurra un cambio. Hay un ejemplo del manual que muestra su uso con el archivo de registro.

Aparentemente, no puedo comentar hasta que tenga una reputación de = = 50.

@ daniel-f tiene un GRAN ejemplo! El único caso de borde que encontré es que, cuando el servicio que crea los archivos de registro rotativos que estoy leyendo se reinicia, borra los archivos antiguos y crea otros nuevos.

Esto hace que el ‘notificador’ pierda visibilidad en el archivo de registro (ya que es diferente).

Como el servicio escribe en el archivo de registro cada 60 segundos, hice una rápida modificación del bucle for, que se muestra a continuación:

 last_pull = datetime.datetime.now() while True: ... ... for event in notifier.event_gen(): if event is not None: last_pull = datetime.datetime.now() (header, type_names, watch_path, filename) = event if set(type_names) & set(['IN_MOVE_SELF']): # moved notifier.remove_watch(file_watcher.intput_logfile) file.close() time.sleep(1) break elif set(type_names) & set(['IN_MODIFY']): # modified lines = file.readlines() for line in lines: process(line, file_watcher, history=False) else: if (datetime.datetime.now() - last_pull).total_seconds() >= time_to_refresh: last_pull = datetime.datetime.now() notifier.remove_watch(file_watcher.intput_logfile) file.close() break 

Esto vuelve a ver el archivo después de 75 segundos sin una actualización.