赞
踩
一个成熟的软件产品,日志是必不可少的一部分,在python中我们怎么实现产品级别的日志输出呢?
python有一个内置模块logging,用来输出日志信息,可以进行各种配置,能满足我们大部分场景对日志的需求。
导入logging模块,直接输出5个级别的日志信息,我们看一下会输出什么
- # -*- coding: utf-8 -*-
- import logging
-
-
- if __name__ == '__main__':
- logging.debug('debug级别,一般用来打印一些调试信息,级别最低')
- logging.info('info级别,一般用来打印一些正常的操作信息')
- logging.warning('waring级别,一般用来打印警告信息')
- logging.error('error级别,一般用来打印一些错误信息')
- logging.critical('critical级别,一般用来打印一些致命的错误信息,等级最高')
会发现只输出下面三条信息,这是因为logging是分级别的,上面5个级别的信息从上到下依次递增,可以通过设置logging的level,使其只打印某个级别以上的信息。因为默认等级是 WARNING,所以只有 WARNING 以上级别的日志被打印出来。
如果我们想把debug和info也打印出来,可以使用 basicConfig 对其进行配置:
基本上日志都是分级别的,这样可以让我们在不同的时期关注不同的重点,比如我们把一些调试的信息以debug的级别输出,并且把 logging 的 level 设为 DEBUG,这样我们以后不需要显示这些日志的时候,只需要把level设置为info或者更高即可。
我们发现上面的日志输出信息很简略,暂时还不能满足我们的需求,比如我们可能需要输出该条信息的时间,所在位置等等,这同样可以通过basicConfig进行配置。
format 可以指定输出的内容和格式,其内置的参数如下:
- %(name)s:Logger的名字
- %(levelno)s:打印日志级别的数值
- %(levelname)s:打印日志级别的名称
- %(pathname)s:打印当前执行程序的路径,其实就是sys.argv[0]
- %(filename)s:打印当前执行程序名
- %(funcName)s:打印日志的当前函数
- %(lineno)d:打印日志的当前行号
- %(asctime)s:打印日志的时间
- %(thread)d:打印线程ID
- %(threadName)s:打印线程名称
- %(process)d:打印进程ID
- %(message)s:打印日志信息
- # -*- coding: utf-8 -*-
- import logging
-
-
- if __name__ == '__main__':
- logging.basicConfig(format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
- level=logging.DEBUG) # 配置输出格式、配置日志级别
- logging.debug('debug级别,一般用来打印一些调试信息,级别最低')
- logging.info('info级别,一般用来打印一些正常的操作信息')
- logging.warning('waring级别,一般用来打印警告信息')
- logging.error('error级别,一般用来打印一些错误信息')
- logging.critical('critical级别,一般用来打印一些致命的错误信息,等级最高')
以上我们只是把日志输出到控制台,但很多时候我们可能会需要把日志存到文件,这样程序出现问题时,可以方便我们根据日志信息进行定位。
最简单的方式是在basicConfig中配置filename和filemode:
- # -*- coding: utf-8 -*-
- import logging
-
-
- if __name__ == '__main__':
- logging.basicConfig(format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
- level=logging.DEBUG,
- filename='demo.log',
- filemode='a') # 配置输出格式、日志级别、存储文件及文件打开模式
- logging.debug('debug级别,一般用来打印一些调试信息,级别最低')
- logging.info('info级别,一般用来打印一些正常的操作信息')
- logging.warning('waring级别,一般用来打印警告信息')
- logging.error('error级别,一般用来打印一些错误信息')
- logging.critical('critical级别,一般用来打印一些致命的错误信息,等级最高')
这样就输出到文件中了
其中参数 filemode表示文件打开模式,不设的话默认为’a’,即追加模式,可以不设;也可以设为’w’,每次写日志会覆盖之前的日志。
但是进行这样的操作之后,我们会发现控制台不输出了,怎么做到既输出到控制台又写入到文件呢?
以上我们只是使用logging进行非常简单的操作,但这样作用有限,其实 logging 库采取了模块化的设计,提供了许多组件:记录器、处理器、过滤器和格式化器。
Logger 暴露了应用程序代码能直接使用的接口,用于记录日志。
Handler 将(记录器产生的)日志记录发送至合适的目的地(控制台、文件等)。
Filter 提供了更好的粒度控制,它可以决定输出哪些日志记录。
Formatter 指明了最终输出中日志记录的内容和格式。
简单地说,其中 Logger 是负责记录日志消息的,然后我们要把这些日志消息放到哪里,交给 Handler 处理,Filter 则帮我们过滤信息(不限于通过级别过滤),Formatter 就是跟上面的 format 一个意思,用来设置日志内容和格式。
这样,我们试一下使用模块的方式,重新记录日志:
- # -*- coding: utf-8 -*-
- import logging
-
-
- if __name__ == '__main__':
- logger = logging.getLogger()
-
- logger.debug('debug级别,一般用来打印一些调试信息,级别最低')
- logger.info('info级别,一般用来打印一些正常的操作信息')
- logger.warning('waring级别,一般用来打印警告信息')
- logger.error('error级别,一般用来打印一些错误信息')
- logger.critical('critical级别,一般用来打印一些致命的错误信息,等级最高')
首先第一行 getLogger 获取了一个记录器,其中命名标识了这个 Logger。然后下面的输出方式跟我们一开始 logging 的用法是很相似的,看起来是不是很简单。我们没有配置handler,它默认输出到控制台。
那我们就给他加一个Handler吧,Handler的种类有很多,常用的有4种:
- logging.StreamHandler -> 控制台输出
- logging.FileHandler -> 文件输出
- logging.handlers.RotatingFileHandler -> 按照大小自动分割日志文件,一旦达到指定的大小重新生成文件
- logging.handlers.TimedRotatingFileHandler -> 按照时间自动分割日志文件
现在我们先使用最简单的StreamHandler把日志输出到控制台:
- # -*- coding: utf-8 -*-
- import logging
-
- if __name__ == '__main__':
- logger = logging.getLogger('demo')
- stream_handler = logging.StreamHandler()
- logger.addHandler(stream_handler)
-
- logger.debug('debug级别,一般用来打印一些调试信息,级别最低')
- logger.info('info级别,一般用来打印一些正常的操作信息')
- logger.warning('waring级别,一般用来打印警告信息')
- logger.error('error级别,一般用来打印一些错误信息')
- logger.critical('critical级别,一般用来打印一些致命的错误信息,等级最高')
因为我们没有设置日志级别(第一层过滤,第二层过滤;第一层一般设为debug,第二层自定义)
使用Formatter模块设置一下输出格式
同时将日志输出到控制台和文件(需要两个头)
- # -*- coding: utf-8 -*-
- import logging
-
-
- if __name__ == '__main__':
- logger = logging.getLogger('demo')
- logger.setLevel(level=logging.DEBUG)#相当于第一层过滤网
-
- formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
-
- #输出到文件
- file_handler = logging.FileHandler('demo2.log')#设置文件名,模式,编码
- file_handler.setLevel(level=logging.INFO)#相当于第二层过滤网;第一层之后的内容再次过滤。
- file_handler.setFormatter(formatter)
-
- # 输出到控制台
- stream_handler = logging.StreamHandler()
- stream_handler.setLevel(logging.DEBUG)#相当于第二层过滤网;第一层之后的内容再次过滤。
- stream_handler.setFormatter(formatter)
-
- logger.addHandler(file_handler)
- logger.addHandler(stream_handler)
-
- logger.debug('debug级别,一般用来打印一些调试信息,级别最低')
- logger.info('info级别,一般用来打印一些正常的操作信息')
- logger.warning('waring级别,一般用来打印警告信息')
- logger.error('error级别,一般用来打印一些错误信息')
- logger.critical('critical级别,一般用来打印一些致命的错误信息,等级最高')
- # -*- coding: utf-8 -*-
- import logging
- from logging import handlers
-
-
- if __name__ == '__main__':
- logger = logging.getLogger('demo')
- logger.setLevel(level=logging.DEBUG)
-
- formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
-
- file_handler = logging.FileHandler('demo2.log')
- file_handler.setLevel(level=logging.INFO)
- file_handler.setFormatter(formatter)
-
- stream_handler = logging.StreamHandler()
- stream_handler.setLevel(logging.DEBUG)
- stream_handler.setFormatter(formatter)
-
- # 实例化TimedRotatingFileHandler
- # interval是时间间隔
- # backupCount是备份文件的个数,如果超过这个个数,就会自动删除
- # when是间隔的时间单位,单位有以下几种:S 秒;M 分;H 小时;D 天;W 每星期(interval==0时代表星期一);midnight 每天凌晨
- time_rotating_handler = handlers.TimedRotatingFileHandler(
- filename='demo3.log',
- when='D',
- backupCount=10,
- encoding='utf-8') # 往文件里写入#指定间隔时间自动生成文件的处理器
- time_rotating_handler.setFormatter(formatter) # 设置文件里写入的格式
-
- logger.addHandler(file_handler)
- logger.addHandler(stream_handler)
- logger.addHandler(time_rotating_handler)
-
- logger.debug('debug级别,一般用来打印一些调试信息,级别最低')
- logger.info('info级别,一般用来打印一些正常的操作信息')
- logger.warning('waring级别,一般用来打印警告信息')
- logger.error('error级别,一般用来打印一些错误信息')
- logger.critical('critical级别,一般用来打印一些致命的错误信息,等级最高')
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。