所以现在我们有很多python脚本,我们正在尝试整合它们并修复和裁员.我们要做的一件事是确保所有sys.stdout/sys.stderr进入python日志记录模块.
现在最重要的是,我们希望打印出以下内容:
[<ERROR LEVEL>] | <TIME> | <WHERE> | <MSG>
Run Code Online (Sandbox Code Playgroud)
现在所有的python错误消息中的所有sys.stdout/sys.stderr消息的格式都是[LEVEL] - MSG,它们都是使用sys.stdout/sys.stderr编写的.我可以在我的sys.stdout包装器和sys.stderr包装器中解析这个问题.然后根据解析的输入调用相应的日志记录级别.
所以基本上我们有一个名为foo的包,以及一个名为log的子包.在__init__.py我们定义以下内容:
def initLogging(default_level = logging.INFO, stdout_wrapper = None, \
stderr_wrapper = None):
"""
Initialize the default logging sub system
"""
root_logger = logging.getLogger('')
strm_out = logging.StreamHandler(sys.__stdout__)
strm_out.setFormatter(logging.Formatter(DEFAULT_LOG_TIME_FORMAT, \
DEFAULT_LOG_TIME_FORMAT))
root_logger.setLevel(default_level)
root_logger.addHandler(strm_out)
console_logger = logging.getLogger(LOGGER_CONSOLE)
strm_out = logging.StreamHandler(sys.__stdout__)
#strm_out.setFormatter(logging.Formatter(DEFAULT_LOG_MSG_FORMAT, \
# DEFAULT_LOG_TIME_FORMAT))
console_logger.setLevel(logging.INFO)
console_logger.addHandler(strm_out)
if stdout_wrapper:
sys.stdout = stdout_wrapper
if stderr_wrapper:
sys.stderr = stderr_wrapper
def cleanMsg(msg, is_stderr = False):
logy = logging.getLogger('MSG')
msg = msg.rstrip('\n').lstrip('\n')
p_level = r'^(\s+)?\[(?P<LEVEL>\w+)\](\s+)?(?P<MSG>.*)$'
m = re.match(p_level, msg)
if m:
msg = m.group('MSG')
if m.group('LEVEL') in ('WARNING'):
logy.warning(msg)
return
elif m.group('LEVEL') in ('ERROR'):
logy.error(msg)
return
if is_stderr:
logy.error(msg)
else:
logy.info(msg)
class StdOutWrapper:
"""
Call wrapper for stdout
"""
def write(self, s):
cleanMsg(s, False)
class StdErrWrapper:
"""
Call wrapper for stderr
"""
def write(self, s):
cleanMsg(s, True)
Run Code Online (Sandbox Code Playgroud)
现在我们将在我们的一个脚本中调用它,例如:
import foo.log
foo.log.initLogging(20, foo.log.StdOutWrapper(), foo.log.StdErrWrapper())
sys.stdout.write('[ERROR] Foobar blew')
Run Code Online (Sandbox Code Playgroud)
哪个将转换为错误日志消息.喜欢:
[ERROR] | 20090610 083215 | __init__.py | Foobar Blew
Run Code Online (Sandbox Code Playgroud)
现在的问题是当我们这样做时,记录错误消息的模块现在是__init__(对应于foo.log.__init__.py文件),这违背了整个目的.
我尝试对stderr/stdout对象进行deepCopy/shallowCopy,但这没有做任何事情,它仍然说模块中出现了消息__init__.py.我怎么能这样做,所以这不会发生?
问题是,日志记录模块正在查看调用堆栈中的单个层以查找谁调用它,但现在您的函数是该点的中间层(尽管我已经预期它会报告cleanMsg,而不是__init__,因为那是你的位置'调用log()).相反,你需要它上升两个级别,否则将你的调用者传递给记录的消息.您可以通过自己检查堆栈帧并抓取调用函数,将其插入到消息中来完成此操作.
要查找调用框架,可以使用inspect模块:
import inspect
f = inspect.currentframe(N)
Run Code Online (Sandbox Code Playgroud)
将查找N帧,并返回帧指针.即你的直接调用者是currentframe(1),但如果这是stdout.write方法,你可能不得不再去另一帧.拥有调用框架后,您可以获取正在执行的代码对象,并查看与其关联的文件和函数名称.例如:
code = f.f_code
caller = '%s:%s' % (code.co_filename, code.co_name)
Run Code Online (Sandbox Code Playgroud)
您可能还需要放置一些代码来处理调用您的非python代码(即C函数或内置函数),因为这些代码可能缺少f_code对象.
或者,按照mikej的回答,您可以在继承自logging.Logger的自定义Logger类中使用相同的方法,该类覆盖findCaller以向上导航多个帧,而不是一个.