I'm trying to set up a particular logging scheme for an application I'm building. For it, I want to be able to rotate logs arbitrarily on a custom condition. As such, the built-in options of rotating based on time (using TimedRotatingFileHandler
) or log size (using RotatingFileHandler
) are not sufficient. Both TimedRotatingFileHandler
and RotatingFileHandler
do however have the method doRollover
which I could use to implement what I want. The problem comes from that I'm using logging.config.dictConfig
to set up my log configuration, like so:
config = {
"version": 1,
"formatters": {
"default_formatter": {
"format": logging.BASIC_FORMAT,
},
},
"handlers": {
"file": {
"class": "logging.handlers.RotatingFileHandler",
"level": "NOTSET",
"formatter": "default_formatter",
"backupCount": 5,
"filename": "log.txt",
"encoding": "utf8",
},
},
"loggers": {
"": {
"level": "NOTSET",
"handlers": ["file"],
},
},
}
logging.config.dictConfig(config)
This way, logging.config.dictConfig
is responsible for instantiating RotatingFileHandler
, and so I never get the chance to retain a reference to the class instance (i.e., the object). As such, it is not clear how I could go about calling methods upon the object.
How could I go about calling a method (in my case, doRollover
) on an object instantiated as a handler by logging.config.dictConfig
? Alternatively, if that is not possible, how can I manually provide a handler object that I have instantiated by calling the constructor directly given this configuration?
This can be done by assessing the handlers
property of the logger the handler is installed onto. In the code snippet above, the root logger is assigned the handler, and so we can get it like so:
file_handler_name = "file"
root_logger = logging.getLogger()
file_handler = next(
handler for handler in root_logger.handlers if handler.name == file_handler_name
)
file_handler.doRollover() # or whatever other method you want to call
This works because in the question's example, the handler is named "file", and in here we essentially iterate through root_logger.handlers
until we find one where the name
property is equal to "file". The handler returned by this iteration is then the object that was instantiated by logging.config.dictConfig
.
If using next
isn't to your liking, the equivalent with a for-loop would be this:
file_handler_name = "file"
root_logger = logging.getLogger()
for handler in root_logger.handlers:
if handler.name == "file":
file_handler = handler
break
file_handler.doRollover() # or whatever other method you want to call