Python3 logging (日志)

Python3 logging (日志)

时间:2019-05-13
环境:python3.7.3
官方文档:https://docs.python.org/3/library/logging.config.html#logging-config-dictschetma

1. 相关模块引入

import logging
import logging.config

2. 基础使用

#!/usr/local/bin/python3
# -*- coding:utf-8 -*-
import logging

logging.debug('debug message')
logging.info('info message')
logging.warning('warn message')
logging.error('error message')
logging.critical('critical message')

输出结果:

WARNING:root:warn message
ERROR:root:error message
CRITICAL:root:critical message

结论:
*. 默认 打印等级:WARNING(即只有日志级别高于WARNING的日志信息才会输出)
*. 默认 打印格式:[%(levelname)s]:[%(name)s]:[%(message)s] 即:[日志等级]:[Logger实例名称]:[日志消息内容]

实际开发中,需要更多自定义化的配置,在开始自定义配置前,先了解几个核心概念

3. 核心概念

  • 1.Logger 记录器:暴露了应用程序代码能直接使用的接口。
  • 2.Handler 处理器:将(记录器产生的)日志记录发送至合适的目的地。
  • 3.Filter 过滤器:提供了更好的粒度控制,它可以决定输出哪些日志记录。
  • 4.Formatter 格式化器:指明了最终输出中日志记录的布局。

3.1 Logger 记录器

Logger是一个树形层级结构,在使用接口debug,info,warn,error,critical之前必须创建Logger实例,即创建一个记录器,如果没有显式的进行创建,则默认创建一个root logger,并应用默认的日志级别(WARN),处理器Handler(StreamHandler,即将日志信息打印输出在标准输出上),和格式化器Formatter(默认的格式即为第一个简单使用程序中输出的格式)。

创建方法: logger = logging.getLogger(logger_name)

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

logger.setLevel(logging.ERROR)  # 设置日志级别为ERROR,即只有日志级别大于等于ERROR的日志才会输出
logger.addHandler(handler_name)  # 为Logger实例增加一个处理器
logger.removeHandler(handler_name)   # 为Logger实例删除一个处理器

3.2 Handler 处理器

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

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

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

3.2.1 StreamHandler

  1. logging模块中的三个handler之一。
  2. 能够将日志信息输出到sys.stdout, sys.stderr 或者类文件对象(更确切点,就是能够支持write()和flush()方法的对象)。

构造参数:

class logging.StreamHandler(stream=None)
日志信息会输出到指定的stream中,如果stream为空则默认输出到sys.stderr

3.2.2 FileHandler

  1. logging模块自带的三个handler之一。
  2. 继承自StreamHandler。将日志信息输出到磁盘文件上。

构造参数:

class logging.FileHandler(filename, mode='a', encoding=None, delay=False)
模式默认为append,delay为true时,文件直到emit方法被执行才会打开。默认情况下,日志文件可以无限增大。

3.2.3 NullHandler

  1. logging模块自带的三个handler之一。
  2. 空操作handler, 没有参数。

3.2.4 WatchedFileHandler

  1. 位于logging.handlers模块中。
  2. 用于监视文件的状态,如果文件被改变了,那么就关闭当前流,重新打开文件,创建一个新的流。由于newsyslog或者logrotate的使用会导致文件改变。这个handler是专门为linux/unix系统设计的,因为在windows系统下,正在被打开的文件是不会被改变的。

参数和FileHandler相同:

class logging.handlers.WatchedFileHandler(filename, mode='a', encoding=None, delay=False)

3.2.5 RotatingFileHandler

  1. 位于logging.handlers支持循环日志文件。

构造参数:

class logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0)

参数maxBytes和backupCount允许日志文件在达到maxBytes时rollover.当文件大小达到或者超过maxBytes时,就会新创建一个日志文件。
上述的这两个参数任一一个为0时,rollover都不会发生。也就是就文件没有maxBytes限制。backupcount是备份数目,也就是最多能有多少个备份。
命名会在日志的base_name后面加上.0-.n的后缀,如example.log.1,example.log.1,…,example.log.10。当前使用的日志文件为base_name.log。

3.2.6 TimedRotatingFileHandler

定时循环日志handler,位于logging.handlers,支持定时生成新日志文件。

构造参数:

class logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False)

参数when决定了时间间隔的类型,参数interval决定了多少的时间间隔。如when='D',interval=2,就是指两天的时间间隔,backupCount决定了能留几个日志文件。超过数量就会丢弃掉老的日志文件。

when的参数决定了时间间隔的类型。两者之间的关系如下:
 'S'         |  秒
 'M'         |  分
 'H'         |  时
 'D'         |  天
 'W0'-'W6'   |  周一至周日
 'midnight'  |  每天的凌晨
utc参数表示UTC时间。

3.2.7 其他handler——SocketHandler、DatagramHandler、SysLogHandler、NtEventHandler、SMTPHandler、MemoryHandler、HTTPHandler

这些handler都不怎么常用,所以具体介绍就请参考官方文档 (https://docs.python.org/3.7/library/logging.handlers.html)

3.3 Formatter 格式化器

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

创建方法: formatter = logging.Formatter(fmt=None, datefmt=None, style='%')
fmt:消息的格式化字符串,如果不指明fmt,将使用'%(message)s'。
datefmt:日期字符串。如果不指明datefmt,将使用ISO8601日期格式。
style:引用样式,默认'%', 即'%()s'

3.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='')

以下是相关概念总结:

熟悉了这些概念之后,有另外一个比较重要的事情必须清楚,即Logger是一个树形层级结构;
Logger可以包含一个或多个Handler和Filter,即Logger与Handler或Fitler是一对多的关系;
一个Logger实例可以新增多个Handler,一个Handler可以新增多个格式化器或多个过滤器,而且日志级别将会继承。

1557738246703.jpg

3.5 Logging工作流程

logging模块使用过程

  1. 第一次导入logging模块或使用reload函数重新导入logging模块,logging模块中的代码将被执行,这个过程中将产生logging日志系统的默认配置。

  2. 自定义配置(可选)。logging标准模块支持三种配置方式: dictConfig,fileConfig,listen。

    • dictConfig是通过一个字典进行配置Logger,Handler,Filter,Formatter;
    • fileConfig则是通过一个文件进行配置
    • listen则监听一个网络端口,通过接收网络数据来进行配置。

    当然,除了以上集体化配置外,也可以直接调用Logger,Handler等对象中的方法在代码中来显式配置。

  3. 使用logging模块的全局作用域中的getLogger函数来得到一个Logger对象实例(其参数即是一个字符串,表示Logger对象实例的名字,即通过该名字来得到相应的Logger对象实例)。

  4. 使用Logger对象中的debug,info,error,warn,critical等方法记录日志信息。

logging模块处理流程

477558-a099cc71d0a4c453.png
  1. 判断日志的等级是否大于Logger对象的等级,如果大于,则往下执行,否则,流程结束。
  2. 产生日志。第一步,判断是否有异常,如果有,则添加异常信息。第二步,处理日志记录方法(如debug,info等)中的占位符,即一般的字符串格式化处理。
  3. 使用注册到Logger对象中的Filters进行过滤。如果有多个过滤器,则依次过滤;只要有一个过滤器返回假,则过滤结束,且该日志信息将丢弃,不再处理,而处理流程也至此结束。否则,处理流程往下执行。
  4. 在当前Logger对象中查找Handlers,如果找不到任何Handler,则往上到该Logger对象的父Logger中查找;如果找到一个或多个Handler,则依次用Handler来处理日志信息。但在每个Handler处理日志信息过程中,会首先判断日志信息的等级是否大于该Handler的等级,如果大于,则往下执行(由Logger对象进入Handler对象中),否则,处理流程结束。
  5. 执行Handler对象中的filter方法,该方法会依次执行注册到该Handler对象中的Filter。如果有一个Filter判断该日志信息为假,则此后的所有Filter都不再执行,而直接将该日志信息丢弃,处理流程结束。
  6. 使用Formatter类格式化最终的输出结果。 注:Formatter同上述第2步的字符串格式化不同,它会添加额外的信息,比如日志产生的时间,产生日志的源代码所在的源文件的路径等等。
  7. 真正地输出日志信息(到网络,文件,终端,邮件等)。至于输出到哪个目的地,由Handler的种类来决定。

4. 自定义配置

4.1 格式配置

4.1.1 format 格式中的变量

格式 描述
%(asctime)s 打印日志的时间
%(process)d 打印进程ID
%(thread)d 打印线程id
%(threadName)s 打印线程名称
%(levelname)s 打印日志级别名称
%(levelno)s 打印日志级别的数值
%(pathname)s 打印当前执行程序的路径
%(filename)s 打印当前执行程序名称
%(funcName)s 打印日志的当前函数
%(lineno)d 打印日志的当前行号
%(message)s 打印日志信息

格式配置:

完整输出: %(asctime)s [%(process)d] [%(threadName)s(%(thread)d)] [%(name)s] [%(levelname)s(%(levelno)s)] %(pathname)s.%(filename)s[line:%(lineno)d, funcName:%(funcName)s] %(message)s'

个人格式(延续Java logback习惯): '%(asctime)s [%(threadName)s] [%(name)s] [%(levelname)s] %(filename)s[line:%(lineno)d] %(message)s'

4.1.2 datefmt

参考: https://docs.python.org/3.7/library/time.html?highlight=strftime#time.strftime

格式 描述
%a 本地化的缩写星期中每日的名称
%A 本地化的星期中每日的完整名称
%b 本地化的月缩写名称
%B 本地化的月完整名称
%c 本地化的适当日期和时间表示
%d 十进制数 [01,31] 表示的月中日
%H 十进制数 [00,23] 表示的小时(24小时制)
%I 十进制数 [01,12] 表示的小时(12小时制)
%j 十进制数 [001,366] 表示的年中日
%m 十进制数 [01,12] 表示的月
%M 十进制数 [00,59] 表示的分钟
%p 本地化的 AM 或 PM
%S 十进制数 [00,61] 表示的秒
%U 十进制数 [00,53] 表示的一年中的周数(星期日作为一周的第一天)作为。在第一个星期日之前的新年中的所有日子都被认为是在第0周
%w 十进制数 [0(星期日),6] 表示的周中日
%W 十进制数 [00,53] 表示的一年中的周数(星期一作为一周的第一天)作为。在第一个星期一之前的新年中的所有日子被认为是在第0周
%x 本地化的适当日期表示
%X 本地化的适当时间表示
%y 十进制数 [00,99] 表示的没有世纪的年份
%Y 十进制数表示的带世纪的年份
%z 时区偏移以格式 +HHMM 或 -HHMM 形式的 UTC/GMT 的正或负时差指示,其中H表示十进制小时数字,M表示小数分钟数字 [-23:59, +23:59]
%Z 时区名称(如果不存在时区,则不包含字符)
%% 字面的 '%' 字符

4.2 第一种:显式调用操作

显式创建 Logger记录器、Handler处理器 和 Formatter格式化器,并进行相关设置

import logging.config
# create logger
logger_name = "example"
logger = logging.getLogger(logger_name)
logger.setLevel(logging.DEBUG)

# create file handler
log_path = "./loggingDemo-direct.log"
fh = logging.FileHandler(log_path)
fh.setLevel(logging.WARNING)

# create formatter
fmt = '%(asctime)s [%(threadName)s] [%(name)s] [%(levelname)s] %(filename)s[line:%(lineno)d] %(message)s'
datefmt = '%Y-%m-%d %H:%M:%S'
formatter = logging.Formatter(fmt, datefmt)

# add handler and formatter to logger
fh.setFormatter(formatter)
logger.addHandler(fh)

logger.debug('log debug')
logger.info('log info')
logger.warning('log warning')
logger.error('log error')
logger.critical('log critical')

4.3 第二种:logging.basicConfig() 配置

key 描述
filename 指定使用指定的文件名,而不是StreamHandler创建FileHandler。
filemode 如果指定了文件名,请在此模式下打开文件。 默认为'a'。
format 格式化字符串
datefmt 格式化日期/时间格式
style 引用样式,默认'%', 即'%()s'
level logger 的 等级
stream 使用指定的流初始化StreamHandler。 注:此参数与filename不兼容 - 如果两者都存在,则引发ValueError。
handlers 如果指定,则应该是已创建的处理程序的可迭代,以添加到根记录器。 任何没有格式化程序集的处理程序都将被分配在此函数中创建的默认格式化程序。 请注意,此参数与filename或stream不兼容 - 如果两者都存在,则引发ValueError。

实例:

import logging.config

logging.basicConfig(
    filename='loggingDemo-basicConfig.log',
    level=logging.DEBUG,
    format='%(asctime)s [%(threadName)s] [%(name)s] [%(levelname)s] %(filename)s[line:%(lineno)d] %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

logging.debug('log debug')
logging.info('log info')
logging.warning('log warning')
logging.error('log error')
logging.critical('log critical')

4.4 第三种 logging.config.fileConfig 读取配置文件

配置文件logging.conf如下:

[loggers]
keys=root, fileLogger, rotatingFileLogger

[handlers]
keys=consoleHandler, fileHandler, rotatingFileHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_fileLogger]
level=DEBUG
# 该logger中配置的handler
handlers=fileHandler
# logger 的名称
qualname=fileLogger
propagate=0

[logger_rotatingFileLogger]
level=DEBUG
# 这样配置,rotatingFileLogger中就同时配置了consoleHandler,rotatingFileHandler
# consoleHandler 负责将日志输出到控制台
# rotatingFileHandler 负责将日志输出保存到文件中
handlers=consoleHandler,rotatingFileHandler
qualname=rotatingFileLogger
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=simpleFormatter
args=('./loggingDemo-file.log', 'a')

[handler_rotatingFileHandler]
class=handlers.RotatingFileHandler
level=WARNING
formatter=simpleFormatter
args=('./loggingDemo-rotating.log', 'a', 1*1024*1024, 5)

[formatter_simpleFormatter]
format=%(asctime)s [%(threadName)s] [%(name)s] [%(levelname)s] %(filename)s[line:%(lineno)d] %(message)s
datefmt=%Y-%m-%d %H:%M:%S

实例:

import logging.config
logging.config.fileConfig('./logging.conf')
log = logging.getLogger(name='rotatingFileLogger')
log.debug('log debug')
log.info('log info')
log.warning('log warning')
log.error('log error')
log.critical('log critical')

4.5 第四种 logging.config.dictConfig 读取配置信息

import logging.config
dictConfig = {
    'version': 1,
    'disable_existing_loggers': True,
    'incremental': False,
    'formatters': {
        'master_format': {
            'class': 'logging.Formatter',
            'format': '%(asctime)s [%(threadName)s] [%(name)s] [%(levelname)s] %(filename)s[line:%(lineno)d] %(message)s',
            'datefmt': '%Y-%m-%d %H:%M:%S'
        }
    },
    'filters': {
        'filter_by_name': {
            'class': 'logging.Filter',
            'name': 'fileLogger'
        },
        # 仅 INFO 能输出
        'filter_single_level_pass': {
            'class': 'logging.StreamHandler',
            'pass_level': logging.INFO
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': logging.DEBUG,
            'formatter': 'master_format',
        },
        'fileHandler': {
            'class': 'logging.FileHandler',
            'filename': 'logfile.log',
            'level': logging.INFO,
            'formatter': 'master_format',
            'filters': ['filter_by_name', ],
        },
        'rotatingFileHandler': {
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': 'rotatingLogfile.log',
            'level': logging.DEBUG,
            'formatter': 'master_format',
            'maxBytes': 256,
            'backupCount': 2
        },
    },
    'loggers': {
        'root': {
            'handlers': ['console', ],
            'level': 'INFO',
            'propagate': False
        },
        'fileLogger': {
            'handlers': ['console', 'fileHandler'],
            'filters': ['filter_by_name', ],
            'level': 'DEBUG'
        },
        'rotatingFileLogger': {
            'handlers': ['console', 'rotatingFileHandler'],
            'level': 'INFO'
        }
    }
}
logging.config.dictConfig(dictConfig)
log = logging.getLogger(name='fileLogger')
log.debug('log debug')
log.info('log info')
log.warning('log warning')
log.error('log error')
log.critical('log critical')

log2 = logging.getLogger(name='rotatingFileLogger')
log2.debug('log debug')
log2.info('log info')
log2.warning('log warning')
log2.error('log error')
log2.critical('log critical')

4.6 第五种 listen() 进行网络配置。

更多详细内容参考logging.config日志配置

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 229,460评论 6 538
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,067评论 3 423
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 177,467评论 0 382
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,468评论 1 316
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,184评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,582评论 1 325
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,616评论 3 444
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,794评论 0 289
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,343评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,096评论 3 356
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,291评论 1 371
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,863评论 5 362
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,513评论 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,941评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,190评论 1 291
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,026评论 3 396
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,253评论 2 375

推荐阅读更多精彩内容

  • 本文章是我大概三年前,在上家单位使用 Python 工作时结合官方文档做的整理。现在 Python 官方文档听说已...
    好吃的野菜阅读 217,407评论 14 232
  • 在现实生活中,记录日志非常重要。银行转账时会有转账记录;飞机飞行过程中,会有黑盒子(飞行数据记录器)记录飞行过程中...
    chliar阅读 777评论 1 0
  • logging介绍Python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用。这个模块...
    modingfa阅读 6,841评论 0 2
  • 在这信息网络发达的时代,许多中小学生的梦想都是做主播做电商之类的都憧憬着大城市的生活,哪有我们当初的梦想纯粹和天真...
    伊伊伊卡诺TQ阅读 682评论 0 2
  • 原文链接://www.greatytc.com/p/a62b6520e0de 介绍 FFmpeg是一个优...
    Tyhj阅读 2,007评论 3 4