有没有办法在记录器的实际格式部分中使用可变数据?
我希望我的日志包含堆栈中函数的名称。例如,下面是以下代码:
# logging_utils.py
def init_logger(logger_name: str) -> logging.Logger:
log = logging.getLogger(logger_name)
log.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)
formatter = logging.Formatter(f'[ %(asctime)s ] [ %(levelname)s ] [ %(callStack?)s ] %(message)s')
handler.setFormatter(formatter)
log.addHandler(handler)
return log
def format_stack() -> str:
return ':'.join(frame.function for frame in inspect.stack()[::-1][:-1]).replace('<module>:', '')
def f():
g()
def g():
h()
def h():
logger = init_logger('x')
logger.info('My actual message')
if __name__ == '__main__':
f()
我希望日志消息看起来像
[ 2019-02-18 14:14:23,558 ] [ INFO ] [ logging_utils:f:g:h ] My actual message
距离我最近的是使用这样的自定义类:
import sys
import inspect
import logging
class Logger:
_logger: logging.Logger
def __init__(self, name: str):
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)
formatter = logging.Formatter(f'[ %(asctime)s ] [ %(levelname)s ] %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
self._logger = logger
@staticmethod
def _format_stack_for_logger() -> str:
stack = inspect.stack()[::-1]
stack_names = (inspect.getmodulename(stack[0].filename),
*(frame.function
for frame
in stack[1:-3]))
return '::'.join(stack_names)
def _log(self, level: int, msg: str, *args, **kwargs):
self._logger.log(level, '[ %s ] %s', self._format_stack_for_logger(), msg, *args, **kwargs)
def debug(self, msg: str, *args, **kwargs):
self._log(logging.DEBUG, msg, *args, **kwargs)
def info(self, msg: str, *args, **kwargs):
self._log(logging.INFO, msg, *args, **kwargs)
def warning(self, msg: str, *args, **kwargs):
self.info(logging.WARNING, msg, *args, **kwargs)
def error(self, msg: str, *args, **kwargs):
self._log(logging.ERROR, msg, *args, **kwargs)
def critical(self, msg: str, *args, **kwargs):
self._log(logging.CRITICAL, msg, *args, **kwargs)
def f():
g()
def g():
h()
def h():
logger = Logger('x')
logger.info('My actual message :(')
if __name__ == '__main__':
f()
但是堆栈跟踪信息是...半硬编码(?)到日志记录字符串中。我正在寻找一种获得此结果的优雅方法。
谢谢!
编辑:
感谢@VinaySajip朝着正确的方向微移。
不幸的是,默认的Formatter
的{{1}}方法仅将format
的输出附加在要打印的字符串的末尾。无论您做什么,只要使用默认的formatStack
实现,就会发生这种情况。
我所做的是扩展format
类,重新实现了Formatter
方法。格式化方法的内容是从format
模块复制并粘贴的,占70%,但是删除了附加堆栈信息的部分。
相反,将logging
格式化好的结果(由新的inspect.stack()
返回)放置在将要格式化的formatStack
的{{1}}成员中。
之所以这样做,是因为要获得将要打印的最终消息,将stack_info
的词典用作LogRecord
的第二个操作数。因此,当格式字符串与LogRecord
的字典进行插值时,fmt_string % args
将包含格式正确的调用堆栈,因此真正要做的就是在其格式字符串中使用LogRecord
。>
基本上,我已经在格式化程序的格式字符串中添加了对LogRecord.stack_info
的支持。
代码:
%(stack_info)s
答案 0 :(得分:1)
最简单的方法是拥有一个自定义的Formatter
子类,该子类将覆盖here中记录的formatStack
方法。
将指定的堆栈信息(由
traceback.print_stack()
返回的字符串,但删除最后一个换行符)格式化为字符串。此默认实现只是返回输入值。
答案 1 :(得分:-1)
我无法发表评论,所以我使用的是答案:
这是我过去使用的装饰器,它记录执行函数及其args
def logging(f):
@wraps(f)
def wrapper(*args, **kwargs):
import logging
now = datetime.datetime.now()
time = now.strftime("%Y-%m-%d %H:%M")
logging.basicConfig(filename='logs/webservices.log',level=logging.INFO)
handle = f(*args, **kwargs)
logging.info(f'[{time}] - Executing instruction : [{" ".join((inspect.stack()[1][4][0]).split())}]')
logging.info(f'[{time}] - with args [{args}]')
logging.info(f'[{time}] - Returned result [{handle}]')
return handle
return wrapper