我想将Python记录器包装在一个自定义类中,以嵌入一些特定于应用程序的功能,并隐藏开发人员的设置细节(设置文件输出,日志记录级别等).为此,我使用以下API创建了一个类:
__init__(log_level, filename)
debug(msg)
info(msg)
warning(msg)
error(msg)
Run Code Online (Sandbox Code Playgroud)
Logger.debug/info/warning/etc调用通常在日志中写入进行日志调用的函数和行号.但是,使用我的自定义类,写入日志文件的函数和行号始终相同(对应于自定义类中的debug()/ info()/ warning()/ error()函数).我希望它保存记录msg的应用程序代码行.那可能吗?
提前致谢.
Try ,它计算从原始日志记录调用到记录器的、等调用的stacklevel调用次数。日志记录3.8中的新增内容:debug()info()
第三个可选关键字参数是 stacklevel,默认为 1。如果大于 1,则在计算为日志记录事件创建的 LogRecord 中设置的行号和函数名称时,将跳过相应数量的堆栈帧。这可以用于记录帮助程序,以便记录的函数名称、文件名和行号不是帮助程序函数/方法的信息,而是其调用者的信息。该参数的名称反映了警告模块中的等效参数。
如果您愿意重新实现一点标准日志记录模块,则可以生成日志包装器。诀窍是编写自己的 findCaller() 方法,该方法知道在解释回溯时如何忽略日志包装器源文件。
在 logwrapper.py 中:
import logging
import os
import sys
from logging import *
# This code is mainly copied from the python logging module, with minor modifications
# _srcfile is used when walking the stack to check when we've got the first
# caller stack frame.
#
if hasattr(sys, 'frozen'): #support for py2exe
_srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:])
elif __file__[-4:].lower() in ['.pyc', '.pyo']:
_srcfile = __file__[:-4] + '.py'
else:
_srcfile = __file__
_srcfile = os.path.normcase(_srcfile)
class LogWrapper(object):
def __init__(self, logger):
self.logger = logger
def debug(self, msg, *args, **kwargs):
"""
Log 'msg % args' with severity 'DEBUG'.
To pass exception information, use the keyword argument exc_info with
a true value, e.g.
logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
"""
if self.logger.isEnabledFor(DEBUG):
self._log(DEBUG, msg, args, **kwargs)
def info(self, msg, *args, **kwargs):
"""
Log 'msg % args' with severity 'INFO'.
To pass exception information, use the keyword argument exc_info with
a true value, e.g.
logger.info("Houston, we have a %s", "interesting problem", exc_info=1)
"""
if self.logger.isEnabledFor(INFO):
self._log(INFO, msg, args, **kwargs)
# Add other convenience methods
def log(self, level, msg, *args, **kwargs):
"""
Log 'msg % args' with the integer severity 'level'.
To pass exception information, use the keyword argument exc_info with
a true value, e.g.
logger.log(level, "We have a %s", "mysterious problem", exc_info=1)
"""
if not isinstance(level, int):
if logging.raiseExceptions:
raise TypeError("level must be an integer")
else:
return
if self.logger.isEnabledFor(level):
self._log(level, msg, args, **kwargs)
def _log(self, level, msg, args, exc_info=None, extra=None):
"""
Low-level logging routine which creates a LogRecord and then calls
all the handlers of this logger to handle the record.
"""
# Add wrapping functionality here.
if _srcfile:
#IronPython doesn't track Python frames, so findCaller throws an
#exception on some versions of IronPython. We trap it here so that
#IronPython can use logging.
try:
fn, lno, func = self.findCaller()
except ValueError:
fn, lno, func = "(unknown file)", 0, "(unknown function)"
else:
fn, lno, func = "(unknown file)", 0, "(unknown function)"
if exc_info:
if not isinstance(exc_info, tuple):
exc_info = sys.exc_info()
record = self.logger.makeRecord(
self.logger.name, level, fn, lno, msg, args, exc_info, func, extra)
self.logger.handle(record)
def findCaller(self):
"""
Find the stack frame of the caller so that we can note the source
file name, line number and function name.
"""
f = logging.currentframe()
#On some versions of IronPython, currentframe() returns None if
#IronPython isn't run with -X:Frames.
if f is not None:
f = f.f_back
rv = "(unknown file)", 0, "(unknown function)"
while hasattr(f, "f_code"):
co = f.f_code
filename = os.path.normcase(co.co_filename)
if filename == _srcfile:
f = f.f_back
continue
rv = (co.co_filename, f.f_lineno, co.co_name)
break
return rv
Run Code Online (Sandbox Code Playgroud)
以及一个使用它的例子:
import logging
from logwrapper import LogWrapper
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(filename)s(%(lineno)d): "
"%(message)s")
logger = logging.getLogger()
lw = LogWrapper(logger)
lw.info('Wrapped well, this is interesting')
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2580 次 |
| 最近记录: |