Python 日志

1. logging levels

官网: https://docs.python.org/3/tutorial/stdlib2.html#logging

print()替换为logging是第3种方式,和assert比,logging不会抛出错误,而且可以输出到文件:

LevelNumeric value何时使用
CRITICAL50严重错误,表明软件已不能继续运行了。
ERROR40由于更严重的问题,软件已不能执行一些功能了。
WARNING30表明发生了一些意外,或者不久的将来会发生问题(如‘磁盘满了’)。软件还是在正常工作。
INFO20证明事情按预期工作。
DEBUG10详细信息,典型地调试问题时会感兴趣。
NOTSET0所有的消息都要记录,这只对根logger有效。

级别依次变高:

import logging

logging.debug('Debugging information')
logging.info('Informational message')
logging.warning('Warning:config file %s not found', 'server.conf')
logging.error('Error occurred')
logging.critical('Critical error -- shutting down')</pre>

默认输出:

WARNING:root:Warning:config file server.conf not found
ERROR:root:Error occurred
CRITICAL:root:Critical error -- shutting down

这里写图片描述

2. logging组件

logging模块与log4j的机制是一样的,只是具体的实现细节不同。模块提供logger,handler,filter,formatter。

LevelNumeric value何时使用
logger记录器暴露了应用程序代码能直接使用的接口,应用程序往往通过调用它提供的api来记录日志
handler处理器将(记录器产生的)日志记录发送至合适的目的地
filter过滤器提供了更好的粒度控制,它可以决定输出哪些日志记录
formatter格式化器指明了最终输出中日志记录的布局

2.1 logger 对象

  • logger不可直接实例化

logger从来不会直接被实例化,只会通过模块级方法logging.getLogger(name)来实例化。如果在多模块调用中getLogger(name)使用了同样的name,那么将会返回对同一个logger对象的引用

  • logger的name是有层级关系的

对于namefoo的logger,foo.bar,foo.bar.baz都是继承于它的
loggername的分层结果有点像Python的package。特别是每个模块中使用 logging.getLogger(__name__) ,因为__name__就是模块的名称

2.1.1 创建logger实例

Logger是一个树形层级结构,在使用接口debug,info,warn,error,critical之前必须创建Logger实例,即创建一个记录器

创建方法:

logger = logging.getLogger(logger_name)

创建Logger实例后,可以使用以下方法进行日志级别设置,增加处理器Handler。

  • logger.setLevel(logging.ERROR)
    设置日志级别为ERROR,即只有日志级别大于等于ERROR的日志才会输出

  • logger.addHandler(handler_name)
    为Logger实例增加一个处理器

  • logger.removeHandler(handler_name)
    为Logger实例删除一个处理器

2.1.2 默认logger实例

如果没有显式的进行创建,则默认以下项:
- 创建一个root logger
- 日志级别(WARN)
- 处理器Handler(StreamHandler,即将日志信息打印输出在标准输出上)
- 格式化器Formatter(默认的格式即为第一个简单使用程序中输出的格式)。

2.1.3 logger的方法

logger.propagate

  • 如果表达式是TRUE,

  • logger.setLevel(lvl)

  • Logger.isEnabledFor(lvl)

lvl级别的消息可以被这个logger处理。

这个方法首先要检查模块级的设置logging.disable(lvl),然后检查这个logger的getEffectiveLevel()方法

  • logger.getEffectiveLevel()

表明这个logger有效的级别

2.2 handler对象

Handler处理器类型有很多种,比较常用的有三个,StreamHandlerFileHandlerNullHandler,详情可以访问Python logging.handlers

  • StreamHandler

创建方法:

sh = logging.StreamHandler(stream=None)

创建StreamHandler之后,可以通过使用以下方法设置日志级别,设置格式化器Formatter,增加或删除过滤器Filter。

ch.setLevel(logging.WARN) # 指定日志级别,低于WARN级别的日志将被忽略
ch.setFormatter(formatter_name) # 设置一个格式化器formatter
ch.addFilter(filter_name) # 增加一个过滤器,可以增加多个
ch.removeFilter(filter_name) # 删除一个过滤器
  • FileHandler

创建方法:

fh = logging.FileHandler(filename, mode='a', encoding=None, delay=False)

# filename是全路径或者相对路径
  • NullHandler

NullHandler类位于核心logging包,不做任何的格式化或者输出。
本质上它是个“什么都不做”的handler,由库开发者使用。

2.3 Formatter对象

使用Formatter对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%S。

创建方法:

formatter = logging.Formatter(fmt=None, datefmt=None)

其中,fmt是消息的格式化字符串,datefmt是日期字符串。如果不指明fmt,将使用’%(message)s’。如果不指明datefmt,将使用ISO8601日期格式。

2.4 Filter对象

Handlers和Loggers可以使用Filters来完成比级别更复杂的过滤。Filter基类只允许特定Logger层次以下的事件。例如用‘A.B’初始化的Filter允许Logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’等记录的事件,logger‘A.BB’, ‘B.A.B’ 等就不行。 如果用空字符串来初始化,所有的事件都接受。

创建方法:

filter = logging.Filter(name='')

2.5 各组件关系

Logger是一个树形层级结构;

Logger可以包含一个或多个Handler和Filter,即Logger与Handler或Fitler是一对多的关系;

一个Logger实例可以新增多个Handler,一个Handler可以新增多个格式化器或多个过滤器,而且日志级别将会继承。

这里写图片描述

3. 配置使用

配置方式
配置方式方式方式
显式创建显示创建记录器Logger、处理器Handler和格式化器Formatter,并进行相关设置;使用的是logging组件类的方法
简单方式使用basicConfig()函数直接进行配置使用的是模块级别的方法
配置文件使用fileConfig()函数读取配置文件;
配置字典使用dictConfig()函数读取配置信息;
通过网络进行配置使用listen()函数进行网络配置。

3.1 显示配置

使用的是四大组件

import logging

# create logger

logger_name = "example"
logger = logging.getLogger(logger_name)
logger.setLevel(logging.WARN)

# create file handler

log_path = "./log.log"
filehandler = logging.FileHandler(log_path)
filehandler.setLevel(logging.ERROR)

# create formatter

fmt='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s'
datefmt='%a, %d %b %Y %H:%M:%S'
formatter = logging.Formatter(fmt,datefmt)

# add handle and formatter to logger

filehandler.setFormatter(formatter)
logger.addHandler(filehandler)

# print log info

logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')
  • 显示创建logger
  • 显示创建Handler
  • 显示创建Formatter
  • 将Handler添加Formatter
  • 将logger添加Handler
    (关系参考3.5节图)

3.2 简单配置

使用的都是模块级别logging的函数

logging.basicConfig函数各参数:

  • format: 指定输出的格式和内容,format可以输出很多有用信息,如上例所示:
    • %(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: 打印日志信息
  • filename: 指定日志文件名
  • filemode: 和file函数意义相同,指定日志文件的打开模式,’w’或’a’
  • datefmt: 指定时间格式,同time.strftime()
  • level: 设置日志级别,默认为logging.WARNING
  • stream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略
import logging

############################这个配置会将日志打印在文件中#################################
logging.basicConfig(level=logging.DEBUG,
                format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                datefmt='%a, %d %b %Y %H:%M:%S',
                filename='myapp.log',
                filemode='w')
#######################################################################################


##############################这个配置会将日志打印在屏幕上###############################
#定义一个StreamHandler,将INFO级别或更高的日志信息打印到标准错误,并将其添加到当前的日志处理对象#
console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)
######################################################################################

logging.debug('This is debug message')
logging.info('This is info message')
logging.warning('This is warning message')

./myapp.log文件中内容为:

Sun, 24 May 2009 21:48:54 demo2.py[line:11] DEBUG This is debug message
Sun, 24 May 2009 21:48:54 demo2.py[line:12] INFO This is info message
Sun, 24 May 2009 21:48:54 demo2.py[line:13] WARNING This is warning message

屏幕上

root        : INFO     This is info message
root        : WARNING  This is warning message

3.3 文件配置

fileConfig()理解的配置文件格式基于configparser的功能。

文件必需包含名为[loggers], [handlers][formatters]的节,标识了文件中定义的各种类型的对象的名字。

如下:

[loggers]
keys=root,log02,log03,log04,log05,log06,log07

[handlers]
keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09

[formatters]
keys=form01,form02,form03,form04,form05,form06,form07,form08,form09

对于每一个实体,有一个独立的节标识如何配置该实体。
- [loggers]节中有名为log01logger[logger_log01]的节中包含了相关的配置细节
- 类似的,[handlers]节中有名为hand01handler[handler_hand01]节中包含了相关的配置;
- [formatters]节中有名为form01formatter[formatter_form01]节中包含了相关的配置

[logger_root]节中包含了根logger的配置

3.3.1 logger节点

根logger必需指定级别和handler列表。根logger节的例子给定如下。

[logger_root]
level=NOTSET
handlers=hand01

对于非根logger的logger来说,需要一些额外的信息。如下例所示。

[logger_parser]
level=DEBUG
handlers=hand01
propagate=1
qualname=compiler.parser
  • level 和 handlers 的解读和根logger的一样,如果非根logger的级别为NOTSET,系统参考高层次的logger来决定logger的有效级别。
  • propagate为1表示将消息传递给高层次logger的handler,为0表示不传播。
  • qualname是logger在层次中的名字,应用通过该名字得到logger。

3.3.2 handlers节点

[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form02
args=('python.log', 'w')

[handler_hand03]
class=handlers.SocketHandler
level=INFO
formatter=form03
args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)

[handler_hand04]
class=handlers.DatagramHandler
level=WARN
formatter=form04
args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)

[handler_hand05]
class=handlers.SysLogHandler
level=ERROR
formatter=form05
args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)

[handler_hand06]
class=handlers.NTEventLogHandler
level=CRITICAL
formatter=form06
args=('Python Application', '', 'Application')

[handler_hand07]
class=handlers.SMTPHandler
level=WARN
formatter=form07
args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')

[handler_hand08]
class=handlers.MemoryHandler
level=NOTSET
formatter=form08
target=
args=(10, ERROR)

[handler_hand09]
class=handlers.HTTPHandler
level=NOTSET
formatter=form09
args=('localhost:9022', '/log', 'GET')
  • class表示handler的类
  • level的解读和logger的一样,NOTSET表示‘记录所有日志’。
  • formatter表示了该handler的formatter的名字。如果为空,使用默认formatter (logging._defaultFormatter)。如果指定了名字,它必需出现在[formatters]节中,且在配置文件中有对应的节。
  • args是handler类构造函数的参数列表 。参考相关handler的构造函数

3.3.3 formatter节点

[formatter_form01]
format=F1 %(asctime)s %(levelname)s %(message)s
datefmt=
class=logging.Formatter
  • format是整体的格式化字符串,
  • datefmt是strftime()兼容的日期/时间格式化字符串。如果为空,使用ISO8601格式,基本上等同于格式%Y-%m-%d %H:%M:%S。ISO8601格式在上述格式的末尾指明了毫秒,中间有个逗号。ISO8601格式的例子:2003-01-23 00:29:50,411。
  • class可选。它表示formatter类的名字(带点的模块和类名)。实例化Formatter子类时该选项很有用。Formatter子类可以用来表示详细/概要的异常回溯。

3.3.4 调用

然后在源码中调用 logging.config.fileConfig() 方法:

import logging
from logging.config import fileConfig

fileConfig('logging_config.conf')
logger = logging.getLogger("log02")
logger.debug('often makes a very good meal of %s', 'visiting tourists')

一个完整且简洁的例子如下:

我们假设文件名为 logging_config.ini 。关于文件格式的更多细节,请参见 日志指南 中的 日志配置 部分。

[loggers]
keys=root

[handlers]
keys=stream_handler

[formatters]
keys=formatter

[logger_root]
level=DEBUG
handlers=stream_handler

[handler_stream_handler]
class=StreamHandler
level=DEBUG
formatter=formatter
args=(sys.stderr,)

[formatter_formatter]
format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s

然后在源码中调用 logging.config.fileConfig() 方法:

import logging
from logging.config import fileConfig

fileConfig('logging_config.ini')
logger = logging.getLogger()
logger.debug('often makes a very good meal of %s', 'visiting tourists')

3.4 通过字典配置

import logging
from logging.config import dictConfig

logging_config = dict(
    version = 1,
    formatters = {
        'f': {'format':
              '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'}
        },
    handlers = {
        'h': {'class': 'logging.StreamHandler',
              'formatter': 'f',
              'level': logging.DEBUG}
        },
    root = {
        'handlers': ['h'],
        'level': logging.DEBUG,
        },
)

dictConfig(logging_config)

logger = logging.getLogger()
logger.debug('often makes a very good meal of %s', 'visiting tourists')
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值