介绍:在现实生活中,记录日志非常重要,比如:银行转账时会有转账记录;飞机飞行过程中,会有个黑盒子(飞行数据记录器)记录着飞机的飞行过程,那在Python程序中想要记录程序在运行时所产生的日志信息,怎么做呢?可以使用logging模块完成。
print虽然也可以输出信息,但是print不知道日志记录的位置,也没有可读的日志格式,还不能把日志输出到指定文件。所以最佳的做法是使用内置的logging模块,因为logging模块为开发者提供了丰富的功能。
目录
日志级别
日志的使用
输出到控制台
日志等级和输出格式设置
日志数值设置日志级别
保存到日志文件
记录器(logger)
记录器层级示例
处理器(Handler)
按日期轮换日志文件(TimedRotatingFileHandler)
按文件大小轮换日志文件(RotatingFileHandler)
格式器(formatter)
logger.basicConfig
Flask完整的大小轮换日志案例
日志配置文件
目的:
- 可以很方便的了解程序的运行情况
- 可以分析用户的操作行为、喜好等信息
- 方便开发人员检查BUG
日志级别
日志等级分为5个,从低到高分别是:
- DEBUG:程序调试bug时使用,用来记录详细的信息,方便定位问题,在生产环境下一般不开启DEBUG;
- INFO:程序正常运行时使用,用来记录关键代码点的信息,生产环境下常设置为INFO级别;
- WARNING:程序未能按照预期运行时使用,但并不报错,如:用户登录密码错误;
- ERROR:程序出现错误时使用,导致某些功能不能正常运行时记录的信息,如IO操作失败;
- CRITICAL:特别严重问题,导致程序不能继续运行时使用,如:磁盘空间为空,一般很少使用;
说明:默认是WARNING等级,当在WAENING或WAENING之上等级的才记录日志信息。
日志的使用
在logging模块中记录日志的方式有两种:“输出到控制台”和“保存到日志文件”。
注意:logging和print虽然类似,但是括号中只能输出一个字符串,不能用“,”隔开,可以使用“+”拼接成一个字符串。
输出到控制台
说明:日志信息默认只显示大于等于WAENING级别的日志,这说明默认的日志级别设置为WAENING。
1
2
3
4
5
6
7import logging logging.debug('这是一个debug级别的日志信息') logging.info('这是一个info级别的日志信息') logging.warning('这是一个warning级别的日志信息') logging.error('这是一个error级别的日志信息') logging.critical('这是一个critical级别的日志信息')
1
2
3WARNING:root:这是一个warning级别的日志信息 ERROR:root:这是一个error级别的日志信息 CRITICAL:root:这是一个critical级别的日志信息
日志等级和输出格式设置
1
2
3
4
5
6
7
8
9import logging logging.basicConfig( level=日志等级, format=输出格式, datefmt=日期时间格式, encoding=日志编码格式, filename=日志文件, filemode=日志文件写入模式, )
参数 | 说明 |
level | 日志等级,如:logger.DEBUG, logger.INFO, logger.WARN, logger.ERROR, logger.CRITICAL |
format | 日志的输出格式 |
%(levelname)s:打印日志级别名称,如DEBUG, INFO, WARNING, ERROR, CRITICAL | |
%(filename)s:打印当前执行程序名 | |
%(pathname)s:打印当前执行程序路径 | |
%(lineno)d:打印日志的当前行号 | |
%(asctime)s:打印日志的时间,默认形式'2022-05-13 20:05:45,896'(逗号后的数字为毫秒) | |
%(message)s:打印日志信息 | |
%(remote_add)s:打印日志的请求访问的主机IP | |
%(url)s:打印日志的要请求的路由 | |
%(name)s: 用于记录日志的记录器名称 | |
%(process)d: 进程ID(如果可用) | |
%(thread)d: 线程ID(如果可用) | |
datefmt | 格式化日志的展示时间,如:'%Y-%m-%d %H:%M:%S' |
encoding | 日志编码格式(Python3.9才有) |
filename | 日志文件路径,如:'./django.log' |
filemode | 日志文件写入模式,与open方法相同,如:每次重启程序覆盖之前的日志'w',追加'a' |
1
2
3
4
5
6
7
8
9
10
11
12
13
14import logging # 设置日志等级和输出日志格式 logging.basicConfig( level=logging.DEBUG, # 设置输出日志级别 # 时间 - 程序名[行号] - 日志级别:信息 format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s' ) logging.debug('这是一个debug级别的日志信息') logging.info('这是一个info级别的日志信息') logging.warning('这是一个warning级别的日志信息') logging.error('这是一个error级别的日志信息') logging.critical('这是一个critical级别的日志信息')
1
2
3
4
52020-12-28 17:02:39,442 - 测试.py[line: 7] - DEBUG: 这是一个debug级别的日志信息 2020-12-28 17:02:39,442 - 测试.py[line: 8] - INFO: 这是一个info级别的日志信息 2020-12-28 17:02:39,442 - 测试.py[line: 9] - WARNING: 这是一个warning级别的日志信息 2020-12-28 17:02:39,443 - 测试.py[line: 10] - ERROR: 这是一个error级别的日志信息 2020-12-28 17:02:39,443 - 测试.py[line: 11] - CRITICAL: 这是一个critical级别的日志信息
日志数值设置日志级别
日志级别数值 | ||||||
级别 | critical | error | warning | info | debug | notset |
数值 | 50 | 40 | 30 | 20 | 10 | 0 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import logging format = logging.Formatter( # 时间 - 日志器名 - 日志级别 - 文件路径 - 行号: 信息 '%(asctime)s - %(name)s - %(levelname)s - %(pathname)s - %(lineno)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) sh = logging.StreamHandler() # 控制台日志记录器 sh.setFormatter(format) # 对象的形式设置日志格式 logger = logging.getLogger("MyLog") # 设置全局日志工具对象 logger.setLevel(10) # 设置日志级别 logger.addHandler(sh) # 添加日志记录器到全局工具对象 logger.info("这是普通级别的日志信息") logger.warning("这是警告级别的日志信息") logger.critical("这是极重要级别的日志信息") try: a = 2 / 0 except ZeroDivisionError as e: logger.error("错误信息:" + str(e))
1
2
3
42020-12-28 17:15:07 - MyLog - INFO - c:UsershpDesktop测试.py - 13: 这是普通级别的日志信息 2020-12-28 17:15:07 - MyLog - WARNING - c:UsershpDesktop测试.py - 14: 这是警告级别的日志信息 2020-12-28 17:15:07 - MyLog - CRITICAL - c:UsershpDesktop测试.py - 15: 这是极重要级别的日志信息 2020-12-28 17:15:07 - MyLog - ERROR - c:UsershpDesktop测试.py - 20: 错误信息:division by zero
保存到日志文件
将异常信息输出到控制台的做法在生产环境中并不实用,使用logging将异常信息保存到外部文件,更利于运维人员发现错误。
1
2
3
4
5
6
7
8
9
10
11
12
13
14import logging logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s', filename="log.txt", filemode="w" # 每次重启程序,覆盖之前的日志 ) logging.debug('这是一个debug级别的日志信息') logging.info('这是一个info级别的日志信息') logging.warning('这是一个warning级别的日志信息') logging.error('这是一个error级别的日志信息') logging.critical('这是一个critical级别的日志信息')
记录器(logger)
前面的日志记录,其实都是通过一个叫做日志记录器(Logger)的实例对象创建的,每个记录器都有一个名称,直接使用logging来记录日志时,系统会默认创建名为root的记录器。记录器支持层级结构,子记录器通常不需要单独设置日志级别以及Handler(处理器),如果自己记录器没有单独设置,则它的行为会委托给父级。
1
2import logging logger = logging.getLogger(__name__)
默认情况下,记录器是层级结构,会根据项目的层级分层,子记录器会集成父级记录器的配置。
记录器层级示例
在foo.py中设置记录器的级别为INFO
1
2
3
4
5
6
7import logging logging.basicConfig() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) logger.info('this is foo')
在子文件bar.py中的记录器不设置日志级别,默认是WARGING
1
2
3
4import logging logger = logging.getLogger(__name__) logger.info('this is bar')
其他子模块都是bar.py一样类似的代码,没有设置日志级别,最后输出的结果如下:
1
2
3
4INFO:foo:this is foo INFO:foo.bar:this is bar INFO:foo.bam:this is bam INFO:foo.bar.baz:this is baz
发现都可以输出日志级别为INFO的日志,这是因为子文件没有设置日志级别,就会向上找已经设置了日志级别的日志器,这里刚好找到了foo的级别为INFO,如果foo也没有设置的话,就会找到跟记录器root,root默认级别为WARGING。
处理器(Handler)
记录器负责日志的记录,但是日志最终记录到哪里并不关心,而是交给处理器(handler)去处理。
例如一个Flask项目,你可能会将INFO级别的日志记录到文件,将ERROR级别的日志记录到标准输出,将某些关键日志(例如订单或者严重错误)发送到某个邮件地址。这时候你的记录器添加多个不同的处理器来处理不同的消息日志,以此根据消息的重要性发送到特定的位置。
logging中内置了很多使用的处理器,常用的有:
- StreamHandler标准流处理器,将消息发送到标准的输出流、错误流;
- FileHandler文件处理器,将文件发送到文件;
- RotatingFileHandler文件处理器,文件达到指定大小后,启用新文件春初日志;
- TimedRotatingFileHandler文件处理器,日志以特定的时间间隔轮换日志文件;
Handler提供了4个方法,setLevel, setFormatter, addFilter, removeFilter
下方案例通过setLevel可以将记录器的不同级别的消息发送到不同的地方去。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import logging from logging import StreamHandler from logging import FileHandler logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # 设置为DEBUG级别 # 标准流处理器,设置级别为WARNING stream_handler = StreamHandler() stream_handler.setLevel(logger.WARNING) logger.addHandler(stream_handler) # 文件处理器,设置的级别为INFO file_handler = FileHandler(filename='test.log') file_handler.setLevel(logging.INFO) logger.debug('this is debug') logger.info('this is info') looger.warning('this is warning') logger.error('this is error')
1
2
3
4
5
6
7
8# 命令行窗口输出的日志为 this is warning this is error # 日志文件中输出的日志为 this is info this is warning this is error
尽管我们将logger的级别设置成了DEBUG,但是debug记录的消息并没有输出,因为我们给两个Handler设置的级别都比DEBUG高,所以消息被过滤掉了。同时也说明了同一条日志可以被同时记录在不同的地方。
按日期轮换日志文件(TimedRotatingFileHandler)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import logging from logging import handlers class Logger(object): def __init__(self, log_name, level): self.logger = logging.getLogger(log_name) # 获取记录器,并设置记录器的名字 # 设置格式 format = logging.Formatter( """%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s""") # 设置显示级别 self.logger.setLevel(level) # 实例将消息发送到硬盘文件,以特定的时间间隔轮换日志文件,具体看官方文档 tfh = handlers.TimedRotatingFileHandler(filename=log_name, when="D", backupCount=2, encoding="utf-8") tfh.setFormatter(format) self.logger.addHandler(tfh) if __name__ == "__main__": log = Logger("python_log.log", level=logging.DEBUG) try: a = 2/0 except ZeroDivisionError as e: log.logger.error("错误信息是:{0}".format(e))
按文件大小轮换日志文件(RotatingFileHandler)
1
2
3
4
5
6
7
8
9
10# 设置日志记录等级 logging.basicConfig(level=logging.DEBUG) # 调试debug级 # 创建日志处理器,指明日志保存路径、每个日志文件的最大大小、保存的日志文件个数上限 file_log_handler = RotatingFileHandler("logs/log", maxBytes=1024 * 1024 * 100, backupCount=10) # 创建日志记录的格式 formatter = logging.Formatter("%(levelname)s %(filename)s:%(lineno)sd %(message)s") # 为刚创建的日志处理器设置日志记录格式 file_log_handler.setFormatter(formatter) # 为全局的日志工具对象添加日志处理器 logging.getLogger().addHandler(file_log_handler)
格式器(formatter)
在logging.basicConfig中已经配置过日志格式,其实格式器还可以以对象的形式在Handler上设置。格式器可以指定日志的输出格式,如是否展示时间、设置时间格式、要不要展示日志级别、是否展示记录器名称等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import logging from logging import StreamHandler logger = logging.getLogger(__name__) # 标准流处理器 stream_handler = StreamHandler() stream_handler.setLevel(logging.WARNING) # 创建一个格式器 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 作用在handler上 stream_handler.setFormatter(formatter) # 添加处理器 logger.addHandler(stream_handler) logger.info('this is info') logger.warning('this is warning') logger.error('this is error')
注意:
- 格式器只能作用在处理器上,通过处理器的setFormatter方法设置格式器。而且一个Handler只能设置一个格式器;
- logger与格式器是一对多的关系,一个logger可以添加多个格式器(handler);
- handler和logger都可以设置日志等级。
logger.basicConfig
最开始将如何配置logging时,使用的是logger.basicConfig。现在上面已经说了记录器、处理器和格式器,现在我们在来看看logger.basicConfig()方法为我们干了什么。
- 创建一个root记录器
- 设置root记录器的日志级别为warning
- 为root记录器添加StreamHandler处理器
- 为处理器设置一个格式器
1
2
3
4import logging logging.basicConfig() logging.warning('this is warning')
这两行代码其实就等价于:
1
2
3
4
5
6
7
8
9
10import logging logger = logging.getLogger('root') # 设置名为root的记录器 logger.setLevel(logging.WARNING) # 设置日志级别为WARNING handler = logging.StreamHandler(sys.stderr) logger.addHandler(handler) # 添加一个控制台处理器 formatter = logging.Formatter('%(levelname)s:%(name)s:%(message)s') handler.setFormatter(formatter) # 设置格式器 logger.warning('this is warning')
logger.basicConfig()方法做的事情是相当于给日志系统做一个最基本的配置,方便开发者快速上手使用,他必须在开始记录日之前调用。
注意:不过如果root记录器已经指定有其他处理器,这是用再调用basicConfig,该方法失效。
Flask完整的大小轮换日志案例
在框架中使用日志说明:logging日志配置信息在程序入口模块设置一次,整个程序都可以生效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47import os import logging import logging.handlers from flask import request class RequestFormatter(logging.Formatter): # 针对请求信息的日志格式 def format(self, record): record.url = request.url record.remote_addr = request.remote_addr return super().format(record) def create_logger(app): logging_level = app.config['LOGGING_LEVEL'] # 等级 logging_file_dir = app.config['LOGGING_FILE_DIR'] # 路径 logging_file_backup = app.config['LOGGING_FILE_BACKUP'] # 备份 logging_file_max_bytes = app.config['LOGGING_FILE_MAX_BYTES'] # 最大占用空间 # 日志信息会输出到控制中,如果stream为空则默认输出到sys.stderr。 flask_console_handler = logging.StreamHandler() # 为处理器设置格式 flask_console_handler.setFormatter( logging.Formatter('%(levelname)s %(pathname)s %(lineno)d %(message)s')) # 级别 + 模块 + 行号 + 信息 # 针对请求信息的日志格式:时间+ +请求地址+级别+模块+行号+信息 request_formatter = RequestFormatter( '[%(asctime)s] %(remote_addr)s requested %(url)sn%(levelname)s in %(pathname)s %(lineno)d: %(message)s') # flask.log循环日志文件 使用文件大小处理器 flask_file_handler = logging.handlers.RotatingFileHandler( filename=os.path.join(logging_file_dir, 'flask.log'), # 日志文件路径 maxBytes=logging_file_max_bytes, # 日志最大字节数 backupCount=logging_file_backup, # 做多保留几个日志 encoding="UTF8", # 日志编码 delay=True # 默认False,如果为True,则文件打开会被推迟至第一次调用(注意,在Flask中如果不设置为True,当文件满了,切换时会一直切不到空文件中) ) flask_file_handler.setFormatter(request_formatter) # 设置文件日志的记录格式 # 获取flask.app日志 log_flask_app = logging.getLogger('apps') # 获取全局日志工具对象 log_flask_app.addHandler(flask_file_handler) # 为全局日志工具对象添加日志记录器 log_flask_app.setLevel(logging_level) # 设置级别 # 获取输入到控制台的日志 if app.debug: log_flask_app.addHandler(flask_console_handler) # 如果是DEBUG模式,为全局日志对象增加控制台日志记录器
在Flask项目中,可以使用以下两种方式使用日志对象:
1
2
3
4
5import logging from flask import current_app logging.info("使用方式1") current_app.logger.debug("使用方式2")
日志配置文件
日志的配置除了前面介绍的将配置直接写在代码中,还可以将配置信息单独放在配置文件中,实现配置与代码分离。
日志配置文件logging.conf:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21[loggers] keys=root [handlers] keys=consoleHandler [formatters] keys=simpleFormatter [logger_root] level=DEBUG handlers=consoleHandler [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=simpleFormatter args=(sys.stdout,) [formatter_simpleFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
加载配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import logging import logging.config # 加载配置文件 logging.config.fileConfig('logging.conf') # 创建logger logger = logging.getLogger() # 输出日志 logger.debug('这是一个debug级别的日志信息') logger.info('这是一个info级别的日志信息') logger.warning('这是一个warning级别的日志信息') logger.error('这是一个error级别的日志信息') logger.critical('这是一个critical级别的日志信息')
1
2
3
4
52022-05-13 21:50:07,123 - root - DEBUG - 这是一个debug级别的日志信息 2022-05-13 21:50:07,123 - root - INFO - 这是一个info级别的日志信息 2022-05-13 21:50:07,123 - root - WARNING - 这是一个warning级别的日志信息 2022-05-13 21:50:07,123 - root - ERROR - 这是一个error级别的日志信息 2022-05-13 21:50:07,123 - root - CRITICAL - 这是一个critical级别的日志信息
最后
以上就是俊逸黑裤最近收集整理的关于Python logging 日志日志级别日志的使用记录器(logger)处理器(Handler)格式器(formatter)logger.basicConfigFlask完整的大小轮换日志案例日志配置文件的全部内容,更多相关Python内容请搜索靠谱客的其他文章。
发表评论 取消回复