工作流
理解log模块,首先应该理解其工作流,下面通过例子来说明其工作流
首先是main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import sub import sub2 import logging import logging.config from config import LOGGING logging.config.dictConfig(LOGGING) logger = logging.getLogger("main")
def main(): sub.sub1() logger.error("main debug")
if "__main__" == __name__: main()
|
在这里我们引入了sub 和sub2,先不理会其配置文件,假设已经写好配置文件了。
其次是sub.py 文件
1 2 3 4 5
| import logging sub_logger = logging.getLogger("main.sub") def sub1(): print("Hello This is sub1") sub_logger.debug("This is sub1")
|
我们可以看到,如果是多模块引入log机制,可以直接写成 如上面的形式。这种继承机制,让我们可以很方便的复用log.
理解配置文件
配置文件一般有两种:
字典形式
配置文件形式
至于哪种形式更好,就众说纷坛,有说字典形式比较好,因为都是python文件,我们写代码自定义程度更高,一种说配置文件形式其可读性更好。这里没有一个决断,我们首先使用字典形式,用代码来定义配置文件。
在配置文件里面,log有四个类,分别是
- formatters
- handlers
- filters
- loggers
首先我们来看一下formatters
1 2 3 4 5 6 7 8 9 10
| "formatters":{ # 标准模式 "standard":{ 'format':'[%(asctime)s][%(threadName)s:%(thread)d][%(name)s:%(levelname)s(%(lineno)d)]\n[%(module)s:%(funcName)s]:%(message)s' } # 简单模式 "brief":{ "format":'[%(message)s]' } }
|
其中 standard 和 brief 都是我们可以自定义的名字
接下来我们看一下formatters的格式化字符串有哪些,并且分别是什么含义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| %(name)s Logger的名字 %(levelno)s 数字形式的日志级别 %(levelname)s 文本形式的日志级别 %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有 %(filename)s 调用日志输出函数的模块的文件名 %(module)s 调用日志输出函数的模块名 %(funcName)s 调用日志输出函数的函数名 %(lineno)d 调用日志输出函数的语句所在的代码行 %(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示 %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数 %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒 %(thread)d 线程ID。可能没有 %(threadName)s 线程名。可能没有 %(process)d 进程ID。可能没有 %(message)s 用户输出的消息
|
handlers
handlers模块,顾名思义,就是处理log的模块,在这里我们要定义,log的输出是文件形式还是命令行输出形式,文件形式的话,其文件的存储路径,文件大小改如何分割等问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| "handlers":{ "console":{ 'level':'DEBUG', #输出信息的最低级别 'class':'logging.StreamHandler', 'formatter':'standard', #使用standard格式 'filters': ['require_debug_true',], #仅当 DEBUG = True 该处理器才生效,一般没用。 }, 'log':{ 'level':'DEBUG', 'class':'logging.handlers.RotatingFileHandler', 'formatter':'standard', 'filename':os.path.join(BASE_DIR, 'debug.log'), #输出位置 'maxBytes':1024*1024*5, #文件大小 5M 'backupCount': 5, #备份份数 'encoding': 'utf8', #文件编码 }, }
|
同理,这里的console和log也是我们自定义的名字,我们还可以定义其他的,然后选择日志的输出位置,或者设置变量为日志的输出位置
loggers
loggers是日志的全局管理器,我们会在这里定义一个管理器,在后面调用的时候,我们要获得这个管理器的名字。
1 2 3 4 5 6 7 8 9
| # 日志管理器集合 'loggers':{ #管理器 "main":{ 'handlers':['console', 'main_log'], 'level':'DEBUG', 'propagate':True, #是否传递给父记录器 }, }
|
其中 main 是我们自己定义的名字,需要注意的是,如果使用的logger没有在这里定义名字,那么这些配置将无用。
调用logger
这里其实也是前面有提到的东西,我们要引入logging和logging.config。(注意,不能只单纯引入logging,会报错)
在main.py中我们 这么引用
1 2 3 4 5
| import logging import logging.config from config import LOGGING logging.config.dictConfig(LOGGING) logger = logging.getLogger("main"
|
其中这个main 就是我们定义在配置文件中管理器的main
根据其工作流原理,其自模块也需要进行log记录,这个时候我们就不需要写配置文件了,直接使用这个配置文件即可
在sub.py中
1 2 3 4 5
| import logging sub_logger = logging.getLogger("main.sub") def sub1(): print("Hello This is sub1") sub_logger.debug("This is sub1")
|
总结
就这样,我们模拟了如何在python中使用日志记录,这对于开发来说是一件提高效率和产品质量很重要的事情
代码地址
https://github.com/TheFifthMan/python_modules
reference
http://yshblog.com/blog/125
https://segmentfault.com/a/1190000015143449