详解Python自建logging模块


Posted in Python onJanuary 29, 2018

简单使用

最开始,我们用最短的代码体验一下logging的基本功能。

import logging
logger = logging.getLogger()
logging.basicConfig()
logger.setLevel('DEBUG')
logger.debug('logsomething')
#输出
out>>DEBG:root:logsomething

第一步,通过logging.getLogger函数,获取一个loger对象,但这个对象暂时是无法使用的。
第二步,logging.basicConfig函数,进行一系列默认的配置,包括format、handler等。
第三步,logger调用setLevel函数定义日志级别为DEBUG 最后,调用debug函数,输出一条debug级别的message,显示在了标准输出上。 logging中的日志级别

logging在生成日志的时候,有一个日志级别的机制,默认有以下几个日志级别:

CRITICAL = 50
ERROR = 40
WARNING = 30
INFO 20
DEBUG = 10
NOTEST = 0

每一个logger对象,都有一个日志级别,它只会输出高于它level的日志。如果一个logger的level是INFO,那么调用logger.debug()是无法输出日志的,而logger.warning()能够输出。

一般来说,以上的6个日志级别完全满足我们日常使用了。

logging中的基础类

logging是python的一个基础模块,它在python中的源码位置如下:

#主干代码
/usr/lib/python2.7/logging/__init__.py
#扩展的handler和config
/usr/lib/pyhon2.7/logging/config.py
/usr/lib/python2.7/loging/handlers.py

组成logging的主干的几个基础类都在__init__.py中:

第一个基础类LogRecord

一个LogRecord对象,对应了日志中的一行数据。通常包含:时间、日志级别、message信息、当前执行的模块、行号、函数名...这些信息都包含在一个LogRecord对象里。
LogRecord对象可以想象成一个大字典:

class LogRecord(object):
 #代表一条日志的类
 def getMessage(self):
  #获取self.msg
 def markLogRecord(dict):
 #这个方法很重要,生成一个空的LogRecord,然后通过一个字典,直接更新LogReocrd中的成员变量
 rv = LogRecord(None, None, "", 0, "", (), None, None)
 rv.__dict__.update(dict)
 return rv

第二个基础类Formatter

Formatter对象是用来定义日志格式的,LogRecord保存了很多信息,但是打印日志的时候我们只需要其中几个,Formatter就提供了这样的功能,它依赖于python的一个功能:

#通过字典的方式,输出格式化字符串
print('%(name)s:%(num)d'%{'name':'my_name', 'num' : 100})
out >>>my_name:100
如果说LogRecord是后面的那个字典,那么Formatter就是前面的那个格式字符串...的抽象

重要的代码如下:

class Formatter(object):
 def __init__(self, fmt=None, datefmt = None):
  if fmt:
   self._fmt = fmt
  else:
   #默认的format
   self._fmt = "%(message)s"
 def format(self, record)
  #使用self._fmt进行格式化
  s = self._fmt %record.__dict__
  return s

第三个基础类Filter和Filterer

Filter类,功能很简单。Filter.filter()函数传入一个LogRecord对象,通过筛选返回1,否则返回0.从代码中可以看到,其实是对LogRecord.name的筛选。

Filterer类中有一个Filter对象的列表,它是一组Filter的抽象。

重要的代码如下:

class Filter(object):
 def __init__(self, name=''):
  self.name = name
  self.nlen = len(name)
 def filter(self, record):
  #返回1表示record通过,0表示record不通过
  if self.nlen == 0:
   return 1
  elif self.name == record.name:
   return 1
  #record.name不是以filter开头
  elif record.name.find(self.name, 0, self.nlen) != 0:
   return 0
  #最后一位是否为
  return (record.name[self.nlen] == '.')
class Filterer(object):
 #这个类其实是定义了一个self.filters = []的列表管理多个filter
 def addFilter(self, filter):
 def removefilter(self, filter):
 def filter(self, record):
 #使用列表中所有的filter进行筛选,任何一个失败都会返回0
 #例如:
  #filter.name = 'A', filter2.name='A.B', filter2.name = 'A, B, C'
  #此时record.name = 'A,B,C,D'这样的record才能通过所有filter的筛选

logging中的高级类

有了以上三个基础的类,就可以拼凑一些更重要的高级类了,高级类可以实现logging的重要功能。

Handler——抽象了log的输出过程 Handler类继承自Filterer。Handler类时log输出这个过程的抽象。
同时Handler类具有一个成员变量self.level,在第二节讨论的日志级别的机制,就是在Handler中实现的。
Handler有一个emit(record)函数,这个函数负责输出log,必须在Handler的子类中实现。

重要代码如下:

class Handler(Filterer):
 def __init__(self, level = NOTEST)
  #handler必须有level属性
  self.level = _checkLevel(level)
 def format(self, record):
  #使用self.formatter, formattercord
 def handler(self, record):
  #如果通过filter的筛选,则emit这条log
  rv = self.filter(record)
  self.emit(record)
 def emit(self, record):
  #等待子类去实现

接下来看两个简单的handler的子类,其中在logging源码中,有一个handler.py专门定义了很多复杂的handler,有的可以将log缓存在内存中,有的可以将log做rotation等。

StreamHandler
最简单的handler实现,将log写入一个流,默认的stream是sys.stderr

重要的代码如下:

class StreamHandler(Handler):
 def __init__(self, stream = None):
  if stream is None:
   stream = sys.stderr
  self.stream = stream
 def emit(self, record):
  #将record的信息写入流
  #处理一些编码的异常
  fs = '%s\n' #每条日志都有换行
  stream = self.stream
  stream.write(fs%msg)

FileHandler

将log输出到文件的handler,继承StreamHandler

重要代码如下:

class FileHandler(StreamHandler):
 def __init__(self, filename, mode='a')
  #append方式打开一个文件
  StreamHandler.__init__(self, self._open())
 def emit(self, record):
  #和streamhandler保持一致
  StreamHandler.emit(self, record)

Logger——一个独立的log管道

什么是logger?

+ logger类继承自Filterer,

+ logger对象有logger.level日志级别

+ logger对象控制多个handler:logger.handlers = []

+ logger对象之间存在福字关系

简单的来说,logger这个类,集中了我们以上所有的LogRecord、Filter类、Formatter类、handler类。首先,logger根据输入生成一个LogRecord读写,经过Filter和Formatter之后,再通过self.handlers列表中的所有handler,把log发送出去。

一个logger中可能有多个handler,可以实现把一份log放到任意的位置。

class Logger(Filterer):
 def __init__(self, name, level=NOTEST)
  #handler列表
  self.handlers = []
  self.level = _checklevel(level)
 def addHandler(self, hdlr):
 def removeHandler(self, hdlr):
 def _log(self, level, msg, args, exc_info=None, extra=None):
  #在_log函数中创建了一个LogRecord对象
  record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info, func, extra)
  #交给handle函数
  self.handle(record)
 def handle(self, reord):
  #进行filter,然后调用callHandlers
  if(not self.disabled) and self.filter(record):
   self.callHandlers(record)
 def callHandlers(self, record):
  #从当前logger到所有的父logger,递归的handl传入的record
  c = self
  while c:
   for hdlr in c.handlers:
    hdlr.handle(record) #进入handler的emit函数发送log
   ....
   c = c.parent

LoggerAdapter——对标准logger的一个扩展

LogRecord这个大字典中提供的成员变量已经很多,但是,如果在输出log时候仍然希望能够夹带一些自己想要看到的更多信息,例如产生这个log的时候,调用某些函数去获得其他信息,那么就可以把这些添加到Logger中,LoggerAdapter这个类就起到这个作用。

LoggerAdapter这个类很有意思,如果不做什么改动,那么LoggerAdapter类和Logger并没有什么区别。LoggerAdapter只是对Logger类进行了一下包装。

LoggerAdapter的用法其实是在它的成员函数process()的注释中已经说明了:

def process(self, msg, kwargs):
 '''
 Normally,you'll only need to overwrite this one method in a LoggerAdapter subclass for your specific needs.
 '''

也就是说重写process函数,以下是一个例子:

import logging
import random
L=logging.getLogger('name')
#定义一个函数,生成0~1000的随机数
def func():
 return random.randint(1,1000)
class myLogger(logging.LoggerAdapter):
 #继承LoggerAdapter,重写process,生成随机数添加到msg前面
 def process(self,msg,kwargs):
  return '(%d),%s' % (self.extra['name'](),msg) ,kwargs
#函数对象放入字典中传入 
LA=myLogger(L,{'name':func})
#now,do some logging
LA.debug('some_loging_messsage')
out>>DEBUG:name:(167),some_loging_messsage
Python 相关文章推荐
python获取远程图片大小和尺寸的方法
Mar 26 Python
详解Python各大聊天系统的屏蔽脏话功能原理
Dec 01 Python
Python3.4实现远程控制电脑开关机
Feb 22 Python
对numpy 数组和矩阵的乘法的进一步理解
Apr 04 Python
解决python3中cv2读取中文路径的问题
Dec 05 Python
python将控制台输出保存至文件的方法
Jan 07 Python
如何利用Pyecharts可视化微信好友
Jul 04 Python
Python操作qml对象过程详解
Sep 26 Python
python读取word 中指定位置的表格及表格数据
Oct 23 Python
python实现全排列代码(回溯、深度优先搜索)
Feb 26 Python
python 判断txt每行内容中是否包含子串并重新写入保存的实例
Mar 12 Python
Python Selenium破解滑块验证码最新版(GEETEST95%以上通过率)
Jan 29 Python
python抓取网页中链接的静态图片
Jan 29 #Python
Python实现识别手写数字 Python图片读入与处理
Mar 23 #Python
Python实现PS滤镜特效Marble Filter玻璃条纹扭曲效果示例
Jan 29 #Python
Python实现识别手写数字大纲
Jan 29 #Python
django文档学习之applications使用详解
Jan 29 #Python
Python实现PS滤镜Fish lens图像扭曲效果示例
Jan 29 #Python
python实现识别手写数字 python图像识别算法
Mar 23 #Python
You might like
PHP得到mssql的存储过程的输出参数功能实现
2012/11/23 PHP
PHP中spl_autoload_register()函数用法实例详解
2016/07/18 PHP
thinkPHP5框架设置404、403等http状态页面的方法
2018/06/05 PHP
javascript编程起步(第六课)
2007/02/27 Javascript
html a标签-超链接中confirm方法使用介绍
2013/01/04 Javascript
ExtJS下 Ext.Direct加载和提交过程排错小结
2013/04/02 Javascript
jquery中邮箱地址 URL网站地址正则验证实例代码
2013/09/15 Javascript
利用JS进行图片的切换即特效展示图片
2013/12/03 Javascript
用IE重起计算机或者关机的示例代码
2014/03/10 Javascript
那些精彩的JavaScript代码片段
2017/01/12 Javascript
微信小程序之事件交互操作实例分析
2018/12/03 Javascript
Vue+Express实现登录注销功能的实例代码
2019/05/05 Javascript
JavaScript 作用域scope简单汇总
2019/10/23 Javascript
Vue 实现分页与输入框关键字筛选功能
2020/01/02 Javascript
jquery实现简易验证插件封装
2020/09/13 jQuery
Vue +WebSocket + WaveSurferJS 实现H5聊天对话交互的实例
2020/11/18 Vue.js
[01:04:14]VP vs TNC 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
Python中的字符串替换操作示例
2016/06/27 Python
启动targetcli时遇到错误解决办法
2017/10/26 Python
Python操作mongodb的9个步骤
2018/06/04 Python
基于python绘制科赫雪花
2018/06/22 Python
使用python实现男神女神颜值打分系统(推荐)
2019/10/31 Python
python实现打砖块游戏
2020/02/25 Python
Python self用法详解
2020/11/28 Python
印度最大的酒店品牌网络:OYO Rooms
2016/07/24 全球购物
什么是网络协议
2016/04/07 面试题
行政经理的岗位职责
2013/11/23 职场文书
回门宴新郎答谢词
2014/01/12 职场文书
生产厂长岗位职责
2014/02/21 职场文书
战友聚会主持词
2014/04/02 职场文书
跑操口号
2014/06/12 职场文书
学生会竞选演讲稿怎么写
2014/08/26 职场文书
机关党员公开承诺书
2014/08/30 职场文书
校长四风对照检查材料
2014/09/27 职场文书
2014年体育教师工作总结
2014/12/03 职场文书
python基于tkinter实现gif录屏功能
2021/05/19 Python