Reputation: 823
I'm using the basic logging python module to add log.
I decide to create a log class, as follow, named Log.py
:
class Log:
def __init__(self, path):
self.LOGGER = logging.getLogger('Open-Capture')
if self.LOGGER.hasHandlers():
self.LOGGER.handlers.clear() # Clear the handlers to avoid double logs
logFile = RotatingFileHandler(path, mode='a', maxBytes=5 * 1024 * 1024,
backupCount=2, encoding=None, delay=0)
formatter = logging.Formatter('[%(threadName)-14s] [%(filename)s:%(lineno)-15s] %(asctime)s %(levelname)s %(message)s', datefmt='%d-%m-%Y %H:%M:%S')
logFile.setFormatter(formatter)
self.LOGGER.addHandler(logFile)
self.LOGGER.setLevel(logging.DEBUG)
def info(self, msg):
self.LOGGER.info(msg)
def error(self, msg):
self.LOGGER.error(msg)
As you can see, I have the %(filename)s:%(lineno)
vars to add more lisibility. But in my log file I have Log.py:34
instead of filename and line number of the source file because when I log I call it like this :
Log = Log(Config.cfg['GLOBAL']['logfile'])
Log.info('test log')
Is there any way to had the source file on my log file instead of Log.py
?
Thanks in advance
Upvotes: 1
Views: 2709
Reputation: 2525
Yes it is possible, althrough I'm quite sure the implementation I'm posting here is not 100% safe and sure there is a better / more elegant implementation, take this as a hint.
To get the filename and line# of the caller you can use the inspect
module, to add those custom infos to your logs you can add a custom Filter
:
import logging
from logging.handlers import RotatingFileHandler
from inspect import getframeinfo, stack
class CallerFilter(logging.Filter):
""" This class adds some context to the log record instance """
file = ''
line_n = ''
def filter(self, record):
record.file = self.file
record.line_n = self.line_n
return True
def caller_reader(f):
"""This wrapper updates the context with the callor infos"""
def wrapper(self, *args):
caller = getframeinfo(stack()[1][0])
self._filter.file = caller.filename
self._filter.line_n = caller.lineno
return f(self, *args)
return wrapper
class Log:
def __init__(self, path):
self.LOGGER = logging.getLogger('Open-Capture')
if self.LOGGER.hasHandlers():
self.LOGGER.handlers.clear() # Clear the handlers to avoid double logs
logFile = RotatingFileHandler(path, mode='a', maxBytes=5 * 1024 * 1024,
backupCount=2, encoding=None, delay=0)
formatter = logging.Formatter('[%(threadName)-14s] [%(file)s:%(line_n)-15s] %(asctime)s %(levelname)s %(message)s', datefmt='%d-%m-%Y %H:%M:%S')
logFile.setFormatter(formatter)
self.LOGGER.addHandler(logFile)
# Here we add the Filter, think of it as a context
self._filter = CallerFilter()
self.LOGGER.addFilter(self._filter)
self.LOGGER.setLevel(logging.DEBUG)
@caller_reader
def info(self, msg):
self.LOGGER.info(msg)
@caller_reader
def error(self, msg):
self.LOGGER.error(msg)
from log import Log
log = Log('l.log')
log.info('LOG MESSAGE')
log.error('LOG MESSAGE 2 ')
log.info('LOG MESSAGE 2 ')
output:
[MainThread ] [script_file.py:4 ] 17-01-2020 16:34:36 INFO LOG MESSAGE
[MainThread ] [script_file.py:5 ] 17-01-2020 16:34:36 ERROR LOG MESSAGE 2
[MainThread ] [script_file.py:6 ] 17-01-2020 16:34:36 INFO LOG MESSAGE 2
Upvotes: 4