python log日志使用

参考:my-logging-best-practices
参考:Python之日志处理(logging模块)

  1. 程序执行后记录日志,而不是之前.
// don't do that,无法确定REST call成功还是失败
log.info("Making request to REST API")
restClient.makeRequest()
 
// do that 看不到这个log就一定有异常代替
restClient.makeRequest()
log.info("Made request to REST API")
  1. 分开记录参数和消息
    日志有两种数据,手写消息表明正在干什么,第二种是一系列参数
// don't do that 很难解析自动化提取参数,可读性不强,想添加另外参数需要重写
restClient.makeRequest()
log.info("Made request to {} on REST API.", url)
 
// do that 
restClient.makeRequest()
log.info("Made request to REST API. [url={}]", url)

3.辨别warning和error区别并恰当使用
warning:程序成功执行了,但是有点小问题
error:程序执行失败

// don't do that
restClient.makeRequest()
log.info("Made request to {} on REST API.", url)
 
// do that
restClient.makeRequest()
log.info("Made request to REST API. [url={}]", url)

REST 调用可能具有以下三种结果之一:

  • 它可以像咒语一样工作。这是 INFO(在调用之后)。
  • 它可能会失败并出现意外异常。这是一个错误(而不是 INFO 日志)。
  • 它可能会导致一些预期的异常。

所以在警告的情况下,程序做了一些事情,但做得并不完美。如果发生错误,程序没有这样做。

另请注意,警告(当然还有错误)呼召行动。如果没有人需要做出反应并执行某些操作,则无需记录警告。
4.INFO记录 DEBUG记录技术
INFO日志应当看起来像事物,告诉你发生了什么,而没有必要描述如何发生,意味着INFO日志更适于事务性日志而不是技术的东西,DEBUG才该传递技术性东西

INFO  | User registered for newsletter. [user="Thomas", email="thomas@tuhrig.de"]
INFO  | Newsletter send to user. [user="Thomas"]
INFO  | User unsubscribed from newsletter. [user="Thomas", email="thomas@tuhrig.de"]
DEBUG | Saved user to newsletter list. [user="Thomas", email="thomas@tuhrig.de"]
DEBUG | Send welcome mail. [user="Thomas", email="thomas@tuhrig.de"]
INFO  | User registered for newsletter. [user="Thomas", email="thomas@tuhrig.de"]
DEBUG | Started cron job to send newsletter of the day. [subscribers=24332]
INFO  | Newsletter send to user. [user="Thomas"]
INFO  | User unsubscribed from newsletter. [user="Thomas", email="thomas@tuhrig.de"]

log实践

import logging
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"
#logging.basicConfig()函数是一个一次性的简单配置工具使,也就是说只有在第一次调用该函数时会起作用,后续再次调用该函数时完全不会产生任何操作的,多次调用的设置并不是累加操作。
logging.basicConfig(filename='log.log', level=logging.DEBUG,
format=LOG_FORMAT, datefmt=DATE_FORMAT)

logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")
#记录的日志中包含变量数据
logging.warning('%s is %d years old.', 'Tom', 10)
'''
logging模块提供的日志记录函数所使用的日志器设置的日志级别是WARNING,
因此只有WARNING级别的日志记录以及大于它的ERROR和CRITICAL级别的日志记录被输出了,
而小于它的DEBUG和INFO级别的日志记录被丢弃了。

上面输出结果中每行日志记录的各个字段含义分别是:
日志级别:日志器名称:日志内容

之所以会这样输出,是因为logging模块提供的日志记录函数所使用的日志器设置的日志格式默认是BASIC_FORMAT,
其值为:"%(levelname)s:%(name)s:%(message)s"
'''
'''
修改这些默认设置,手动调用一下basicConfig()方法
filename	指定日志输出目标文件的文件名,指定该设置项后日志信息就不会被输出到控制台了
filemode	指定日志文件的打开模式,默认为'a'。需要注意的是,该选项要在filename指定时才有效
format	指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序。
logging模块定义的格式字段下面会列出。

datefmt	指定日期/时间格式。需要注意的是,该选项要在format中包含时间字段%(asctime)s时才有效
level	指定日志器的日志级别
stream	指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream。需要说明的是,stream和filename不能同时提供,否则会引发 ValueError异常
style	Python 3.2中新添加的配置项。指定format格式字符串的风格,可取值为'%'、'{'和'$',默认为'%'
handlers	Python 3.3中新添加的配置项。该选项如果被指定,它应该是一个创建了多个Handler的可迭代对象,这些handler将会被添加到root logger。
需要说明的是:filename、stream和handlers这三个配置项只能有一个存在,不能同时出现2个或3个,否则会引发ValueError异常。
'''
'''
logging模块定义的格式字符串字段
asctime	%(asctime)s	日志事件发生的时间--人类可读时间,如:2003-07-08 16:49:45,896
created	%(created)f	日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值
levelname	%(levelname)s	该日志记录的文字形式的日志级别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
pathname	%(pathname)s	调用日志记录函数的源码文件的全路径
filename	%(filename)s	pathname的文件名部分,包含文件后缀
message	%(message)s	日志记录的文本内容,通过 msg % args计算得到
module	%(module)s	filename的名称部分,不包含后缀
lineno	%(lineno)d	调用日志记录函数的源代码所在的行号
funcName	%(funcName)s	调用日志记录函数的函数名
.....  
'''

'''
logging.debug(), logging.info()等方法的定义中,除了msg和args参数外,还有一个**kwargs参数。
它们支持3个关键字参数: exc_info, stack_info, extra,下面对这几个关键字参数作个说明。

关于exc_info, stack_info, extra关键词参数的说明:
exc_info: 其值为布尔值,如果该参数的值设置为True,则会将异常异常信息添加到日志消息中。如果没有异常信息则添加None到日志信息中。
stack_info: 其值也为布尔值,默认值为False。如果该参数的值设置为True,栈信息将会被添加到日志信息中。
extra: 这是一个字典(dict)参数,它可以用来自定义消息格式中所包含的字段,但是它的key不能与logging模块定义的字段冲突。
'''
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(user)s[%(ip)s] - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"

logging.basicConfig(format=LOG_FORMAT, datefmt=DATE_FORMAT)
logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'47.98.53.222'})





logging模块日志流处理流程

1. logging日志模块四大组件

组件名称 对应类名 功能描述
日志器 Logger 提供了应用程序可一直使用的接口
处理器 Handler 将logger创建的日志记录发送到合适的目的输出
过滤器 Filter 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录
格式器 Formatter 决定日志记录的最终输出格式

日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作。

logging日志模块相关类及其常用方法介绍

下面介绍下与logging四大组件相关的类:Logger, Handler, Filter, Formatter。

Logger类

1)向应用程序代码暴露几个方法,使应用程序可以在运行时记录日志消息;
2)基于日志严重等级(默认的过滤设施)或filter对象来决定要对哪些日志进行后续处理;
3)将日志消息传送给所有感兴趣的日志handlers。
Logger对象最常用的方法分为两类:配置方法 和 消息发送方法

最常用的配置方法如下:

方法 描述
Logger.setLevel() 设置日志器将会处理的日志消息的最低严重级别
Logger.addHandler() 和 Logger.removeHandler() 为该logger对象添加 和 移除一个handler对象
Logger.addFilter() 和 Logger.removeFilter() 为该logger对象添加 和 移除一个filter对象

logger对象配置完成后,可以使用下面的方法来创建日志记录:

方法 描述
Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical() 创建一个与它们的方法名对应等级的日志记录
Logger.exception() 创建一个类似于Logger.error()的日志消息
Logger.log() 需要获取一个明确的日志level参数来创建一个日志记录
说明:

Logger.exception()与Logger.error()的区别在于:Logger.exception()将会输出堆栈追踪信息,另外通常只是在一个exception handler中调用该方法。
Logger.log()与Logger.debug()、Logger.info()等方法相比,虽然需要多传一个level参数,显得不是那么方便,但是当需要记录自定义level的日志时还是需要该方法来完成。

Handler类

Handler对象的作用是(基于日志消息的level)将消息分发到handler指定的位置(文件、网络、邮件等)。Logger对象可以通过addHandler()方法为自己添加0个或者更多个handler对象。比如,一个应用程序可能想要实现以下几个日志需求:

1)把所有日志都发送到一个日志文件中;
2)把所有严重级别大于等于error的日志发送到stdout(标准输出);
3)把所有严重级别为critical的日志发送到一个email邮件地址。
这种场景就需要3个不同的handlers,每个handler复杂发送一个特定严重级别的日志到一个特定的位置。
一个handler中只有非常少数的方法是需要应用开发人员去关心的。对于使用内建handler对象的应用开发人员来说,似乎唯一相关的handler方法就是下面这几个配置方法:

方法 描述
Handler.setLevel() 设置handler将会处理的日志消息的最低严重级别
Handler.setFormatter() 为handler设置一个格式器对象
Handler.addFilter() 和 Handler.removeFilter() 为handler添加 和 删除一个过滤器对象

需要说明的是,应用程序代码不应该直接实例化和使用Handler实例。因为Handler是一个基类,它只定义了素有handlers都应该有的接口,同时提供了一些子类可以直接使用或覆盖的默认行为。下面是一些常用的Handler:

Handler 描述
logging.StreamHandler 将日志消息发送到输出到Stream,如std.out, std.err或任何file-like对象。
logging.FileHandler 将日志消息发送到磁盘文件,默认情况下文件大小会无限增长
logging.handlers.RotatingFileHandler 将日志消息发送到磁盘文件,并支持日志文件按大小切割
logging.hanlders.TimedRotatingFileHandler 将日志消息发送到磁盘文件,并支持日志文件按时间切割
logging.handlers.HTTPHandler 将日志消息以GET或POST的方式发送给一个HTTP服务器
logging.handlers.SMTPHandler 将日志消息发送给一个指定的email地址
logging.NullHandler 该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免’No handlers could be found for logger XXX’信息的出现。

Formater类

Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不同的是,应用代码可以直接实例化Formatter类。另外,如果你的应用程序需要一些特殊的处理行为,也可以实现一个Formatter的子类来完成。

Formatter类的构造方法定义如下:

logging.Formatter.init(fmt=None, datefmt=None, style=’%’)
可见,该构造方法接收3个可选参数:

fmt:指定消息格式化字符串,如果不指定该参数则默认使用message的原始值
datefmt:指定日期格式字符串,如果不指定该参数则默认使用"%Y-%m-%d %H:%M:%S"
style:Python 3.2新增的参数,可取值为 ‘%’, ‘{‘和 ‘$’,如果不指定该参数则默认使用’%’

Filter类

Filter可以被Handler和Logger用来做比level更细粒度的、更复杂的过滤功能。Filter是一个过滤器基类,它只允许某个logger层级下的日志事件通过过滤。该类定义如下:

class logging.Filter(name='')
    filter(record)

比如,一个filter实例化时传递的name参数值为’A.B’,那么该filter实例将只允许名称为类似如下规则的loggers产生的日志记录通过过滤:‘A.B’,‘A.B,C’,‘A.B.C.D’,‘A.B.D’,而名称为’A.BB’, 'B.A.B’的loggers产生的日志则会被过滤掉。如果name的值为空字符串,则允许所有的日志事件通过过滤。

filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,返回值为非0表示可以通过过滤。

说明:
如果有需要,也可以在filter(record)方法内部改变该record,比如添加、删除或修改一些属性。
我们还可以通过filter做一些统计工作,比如可以计算下被一个特殊的logger或handler所处理的record数量等。

配置logging的几种方式

作为开发者,我们可以通过以下3中方式来配置logging:

1)使用Python代码显式的创建loggers, handlers和formatters并分别调用它们的配置函数;
2)创建一个日志配置文件,然后使用fileConfig()函数来读取该文件的内容;
3)创建一个包含配置信息的dict,然后把它传递个dictConfig()函数;

实践2

  1. 需求
    现在有以下几个日志记录的需求:

1)要求将所有级别的所有日志都写入磁盘文件中
2)all.log文件中记录所有的日志信息,日志格式为:日期和时间 - 日志级别 - 日志信息
3)error.log文件中单独记录error及以上级别的日志信息,日志格式为:日期和时间 - 日志级别 - 文件名[:行号] - 日志信息
4)要求all.log在每天凌晨进行日志切割
2. 分析
1)要记录所有级别的日志,因此日志器的有效level需要设置为最低级别–DEBUG;
2)日志需要被发送到两个不同的目的地,因此需要为日志器设置两个handler;另外,两个目的地都是磁盘文件,因此这两个handler都是与FileHandler相关的;
3)all.log要求按照时间进行日志切割,因此他需要用logging.handlers.TimedRotatingFileHandler; 而error.log没有要求日志切割,因此可以使用FileHandler;
4)两个日志文件的格式不同,因此需要对这两个handler分别设置格式器;

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值