python日志看起比较简单,要用起来稍微有点复杂,基础用法网上也介绍得比较多,下面就最近遇见的问题,作一个简单的介绍。就是在两个以上的python文件中要记录日志,怎么才能实现在一个地方配置,多个地方使用的情景。
直接上配置代码:
logging_config.py
# -*- coding: UTF-8 -*-
import logging
from logging import FileHandler
class LoggerTest:
@classmethod
def get_logger(cls,name):
#name表示模块的名字,调用函数时传入,以便知道来自哪里
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
# 文件处理器,设置的级别为INFO
file_handler = FileHandler(filename="logging_test.log")
# 创建一个格式器
formatter = logging.Formatter(fmt='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
# 作用在handler上
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
return logger
思路就是在一个单独文件里面,写一个类方法,其它需要用到日志记录的,都调用这个代码。
如:
logging_test2.py
# -*- coding: UTF-8 -*-
from logging_config import LoggerTest
class LoggingTest2:
logger = LoggerTest.get_logger(__name__)
def __init__(self):
self.logger.info("this is test2")
logging_test.py
# -*- coding: UTF-8 -*-
from logging_test2 import LoggingTest2
from logging_config import LoggerTest
class LoggingTest:
logger = LoggerTest.get_logger(__name__)
def __init__(self):
self.logger.info("this is test")
if __name__=='__main__':
LoggingTest()
LoggingTest2()
logging_test2.py、logging_test.py两个文件中,都用到了日志记录的功能,用到的日志对象都来自logging_config.py中的LoggerTest.get_logger,从而实现了只在一处配置的用法。
运行logging_test.py以后,就可以看到日志文件了:
可以看到,这两个py文件的日志都记录到logging_test.log文件中,结果还是比较理想。
现在我们来细究一个问题,那就是关于logging.getLogger(name)这个方法的应用,如果我们直接去掉参数name,程序照样能运行,但是会有些问题。
logger = logging.getLogger()
print(f"logger={logger}")
运行结果如下:
可以看出,如果不传名字name,那么logging会默认用RootLogger来替代,RootLogger的__repr__方法如下,输出类名、模块名和日志级别:
def __repr__(self):
level = getLevelName(self.getEffectiveLevel())
return '<%s %s (%s)>' % (self.__class__.__name__, self.name, level)
再来看看getLogger源代码:
root = RootLogger(WARNING)
Logger.root = root
Logger.manager = Manager(Logger.root)
def getLogger(name=None):
"""
Return a logger with the specified name, creating it if necessary.
If no name is specified, return the root logger.
"""
if name:
return Logger.manager.getLogger(name)
else:
return root
也就是说,有参数会生成 一个相应的Logger,没有就用默认的RootLogger代替。
上面说到如果没有传名字name,又在多个文件中引用了LoggerTest.get_logger这个方法的话,输出的日志会有些问题,重新运行logging_test.py,结果如下:
可以看到,每条日志输出了两次,所以需要在多个py文件在记录日志时,一定记得要传个名字(一般都用模块的名称)来获取logger。不然两个py文件的logger名字都叫root,那么在执行的时候会引起混乱。