Python 内置logging 使用详细讲
logging 的主要作用
提供日志记录的接口和众多处理模块,供用户存储各种格式的日志,帮助调试程序或者记录程序运行过程中的输出信息。
logging 日志等级
logging 日志等级分为五个等级,优先级从高到低依次是 :
**CRITICAL; ** 程序严重错误
**ERROR; ** 程序错误/部分功能错误
**WARNING; ** 程序有发生错误的可能
**INFO; ** 程序正常运行时的信息
DEBUG 程序调试信息
默认的日志的记录等级为 WARNING, 即当日志的等级大于获等于 WARNING 时才会被记录。
一般常用的记录等级为 INFO,其用于记录程序的正常运行的一些信息(类似于print)。
当日志的等级达到 WARNING 以上时,表明此时程序不能正常运行;
logging 的基础函数
logging.basicConfig(**kwargs)
在没有显式的进行创建记录器(logger)时,会默认创建一个root logger,而logging.basicConfig(**kwargs) 可以创建带有默认的Formatter的streamHandle并将其添加到根日志记录器中来初始化基本配置。
比如
import logging
logging.debug('Debug code!')
logging.info('Run code!')
logging.warning('Watch out!')
logging.error('This is an error')
logging.critical('This is a ciritical')
上面代码中 logging 并没有显式的创建logger( logging.getLogger ), 其在直接使用debug(), info(), warning(), error(), critical() 时会使用默认的 root logger,并会自动调用 自定义的或者默认的logging.basicConfig(**kwargs) 初始化 root logger。
自定义的 logging.basicConfig(**kwargs) 中的参数 有以下的主要的选项:
参数 | 功能 |
---|---|
filename | 指定保存日志的文件名,用指定文件名创建一个FileHandler,记录的日志会保存到该文件中 |
format | 指定输出的格式和内容,默认是以冒号分割的levalname、name 和 message |
datefmt | 使用指定的日期/时间格式,与 time.strftime() 所接受的格式相同。 |
level | 指定根日志记录器级别,默认为 logging.WARNING |
stream | 指定日志的输出流,可以指定输出到sys.stderr,std.stdout 或 文件,默认输出到sys.stderr。使用指定的流初始化StramHandler,注意:stream和filename参数不兼容,如果两者同时使用,则会引发ValueError 错误 |
例如下面通过自定义 logging.basicConfig(**kwargs) 来初始化 root logger 来获得DEBUG级别及以上的日志记录并保存到 log.txt 文件中。
import logging
logging.basicConfig(filename='./log.txt',
format='%(asctime)s-%(name)s-%(levelname)s-%(message)s-%(funcName)s:%(lineno)d',
level=logging.DEBUG)
logging.debug('Debug code!')
logging.info('Run code!')
logging.warning('Watch out!')
logging.error('This is an error')
logging.critical('This is a ciritical')
logging 的四大组件(类)
Logger
除了根记录器(root logger)外,最主要的是可以自己创建日志记录器。
通过模块级别的函数 logging.getLogger(name)
实例化记录器
默认情况下,记录器采用层级结构,通过 .
来区分不同的层级。比如 有个名叫 foo
的记录器 则 foo.a
和 foo.b
都是 foo
的子级记录器。当然,最开始的或者说最上层的记录器就是 root logger。如果 name=None,构建的是root logger。
可以直接用当前模块的名称当作记录器的名字 logging.getLogger(__name__)
子级记录器通常不需要单独设置日志级别以及 Handler,如果子级记录器没有单独设置,则它的行为会委托给父级。比如说,记录器foo
的级别为INFO,而foo.a
和 foo.b
都不设置日志级别。此时foo.a
和 foo.b
会遵循foo
的级别设置,即只记录大于等于INFO级别的日志;而如果foo也没设置的话,就会找到根记录器root logger,root默认的级别为WARGING。
logger类的一些常用的方法
方法 | 功能描述 |
---|---|
Logger.setLevel() | 设置日志器(Logger)将会处理的日志消息级别 |
Logger.addHandler() | 添加一个handler对象 |
Logger.removeHandler() | 移除一个handler对象 |
Logger.addFilter() | 添加一个filter对象 |
Logger.removeFilter() | 移除一个filter对象 |
Logger.debug() | 设置DEBUG级别的日志记录 |
Logger.info() | 设置INFO级别的日志记录 |
Logger.warning() | 设置WARNING级别的日志记录 |
Logger.error() | 设置ERROR级别的日志记录 |
Logger.critical() | 设置CRITICAL级别的日志记录 |
Logger.exception() | 输出堆栈追踪信息 |
Logger.log() | 设置一个自定义的level参数来创建一个日志记录 |
logger 结合 后面要介绍的其他的三个组件可以实现以下的功能:
- Logger需要通过handler将日志信息输出到目标位置,目标位置可以是sys.stdout和文件等(这与logging.basicConfig(**kwargs) 设置中不太一致)。
- 一个Logger可以设置不同的Handler,而不同的Handler可以将日志输出到不同的位置(不同的日志文件),并且每个Handler都可以设置自己的filter从而实现日志过滤,保留实际项目中需要的日志。同时每个Handler也可以设置不同的Formatter,在每个Formatter实现同一条日志以不同的格式输出到不同的地方。
Handle
处理器;其可以控制记录的日志输出到什么地方(标准输出/文件/...),同时处理器也可以添加 过滤器(filter)和格式控制器(formatter)来控制输出的内容和输出的格式。
其具有几种常见的处理器:
logging.StreamHandler 标准流处理器,将消息发送到标准输出流、错误流 --> logging.StreamHandler(sys.stdout) # sys.stdout 表示的是指向控制台即标准输出;当我们在 Python 中打印对象调用 print obj 时候,事实上是调用了 sys.stdout.write(obj+'\n')。
print 将你需要的内容打印到了控制台,然后追加了一个换行符
logging.FileHandler 文件处理器,将消息发送到文件 --> logging.FileHandler(log_path)
logging.RotatingFileHandler 文件处理器,文件达到指定大小后,启用新文件存储日志
logging.TimedRotatingFileHandler 文件处理器,日志以特定的时间间隔轮换日志文件
handle 类的一些常用的方法
Handler.setLevel() | 设置处理器将会处理的日志消息的最低严重级别 |
Handler.setFormatter() | 为处理器设置一个格式对象 |
Handler.addFilter() | 为处理器添加一个过滤器对象 |
Handler.removeFilter() | 为处理器删除一个过滤器对象 |
logging.StramHandler() | 将日志消息发送到输出Stream,如std.out,std.err |
logging.FilterHandler() | 将日志消息发送到磁盘文件,默认情况文件大小会无线增长 |
RotationFileHandler() | 将日志消息发送到磁盘文件,支持日志文件按大小切割 |
TimeRotatingFileHandler() | 将日志消息发送到磁盘文件,并支持日志文件按时间切割 |
logging.handers.HTTPHandler() | 将日志消息通过GET或POST的方式发送给一个HTTP服务器 |
logging.handlers.SMTPHandler() | 将日志消息发送email地址 |
Filter
filter组件用来过滤 logger 对象,一个 filter 可以直接添加到 logger对象上,也可以添加到 handler 对象上,而如果在logger和handler中都设置了filter,则日志是先通过logger的filter,再通过handler的filter。由于所有的信息都可以经过filter,所以filter不仅可以过滤信息,还可以增加信息。
Filter 类的实例化对象可以通过 logging.Filter(name) 来创建,其中name 为 记录器的名字,如果没有创建过该名字的记录器,就不会输出任何日志:
filter = logging.Filter("foo.a")
基本过滤器类只允许低于指定的日志记录器层级结构中低于特定层级的事件,例如 这个用 foo.a
初始化的过滤器,则foo.a.b
;foo.a.c
等日志记录器记录的日志都可以通过过滤器,而foo.c
; a.foo
等就不能通过。如果name为空字符串,则所有的日志都能通过。
Filter 类 有 三个方法 :
- addFilter(filter) : 为 logger(logger..addFilter(filter)) 或者 handler(handler..addFilter(filter)) 增加过滤器
- removeFilter(filter) : 为 logger 或者 handler 删除一个过滤器
- filter(record) : 表示是否要记录指定的记录?返回零表示否,非零表示是。一般自定义Filter需要继承Filter基类,并重写filter方法
Formatter
格式化日志的输出;实例化:formatter = logging.Formatter(fmt=None,datefmt=None)
; 如果不指明 fmt,将默认使用 ‘%(message)s’ ,如果不指明 datefmt,将默认使用 ISO8601 日期格式。
其中 fmt 参数 有以下选项:
%(name)s | Logger的名字 |
---|---|
%(levelno)s | 数字形式的日志级别 |
%(levelname)s | 文本形式的日志级别;如果是logger.debug则它是DEBUG,如果是logger.error则它是ERROR |
%(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 | 用户输出的消息; 假如有logger.warning("NO Good"),则在%(message)s位置上是字符串NO Good |
例如:
formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s') # -表示右对齐 8表示取8位
handler.formatter = formatter
datefmt 参数 有以下选项:
参数 | 含义 |
---|---|
%y | 两位数的年份表示(00-99) |
%Y | 四位数的年份表示(000-9999) |
%m | 月份(01-12) |
%d | 月内中的一天(0-31) |
%H | 24小时制小时数(0-23) |
%I | 12小时制小时数(01-12) |
%M | 分钟数(00=59) |
%S 秒 | (00-59) |
例子:
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s","%Y%m%d-%H:%M:%S")
handler.formatter = formatter
logging 的配置
conf 形式的配置
在 loguser.conf 中 写入相关的信息
[loggers]
keys=root,fileLogger,rotatingFileLogger [handlers]
keys=consoleHandler,fileHandler,rotatingFileHandler [formatters]
keys=simpleFormatter [logger_root]
level=INFO
handlers=consoleHandler [logger_fileLogger]
level=INFO
handlers=fileHandler
qualname=fileLogger
propagate=0 [logger_rotatingFileLogger]
level=INFO
handlers=consoleHandler,rotatingFileHandler
qualname=rotatingFileLogger
propagate=0 [handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=simpleFormatter
args=(sys.stdout,) [handler_fileHandler]
class=FileHandler
level=INFO
formatter=simpleFormatter
args=("logs/fileHandler_test.log", "a") [handler_rotatingFileHandler]
class=handlers.RotatingFileHandler
level=WARNING
formatter=simpleFormatter
args=("logs/rotatingFileHandler.log", "a", 10*1024*1024, 50) [formatter_simpleFormatter]
format=%(asctime)s - %(module)s - %(levelname)s -%(thread)d : %(message)s
datefmt=%Y-%m-%d %H:%M:%S
在使用logger时,直接导入配置文件即可
from logging import config with open('./loguser.conf', 'r', encoding='utf-8') as f:
## 加载配置
config.fileConfig(f)
## 创建同名Logger,其按照配置文件的handle,formatter,filter方法初始化
logger = logging.getLogger(name="fileLogger")
yaml 形式配置文件
在 loguser.yaml文件 中 配置相关信息
version: 1
disable_existing_loggers: False
# formatters配置了日志输出时的样式
# formatters定义了一组formatID,有不同的格式;
formatters:
brief:
format: "%(asctime)s - %(message)s"
simple:
format: "%(asctime)s - [%(name)s] - [%(levelname)s] :%(levelno)s: %(message)s"
datefmt: '%F %T'
# handlers配置了需要处理的日志信息,logging模块的handler只有streamhandler和filehandler
handlers:
console:
class : logging.StreamHandler
formatter: brief
level : DEBUG
stream : ext://sys.stdout
info_file_handler:
class : logging.FileHandler
formatter: simple
level: ERROR
filename: ./logs/debug_test.log
error_file_handler:
class: logging.handlers.RotatingFileHandler
level: ERROR
formatter: simple
filename: ./logs/errors.log
maxBytes: 10485760 # 10MB #1024*1024*10
backupCount: 50
encoding: utf8 loggers:
#fileLogger, 就是在代码中通过logger = logging.getLogger("fileLogger")来获得该类型的logger
my_testyaml:
level: DEBUG
handlers: [console, info_file_handler,error_file_handler]
# root为默认情况下的输出配置, 当logging.getLogger("fileLoggername")里面的fileLoggername没有传值的时候,
# 就是用的这个默认的root,如logging.getLogger(__name__)或logging.getLogger()
root:
level: DEBUG
handlers: [console]
同样的可以通过导入 yaml 文件加载配置
with open('./loguser.yaml', 'r', encoding='utf-8') as f:
yaml_config = yaml.load(stream=f, Loader=yaml.FullLoader)
config.dictConfig(config=yaml_config) root = logging.getLogger()
# 子记录器的名字与配置文件中loggers字段内的保持一致
# loggers:
# my_testyaml:
# level: DEBUG
# handlers: [console, info_file_handler,error_file_handler]
my_testyaml = logging.getLogger("my_testyaml")
logging 和 print 的区别
看起来logging要比print复杂多了,那么为什么推荐在项目中使用 logging 记录日志而不是使用print 输出程序信息呢。
相比与print logging 具有以下优点:
- 可以通过设置不同的日志等级,在 release 版本中只输出重要信息,而不必显示大量的调试信息;
- print 将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging 则可以由开发者决定将信息输出到什么地方,以及怎么输出;
- 和 print 相比,logging 是线程安全的。(python 3中 print 也是线程安全的了,而python 2中的print不是)(线程安全是指在多线程时程序不会运行混乱;而python 2 中的print 分两步打印信息,第一打印字符串,第二打印换行符,如果在这中间发生线程切换就会产生输出混乱。这就是为什么python2的print不是原子操作,也就是说其不是线程安全的)
主要参考资料
https://blog.csdn.net/weixin_41010198/article/details/89356417
https://www.cnblogs.com/chenyibai/p/10676574.html
Python 内置logging 使用详细讲的更多相关文章
- Python内置logging模块
最近调试Python代码比较多,发现logging模块很好用. 我的用法如下: import logging logging.basicConfig(level=logging.INFO, forma ...
- python内置函数详细介绍
知识内容: 1.python内置函数简介 2.python内置函数详细介绍 一.python内置函数简介 python中有很多内置函数,实现了一些基本功能,内置函数的官方介绍文档: https: ...
- python内置函数,匿名函数
一.匿名函数 匿名函数:为了解决那些功能很简单的需求而设计的一句话函数 def calc(n): return n**n print(calc(10)) #换成匿名函数 calc = lambda n ...
- Python学习:6.python内置函数
Python内置函数 python内置函数,是随着python解释器运行而创建的函数,不需要重新定义,可以直接调用,那python的内置函数有哪些呢,接下来我们就了解一下python的内置函数,这些内 ...
- Python内置标准模块
time 模块 1 >>> import time 2 >>> time.time() 3 1491064723.808669 4 >>> # t ...
- python(16)- python内置函数
python内置了一系列的常用函数,以便于我们使用,python英文官方文档详细说明:https://docs.python.org/3/library/functions.html Buil ...
- Python入门之 Python内置函数
Python入门之 Python内置函数 函数就是以功能为导向,一个函数封装一个功能,那么Python将一些常用的功能(比如len)给我们封装成了一个一个的函数,供我们使用,他们不仅效率高(底层都是用 ...
- python内置函数
python内置函数 官方文档:点击 在这里我只列举一些常见的内置函数用法 1.abs()[求数字的绝对值] >>> abs(-13) 13 2.all() 判断所有集合元素都为真的 ...
- python 内置函数和函数装饰器
python内置函数 1.数学相关 abs(x) 取x绝对值 divmode(x,y) 取x除以y的商和余数,常用做分页,返回商和余数组成一个元组 pow(x,y[,z]) 取x的y次方 ,等同于x ...
随机推荐
- 技术分享 | WEB 端常见 Bug 解析
对于 WEB 产品来说,有一些常见的 Bug,本章节挑选一些比较典型的 Bug 进行举例介绍. UI Bug 页面展示的时候,需要根据长度的边界值去设计用例进行验证. 一般来说都会有超长内容的验证 ...
- Java基础语法Day_07(1-3 常用API第一部分)
常用API第一部分 第1节 Scanner类 day07_01_API概述和使用步骤(使用最基本的三个步骤 搜索 构造方法 方法) day07_02_Scanner概述及其API文档 ...
- Oracle 错误表
ORA-00001: 违反唯一约束条件 (.) ORA-00017: 请求会话以设置跟踪事件 ORA-00018: 超出最大会话数 ORA-00019: 超出最大会话许可数 ORA-00020: 超出 ...
- c# 一些警告的处理方法
在使用.Net 6开发程序时,发现多了很多新的警告类型.这里总结一下处理方法. CS8618 在退出构造函数时,不可为 null 的 属性"Name"必须包含非 null 值 经常 ...
- 常用的Linux 系统备份、恢复命令
公众号关注 「开源Linux」 回复「学习」,有我为您特别筛选的学习资料~ 删库跑路的事常常听说,不过,这只能是个调侃的话题,真正的工作中可不能这么干,否则,库是删了,路怕是跑不了了. 所以,备份很重 ...
- springboot处理blog字段
springboot处理blog字段 欢迎关注博主公众号「Java大师」, 专注于分享Java领域干货文章https://www.javaman.cn/ 1.数据库表结构 其中content为long ...
- CI/CD介绍以及jenkins安装 1.1
一 .CI/CD介绍 互联网软件开发和发布,形成了一套流程标准,分为几个阶段:编码,构建,集成,测试,交付,部署 持续集成(continuous integration).持续交付(conti ...
- 五、C++运算符重载,使面向对象编程更方便
复数类CComplex 编译器做对象运算的时候,会调用对象的运算符重载函数(优先调用成员方法):如果没有成员方法,就砸全局作用域找合适的运算符重载函数 ++和--运算符是单目运算符,在参数列表里放上一 ...
- linux下三种服务开机自启的方式
方式一.二.三适用于ubuntu,centos推荐使用方式二.方式三 方式一 在ubuntu系统中,如果你使用的apt方式安装的软件,可以使用如下方式直接添加服务的开机自启, 如果你是手动解压缩官网下 ...
- .NET MAUI 正式版GA发布
.NET MAUI – 一个代码库,多个平台 欢迎使用 .NET 多平台应用 UI.此版本标志着我们统一 .NET 平台的多年旅程中的新里程碑.现在,您和超过 500 万其他 .NET 开发人员拥有了 ...