¿Cómo forzar un nombre rotativo con el TimedRotatingFileHandler de python?

Estoy tratando de usar TimedRotatingFileHandler para mantener registros diarios en archivos de registro separados. La rotación funciona perfectamente según lo previsto, pero lo que no me gusta es el nombre de los archivos.

Si configuro un archivo de registro como my_log_file.log, este será el archivo de registro “de hoy”, y cuando cambie el día a la medianoche, se cambiará el nombre a my_log_file.log.2014-07-08 sin la extensión .log al final y se my_log_file.log un nuevo my_log_file.log para el nuevo día.

Lo que me gustaría obtener es que se my_log_file.2014-07-08.log nombre del archivo antiguo a my_log_file.2014-07-08.log o incluso my_log_file-2014-07-08.log , principalmente con .log al final, no en el medio. Además, me gustaría que el archivo de registro “de hoy” ya tenga el nombre de la fecha de hoy, al igual que los anteriores.

¿Hay alguna manera de hacerlo?

Encontré que puedo personalizar el sufijo con:

handler.suffix = “% Y-% m-% d”

Pero no consigo la forma de eliminar la parte interna de .log y de forzar al archivo de registro actual a tener el sufijo agregado.

He creado una clase ParallelTimedRotatingFileHandler destinada principalmente a permitir la escritura de múltiples procesos en paralelo a un archivo de registro. Los problemas con procesos paralelos resueltos por esta clase, son:

  • El momento de reinversión en el que todos los procesos intentan copiar o cambiar el nombre del mismo archivo al mismo tiempo, genera errores.
  • La solución para este problema fue exactamente la convención de nomenclatura que sugieres. Por lo tanto, para un Service nombre de archivo que suministre en el controlador, el registro no va, por ejemplo, a Service.log sino hoy a Service.2014-08-18.log y mañana a Service.2014-08-19.log .
  • Otra solución es abrir los archivos en a (anexo) en lugar de w para permitir escrituras paralelas.
  • La eliminación de los archivos de copia de seguridad también debe hacerse con precaución, ya que varios procesos paralelos eliminan los mismos archivos al mismo tiempo.
  • Esta implementación no tiene en cuenta los segundos de salto (que no es un problema para Unix). En otros sistemas operativos, puede que todavía sea 30/6/2008 23:59:60 en el momento de la reinversión, por lo que la fecha no ha cambiado, por lo que tomamos el mismo nombre de archivo que ayer.
  • Sé que la recomendación estándar de Python es que el módulo de logging no está previsto para procesos paralelos, y debería usar SocketHandler, pero al menos en mi entorno, esto funciona.

El código es solo una pequeña variación del código en el módulo handlers.py de Python estándar. Por supuesto, los derechos de autor de los titulares de derechos de autor.

Aquí está el código:

 import logging import logging.handlers import os import time import re class ParallelTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler): def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, postfix = ".log"): self.origFileName = filename self.when = when.upper() self.interval = interval self.backupCount = backupCount self.utc = utc self.postfix = postfix if self.when == 'S': self.interval = 1 # one second self.suffix = "%Y-%m-%d_%H-%M-%S" self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$" elif self.when == 'M': self.interval = 60 # one minute self.suffix = "%Y-%m-%d_%H-%M" self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}$" elif self.when == 'H': self.interval = 60 * 60 # one hour self.suffix = "%Y-%m-%d_%H" self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}$" elif self.when == 'D' or self.when == 'MIDNIGHT': self.interval = 60 * 60 * 24 # one day self.suffix = "%Y-%m-%d" self.extMatch = r"^\d{4}-\d{2}-\d{2}$" elif self.when.startswith('W'): self.interval = 60 * 60 * 24 * 7 # one week if len(self.when) != 2: raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when) if self.when[1] < '0' or self.when[1] > '6': raise ValueError("Invalid day specified for weekly rollover: %s" % self.when) self.dayOfWeek = int(self.when[1]) self.suffix = "%Y-%m-%d" self.extMatch = r"^\d{4}-\d{2}-\d{2}$" else: raise ValueError("Invalid rollover interval specified: %s" % self.when) currenttime = int(time.time()) logging.handlers.BaseRotatingHandler.__init__(self, self.calculateFileName(currenttime), 'a', encoding, delay) self.extMatch = re.compile(self.extMatch) self.interval = self.interval * interval # multiply by units requested self.rolloverAt = self.computeRollover(currenttime) def calculateFileName(self, currenttime): if self.utc: timeTuple = time.gmtime(currenttime) else: timeTuple = time.localtime(currenttime) return self.origFileName + "." + time.strftime(self.suffix, timeTuple) + self.postfix def getFilesToDelete(self, newFileName): dirName, fName = os.path.split(self.origFileName) dName, newFileName = os.path.split(newFileName) fileNames = os.listdir(dirName) result = [] prefix = fName + "." postfix = self.postfix prelen = len(prefix) postlen = len(postfix) for fileName in fileNames: if fileName[:prelen] == prefix and fileName[-postlen:] == postfix and len(fileName)-postlen > prelen and fileName != newFileName: suffix = fileName[prelen:len(fileName)-postlen] if self.extMatch.match(suffix): result.append(os.path.join(dirName, fileName)) result.sort() if len(result) < self.backupCount: result = [] else: result = result[:len(result) - self.backupCount] return result def doRollover(self): if self.stream: self.stream.close() self.stream = None currentTime = self.rolloverAt newFileName = self.calculateFileName(currentTime) newBaseFileName = os.path.abspath(newFileName) self.baseFilename = newBaseFileName self.mode = 'a' self.stream = self._open() if self.backupCount > 0: for s in self.getFilesToDelete(newFileName): try: os.remove(s) except: pass newRolloverAt = self.computeRollover(currentTime) while newRolloverAt <= currentTime: newRolloverAt = newRolloverAt + self.interval #If DST changes and midnight or weekly rollover, adjust for this. if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc: dstNow = time.localtime(currentTime)[-1] dstAtRollover = time.localtime(newRolloverAt)[-1] if dstNow != dstAtRollover: if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour newRolloverAt = newRolloverAt - 3600 else: # DST bows out before next rollover, so we need to add an hour newRolloverAt = newRolloverAt + 3600 self.rolloverAt = newRolloverAt 

Por lo que sé, no hay manera de lograr esto directamente.

Una solución que podría probar es anular el comportamiento predeterminado.

  • Cree su propia TimedRotatingFileHandler class y anule la función doRollover() function.
  • Compruebe la fuente en su instalación de python /Lib/logging/handlers.py

Algo como esto:

 class MyTimedRotatingFileHandler(TimedRotatingFileHandler): def __init__(self, **kwargs): TimedRotatingFileHandler.__init__(self, **kwargs) def doRollover(self): # Do your stuff, rename the file as you want