Python logging 模块

前言

Python在日志记录处理上是非常灵活的,且功能完备,足以满足所有对日志方面的需求。

如此强大,当然是日志系统设计非常的流弊了。什么封闭开放原则/什么里氏替换原则/依赖倒置原则/单一职责原则等思想融入的淋漓尽致。

里面的对象关系网:Manager对象,Logger对象,Handler对象,Formatter对象, Filter对象,LogRecord对象等。

除此之外,了解其设计时,要梳理清楚模块中几个重要对象之间的关系,和他们各自负责的Mission任务!

logging模块提供的特性

  • logger在逻辑上的继承关系,由Manager对象维护并添加到logger对象中。可以延着继承关系将logRecorder传递,还可以获取有效的日志等级设置。
  • 日志输出可到多种i/o设备
  • 日志格式和内容丰富
  • 日志分级别,也是日志在级别维度上的分类。
  • 可根据日志对象的级别进行过滤、指定输出i/o。
  • logger主要负责继承关系基本单位
  • handler负责输出方向的基本单位
  • formater负责日志格式
  • filter负责过滤,可安置于logger和handler,配合上继承传播机制,灵活过滤。
  • logging.basicConfig()调用进行默认初始化。
  • Logger对象
  • handler对象
  • formatter对象
  • filter对象
  • log recoder对象
  • 内置扩展config模块一次性加载所有相关对象
  • 内置handers模块提供多种handler类型

logging模块的设计过程

可以参考JAVA的日志系统设计,这篇文章采用幽默诙谐的语言基调,从需求出发,阐明日志系统是怎么设计出来的。

我这里按照这篇文章,简单勾画下重点

  • 需求

    • 日志消息可以输出到多种媒介中
    • 日志消息内容可以做格式化
    • 程序代码是通过package,module来组织分类的,要满足不同包,不同模块的代码,有自己的日志处理方式,同时也要有日志分类(日志其实就是某个Event事件发生了的记录),由于是某个事件发生触发了日志,所以这个事件的级别要通过日志来体现,所以日志还要分级别。
    • 按照上一条需求,日志有了级别,那么就要在这种日志分类上有文章可做。比如,不同日志级别的处理方式不同,不同日志级别之间还有有轻重缓急的关系。根据轻重缓急可以已设置某个日志级别以上的处理方式和以下的级别的处理方式。
    • 日志的处理和日志的输出两个地方还可以定制化过滤需求。
  • 分析需求
    • 首先,要有个类来抽象出日志消息,这个类至少有两个属性时间戳,消息本身。日志消息本来就是记录了一个事件的发生。那就用LogRecord类来表示日志消息。
    • LogRecord要输出到不同媒介中,那就抽象一个Handler类,不同的Handler处理LogRecord到不同的媒介中
    • LogRecord要格式化输出,那么就要有一个Formatter类,不同Formatter可以处理为不同的格式类型。
    • 要满足不同包,模块,有自己的处理方式。从用户最后使用的角度考虑,我要每次都创建多个Handler太麻烦了,那就还需要一个来整合这些对象的一个处理器,这是一个抽象出来的类,就叫Logger。每个包,模块都可以有自己的logger,可以用独特的名字来标识这个Logger的唯一性。
    • 日志消息LogRecord是分级别的,其它处理LogRecord的也都可以根据其级别做不同处理。
    • 在logger和handler这两个日志路由位置进行过滤。即增加一个Filter过滤对象到logger和handler。

这样根据需求围绕核心几个类的关系

Logger包含Handler,Handler包含Formatter。都处理LogRecord。Logger和Handler都可以添加Filter对LogRecord进行过滤。

logger的继承

1.就是通过logger的名字的命名层级来查找其parent logger。使用'.'来表示继承关系。没有了通过占位对象占位。
2.logger的继承其实是继承几个方面
2.1. level的继承:子logger写日志的时候,优先使用本身设置的level,如果没有设置,则逐层向上级父logger查询,直到查询到为止。最极端的情况是,使用rootlogger的默认日志级别——WARNING.可以使用logger.getEffectivaLeve()获取有效的等级。
2.2. handler的继承,先将日志对象传递给子logger的所有handler处理,处理完毕,如果孩子的logger的propagate属性设置的是1(TRUE),那么日志record对象会被传播给上级父logger,该父logger的handler都会进行处理,如果父logger的propagate也是1,那么会继续向上。如果子logger的propagate就是0,就不会传播,极端,如果是子logger都没有handler,且不传播,那么就会极端的给logging.lastResort这是一个内置的handler,不会和任何一个logger关联,and acts like StreamHandler which the event description message to the current value of sys.stderr.这个logging.lastResort是没有formatted的,所以我们会看到就message原始格式,输出到标准输出中。
3. 总的来说,其实是逻辑上的继承,只是利用层级关系,可以来使用父logger的level和handler。继承的管理是Manager对象。

logger在逻辑上的继承结构

logger的构建是通过logger的命名创建出的。名字在整个程序中都是唯一的表示。

这样说的设计就可以实现将你自己的日志和使用第三方模块所产生的日志结合在一起。不用在自己的代码中去刻意再写兼容第三方模块产生的日志的处理。都交给logging就可以整合处理了。

Logger 间继承关系的表示:

  • 是通过'.'号来表示的。如:'foo' 'foo.bar' 'foo.bar.baz' 'foo'是'foo.bar'的父亲,'foo.bar' 是'foo.bar.baz' 的父亲。'foo'的父亲其实就是'root'logger。
  • 这种等级关系的表示方法,非常类似于包和模块之间的表示层级关系的方法。

logging.basicConfig()

该模块级函数是设置root logger.如果root logger已经有handler的化,那么调用函数是没有作用的,以下是源码:

def basicConfig(**kwargs):
_acquireLock()
try:
if len(root.handlers) == 0:
filename = kwargs.get("filename")
if filename:
mode = kwargs.get("filemode", 'a')
hdlr = FileHandler(filename, mode)
else:
stream = kwargs.get("stream")
hdlr = StreamHandler(stream)
fs = kwargs.get("format", BASIC_FORMAT)
dfs = kwargs.get("datefmt", None)
fmt = Formatter(fs, dfs)
hdlr.setFormatter(fmt)
root.addHandler(hdlr)
level = kwargs.get("level")
if level is not None:
root.setLevel(level)
finally:
_releaseLock()

Logger Objects

  • Loggers are never instantiated directly,but always through the module-level function logging.getLogger(name).
  • logger对象不能直接通过Logger类实例,都是通过模块级函数logging.getLogger(name)调用产生的。
  • Mutiple calls to getLogger() with the same name will always return a refernece to the same Logger object.
  • Logger的继承关系除了可以继承父级的一些东西如level(getEffectiveLevel())。还可以通过继承关系将LogRecord向上传播。

    logger经常使用的方法:
方法 用法 注释
logger.info()
logger.debug()
logger.setLevel
logger.getEffectiveLevel()
logger.getChild(suffix) 返回一个子logger,子logger的名字会是父logger名字加上suffix
logger.addFilter(filt)
logger.removeFilter(filt)
logger.addHandler(hd)
logger.removeHandler(hd)

Handler

Handler是一个基础类,其它不同类型的handler都是继承于它。同样,handler也不会直接实例化对象,而是使用它的子类。

常用类型Handler

Handler 所在模块 注释
StreamHandler logging
FileHandler logging
NullHandler loggging
BaseRotatingHandler logging.handlers
RotatingFileHandler logging.handlers
TimedRotatingFileHandler logging.handlers
SocketHandler logging.handlers

等等。。。

Handler常用方法:

  • Handler.addFilter()
  • Handler.removeFilter()
  • Handler.setFormatter()
  • Handler.setLevel()

Formatter

该类对象是用于对LogRecord进行格式化处理的。

Formtter对象的初始化需要指定日志字符串格式,还可以指定时间格式,和格式化字符串使用的风格。

Fomatter对象是添加到Handler对象中的。

Filter

Filter对象初始化需要指定过滤logger名,只有指定的logger名所产生的LogRecord才会通过该filter,不然会被它过滤掉。logger名过滤是指不会过滤指定logger名及其子logger。

LogRecord

日志内容的载体。

LogRecord的实例都是通过Logger对象的方法实例的

LogRecord的主要分类就是在日志级别上的分类,分为DEBUG,INFO,WARNING,ERROR,CRITICAL

读取logging配置

参考《02. logging模块利用配置加载logger》

还是贴点实践代码吧

import logging

logging.basicConfig(filename='logging_train.log',level=logging.DEBUG)
logger1 = logging.getLogger('1')
logger2 = logger1.getChild('2') # logger2继承于logger1:即会是1.2
logger2.propagate = True # 是否向上传递LogRecord对象 class myfilter(logging.Filter): # 定义自己的Filter类 def __init__(self, s): # 过滤所有不包含指定字符串的日志
self.filter_string = s def filter(self, red): # logger和handler会调用这个方法判定过滤结果。
if self.filter_string in red.getMessage():
return True
else:
return False filter1 = myfilter('world') handler1 = logging.FileHandler('logging_train1.log')
handler2 = logging.StreamHandler() formatter1 = logging.Formatter('%(asctime)s %(name)s %(threadName)s %(process)s %(levelname)s %(message)s') handler2.setFormatter(formatter1) logger1.addHandler(handler1) logger2.addHandler(handler2) logger2.addFilter(filter1)
logger2.setLevel(logging.CRITICAL)
logger2.error("hello world!")
logger2.critical("Python is the best all over the world!")

0x01 Python logging模块的更多相关文章

  1. python logging模块可能会令人困惑的地方

    python logging模块主要是python提供的通用日志系统,使用的方法其实挺简单的,这块就不多介绍.下面主要会讲到在使用python logging模块的时候,涉及到多个python文件的调 ...

  2. python logging模块使用

    近来再弄一个小项目,已经到收尾阶段了.希望加入写log机制来增加程序出错后的判断分析.尝试使用了python logging模块. #-*- coding:utf-8 -*- import loggi ...

  3. 读懂掌握 Python logging 模块源码 (附带一些 example)

    搜了一下自己的 Blog 一直缺乏一篇 Python logging 模块的深度使用的文章.其实这个模块非常常用,也有非常多的滥用.所以看看源码来详细记录一篇属于 logging 模块的文章. 整个 ...

  4. (转)python logging模块

    python logging模块 原文:http://www.cnblogs.com/dahu-daqing/p/7040764.html 1 logging模块简介 logging模块是Python ...

  5. Python logging 模块学习

    logging example Level When it's used Numeric value DEBUG Detailed information, typically of interest ...

  6. python logging—模块

    python logging模块 python logging提供了标准的日志接口,python logging日志分为5个等级: debug(), info(), warning(), error( ...

  7. Python logging模块无法正常输出日志

    废话少说,先上代码 File:logger.conf [formatters] keys=default [formatter_default] format=%(asctime)s - %(name ...

  8. 0x03 Python logging模块之Formatter格式

    目录 logging模块之Formatter格式 Formater对象 日志输出格式化字符串 LogRecoder对象 时间格式化字符串 logging模块之Formatter格式 在记录日志是,日志 ...

  9. Python Logging模块的简单使用

    前言 日志是非常重要的,最近有接触到这个,所以系统的看一下Python这个模块的用法.本文即为Logging模块的用法简介,主要参考文章为Python官方文档,链接见参考列表. 另外,Python的H ...

随机推荐

  1. Spring+SpringMVC+Spring Data JPA完美整合

    使用Maven实现SSS框架的整合. 方便记录,专门建了一个pom项目用来整合SSS框架所用的jar包 1.POM项目,作为父级项目,记录整个整合中的依赖jar包pom文件 <project x ...

  2. http 1.1 状态码定义

    part of Hypertext Transfer Protocol -- HTTP/1.1RFC 2616 Fielding, et al. 10 Status Code Definitions ...

  3. vue-cli3项目关闭烦人的代码检测

    参考博客:https://blog.csdn.net/e1172090224/article/details/99636767 vue.config.js module.exports = { lin ...

  4. EasyNVR网页摄像机无插件H5、谷歌Chrome直播方案-Onvif(三)使用Onvif协议进行设备PTZ云台控制

    背景分析 熟悉EasyNVR产品的盆友们应该都知道,EasyNVR主要完成的是RTSP视频流到RTMP/HLS/Flv的转码,并提供了一套api和一个可视化管理平台来便于调用.同时支持ONVIF协议进 ...

  5. [转] 2017年PHP开发者大会 鸟哥 (惠新宸@Laruence)精彩问答

    php7.1那个诡异的函数返回类型限定是如何考虑的? 鸟哥:没什么特别考虑,投票投出来的.首先说明一点,我投的是反对票.包括php的命名空间反斜杠我也是非常反对的,但可能由于我并没有对这方面太深的认识 ...

  6. PostgreSQL的递归查询(with recursive) ,替代oracle 的级联查询connect by

    开发有需求,说需要对一张地区表进行递归查询,Postgres中有个 with recursive的查询方式,可以满足递归查询(一般>=2层). 测试如下: create table tb(id ...

  7. 【bat】实现数组,for循环取数据

    1.数组对象 @echo off set objLength=2 set obj[0].name=test1 set obj[0].password=1234 set obj[1].name=test ...

  8. 记一次修复yum被破坏

    现象 # yum There was a problem importing one of the Python modules required to run yum. The error lead ...

  9. Idea Spring 、SpringBoot相关设置技巧

    1.Spring变量依赖注入出现红色波浪线 Could not autowire. No beans of 'UserMapper' type found. less... (Ctrl+F1) Che ...

  10. spring data jpa碰到的坑

    1.不能从别的类的repository那里 执行另一个类的sql,这样映射会失败. 2.有entity,就要有repository,并且还要有id注解 3.还要多表联查未测试,估计要用map去映射出来 ...