暂停日志格式化后再恢复
我有一个日志配置,既可以把日志记录到文件里,也可以在控制台上显示:
logging.basicConfig(filename=logfile, filemode='w',
level=numlevel,
format='%(asctime)s - %(levelname)s - %(name)s:%(funcName)s - %(message)s')
# add console messages
console = logging.StreamHandler()
console.setLevel(logging.INFO)
consoleformatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console.setFormatter(consoleformatter)
logging.getLogger('').addHandler(console)
在我脚本的某个地方,我需要和用户互动,打印一个总结并询问确认。这个总结现在是通过在一个循环中打印出来的。我想暂停当前控制台日志的格式,这样我就可以打印一大段文字,最后加一个问题,并等待用户输入。但我仍然希望所有这些内容都能记录到文件里!
实现这个功能的代码在一个模块里,我尝试了以下方法:
logger = logging.getLogger(__name__)
def summaryfunc:
logger.info('normal logging business')
clearformatter = logging.Formatter('%(message)s')
logger.setFormatter(clearformatter)
logger.info('\n##########################################')
logger.info('Summary starts here')
结果出现了错误:AttributeError: 'Logger' object has no attribute 'setFormatter'
我明白日志记录器就是日志记录器,而不是处理器,但我不太确定怎么才能让它正常工作……
编辑:
根据大家的回答,我的问题变成了:在与用户互动时,如何暂停控制台的日志记录,同时仍然能够记录到文件。也就是说:只暂停streamHandler。因为这个操作发生在一个模块里,处理器的具体设置在别的地方定义,所以这是我实现的方法:
logger.debug('Normal logging to file and console')
root_logger = logging.getLogger()
stream_handler = root_logger.handlers[1]
root_logger.removeHandler(stream_handler)
print('User interaction')
logger.info('Logging to file only')
root_logger.addHandler(stream_handler)
logger.info('Back to logging to both file and console')
这个方法依赖于streamHandler总是位于handlers
返回的列表中的第二个位置,但我相信这是正确的,因为它是我添加处理器到根日志记录器时的顺序……
2 个回答
日志记录不应该用来提供你程序的实际输出——也就是说,不管日志功能是否开启,程序的运行结果应该是一样的。所以我建议你还是继续之前的做法,也就是在循环中使用打印输出。
我同意Vinay的观点,正常的程序输出应该用print
,而logging
则只用于记录日志。不过,如果你想在中间切换格式,然后再切换回来,可以这样做:
import logging
def summarize():
console_handler.setFormatter(logging.Formatter('%(message)s'))
logger.info('Here is my report')
console_handler.setFormatter(console_formatter)
numlevel = logging.DEBUG
logfile = 's2.log'
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler()
console_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)
file_handler = logging.FileHandler(filename=logfile, mode='w')
file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s:%(funcName)s - %(message)s')
file_handler.setFormatter(file_formatter)
logger.addHandler(file_handler)
logger.info('Before summary')
summarize()
logger.info('After summary')
讨论
- 这个脚本创建了一个日志记录器对象,并为它分配了两个处理器:一个用于控制台,一个用于文件。
- 在
summarize()
函数中,我为控制台处理器切换了一个新的格式化器,进行了一些日志记录,然后又切换回来了。 - 再次提醒,
logging
不应该用来显示正常的程序输出。
更新
如果你想暂时关闭控制台日志记录,然后再打开,可以参考以下建议:
def interact():
# Remove the console handler
for handler in logger.handlers:
if not isinstance(handler, logging.FileHandler):
saved_handler = handler
logger.removeHandler(handler)
break
# Interact
logger.info('to file only')
# Add the console handler back
logger.addHandler(saved_handler)
注意,我没有测试logging.StreamHandler
的处理器,因为logging.FileHandler
是从logging.StreamHandler
派生出来的。因此,我移除了那些不是FileHandler
的处理器。在移除之前,我保存了那个处理器,以便后续恢复。
更新 2: .handlers = []
在主脚本中,如果你有:
logger = logging.getLogger(__name__) # __name__ == '__main__'
那么在一个模块中,你可以这样做:
logger = logging.getLogger(__name__) # __name__ == module's name, not '__main__'
问题是,在脚本中,__name__ == '__main__'
,而在模块中,__name__ == <模块的名称>
,而不是'__main__'
。为了保持一致性,你需要想一个名字,并在两个地方都使用:
logger = logging.getLogger('MyScript')