Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Write a header at every logfile that is created with a time-rotating logger

Tags:

python

logging

I've made a time-rotating logger which creates a new logfile at midnight. In my logfile I want to write a header at the top of every file. I'm looking for an efficient way to call a function which writes this header to the logfile upon the moment that the file is created.

import logging
from logging.handlers import TimedRotatingFileHandler

# create time-rotating log handler
logHandler = TimedRotatingFileHandler(logfile, when='midnight')

# create logger
self.log = logging.getLogger('MyTimeRotatingLogger')
self.log.addHandler(logHandler)
like image 308
Gio Avatar asked Jan 08 '15 12:01

Gio


2 Answers

I've solved it! Basically all that needs to be done is overriding the doRollover method of the TimedRotatingFileHandler, also some code is needed in this new parent class in order to pass the logging instance and set the header content. Hereby an extensive example for people who encounter a similar situation.

from logging.handlers import TimedRotatingFileHandler

class MyTimedRotatingFileHandler(TimedRotatingFileHandler):
    def __init__(self, logfile, when, interval):
        super(MyTimedRotatingFileHandler, self).__init__(logfile, when, interval)
        self._header = ""
        self._log = None

    def doRollover(self):
        super(MyTimedRotatingFileHandler, self).doRollover()
        if self._log is not None and self._header != "":
            self._log.info(self._header)

    def setHeader(self, header):
        self._header = header

    def configureHeaderWriter(self, header, log):
        self._header = header
        self._log = log

# create time-rotating log handler
logHandler = MyTimedRotatingFileHandler(logfile, when='midnight')
form = '%(asctime)s %(name)s %(levelname)s: %(message)s'
logFormatter = logging.Formatter(form)
logHandler.setFormatter(logFormatter)

# create logger
log = logging.getLogger('MyLogger')
logHandler.configureHeaderWriter('test-header', log)
log.addHandler(logHandler)
log.setLevel(logging.INFO)
like image 196
Gio Avatar answered Oct 10 '22 19:10

Gio


The solutions above cover the majority of use cases, but if you want to still respect the behavior of the FileHandler.delay attribute, I think the simplest solution is to override the _open() method:

class MyTimedRotatingFileHandler(TimedRotatingFileHandler):
    def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None, header=''):
        self.header = header
        super().__init__(filename, when, interval, backupCount, encoding, delay, utc, atTime)
    def _open(self):
        stream = super()._open()
        if self.header and stream.tell() == 0:
            stream.write(self.header + self.terminator)
            stream.flush()
        return stream

This way you could avoid ending up with "empty" log files containing only the header, and you don't have to worry about passing specific loggers to the handler.

like image 20
rjw Avatar answered Oct 10 '22 19:10

rjw