logging 模块的特点及用法
一, 概述
很多程序都有记录日志的需求, 并且日志中包含的信息即有正常的程序访问日志, 还可能有错误, 警告等信息输出, python 的 logging 模块提供了标准的日志接口, 你可以通过它存储各种格式的日志, logging 的日志可以分为 debug(), info(), warning(), error() and
critical() 5 个级别, 其中他们级别大小关系
debug() <info() <warning() <error() <
critical() 级别越低打印的日记等级就越多
下面我们看一下他是怎样的用法.
二, 用法
1, 最简单的用法
import logging
logging.debug("logging debug")
logging.info("logging info")
logging.warning("user [qianduoduo] attempted wrong password more than 3 times")
logging.error("logging error")
logging.critical("logging critical")
#输出
WARNING:root:user [qianduoduo] attempted wrong password more than 3 times
ERROR:root:logging error
CRITICAL:root:logging critical #root 就是默认的用户名
重点: 为什么上面 debug 和 info 没有输出, 那是因为一个模块默认的日志级别是 warning, 比他级别低的就不会输出 2, 日志级别看一下这几个日志级别分别代表什么意思, 如表:
Level | When it's used |
---|---|
DEBUG | 详细的信息, 通常只有在诊断问题时才感兴趣. |
INFO | 确认事情按预期工作. |
WARNING | 表明发生了意外的事情, 或预示在不久的将来会出现一些问题 (例如" 磁盘空间低 "). 该软件仍按预期运行. |
ERROR | 由于一个更严重的问题, 软件无法执行某些功能. |
CRITICAL | 一个严重的错误, 表明程序本身可能无法继续运行. |
3, 将日记写入文件
import logging
logging.basicConfig(filename='duoduo.log',level=logging.INFO)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
#'duoduo.log'文件输出
INFO:root:So should this
WARNING:root:And this, too #这日记的级别设置在 info, 所以比他更低的 debug 不显示
重点: 如果我们想显示所有级别的日记就要把日记的等级设置为 debug, 就在 level=logging.DEBUG, 设置是级别的名称要大写
4, 加入日期格式 感觉上面的日志格式忘记加上时间啦, 日志不知道时间怎么行呢, 下面就来加上
import logging
logging.basicConfig(filename='duoduo.log',
level=logging.DEBUG,
format='%(asctime)s %(message)s',#asctime 字符串形式的当前时间, message 用户输出的消息
datefmt='%Y-%m-%d %I:%M:%S %p')
logging.debug("logging debug")
logging.info("logging info")
logging.warning("user [qianduoduo] attempted wrong password more than 3 times")
logging.error("logging error")
logging.critical("logging critical")
#输出到文件'duoduo.log'
2018-01-27 07:33:30 PM logging debug
2018-01-27 07:33:30 PM logging info
2018-01-27 07:33:30 PM user [qianduoduo] attempted wrong password more than 3 times
2018-01-27 07:33:30 PM logging error
2018-01-27 07:33:30 PM logging critical
5, format 的日志格式
Logger 的名字 | |
%(levelno)s | 数字形式的日志级别 |
%(levelname)s | 文本形式的日志级别 |
%(pathname)s | 调用日志输出函数的模块的完整路径名, 可能没有 |
%(filename)s | 调用日志输出函数的模块的文件名 |
%(module)s | 调用日志输出函数的模块名 |
%(funcName)s | 调用日志输出函数的函数名 |
%(lineno)d | 调用日志输出函数的语句所在的代码行 |
%(created)f | 当前时间, 用 UNIX 标准的表示时间的浮 点数表示 |
%(relativeCreated)d | 输出日志信息时的, 自 Logger 创建以 来的毫秒数 |
%(asctime)s | 字符串形式的当前时间. 默认格式是 "2003-07-08 16:49:45,896". 逗号后面的是毫秒 |
%(thread)d | 线程 ID. 可能没有 |
%(threadName)s | 线程名. 可能没有 |
%(process)d | 进程 ID. 可能没有 |
%(message)s | 用户输出的消息 |
6,log 的 "终极奥义" 用法
如果想同时把 log 打印在屏幕和文件日志里, 就需要了解一点复杂的知识 了 Python 使用 logging 模块记录日志涉及四个主要类, 使用官方文档中的概括最为合适: 1,logger 提供了应用程序可以直接使用的接口; 2,handler 将 (logger 创建的) 日志记录发送到合适的目的输出; 3,filter 提供了细度设备来决定输出哪条日志记录; 4,formatter 决定日志记录的最终输出格式. 1),logger①每个程序在输出信息之前都需要获得一个 logger.logger 通常对应了程序的模块名, 比如聊天工具的图形界面模块可以这样获得它的 logger:
logger = logging.getLogger("chat.gui")
核心模块可以这样写:(自己想叫什么名称就叫什么名称)
logger = logging.getLogger("chat.kernel")
②logger.setLevel(lel) 说明: 指定最低的日志级别, 低于 lel 的级别将被忽略 (debug 是最低的内置级别, critical 为最高)
logger.setLevel(logging.DEBUG) #设置级别为 debug 级别
③Logger.addFilter(filt),Logger.removeFilter(filt) 说明: 添加或删除指定的 filter④logger.addHandler(hdlr),logger.removeHandler(hdlr) 说明: 增加或删除指定的 handler
logger.addHandler(ch)# 添加 handler
logger.removeHandler(ch) #删除 handler
⑤Logger.debug(),Logger.info(),Logger.warning(),Logger.error(),Logger.critical() 说明: 可以设置的日志级别
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
⑥获取 handler 个数
handler_len = len(logger.handlers)
print(handler_len)
# 输出
1
2),handler handler 对象负责发送相关的信息到指定目的地. Python 的日志系统有多种 Handler 可以使用. 有些 Handler 可以把信息输出到控制台, 有些 Logger 可以把信息输出到文件, 还有些 Handler 可以把信息发送到网络上. 如果觉得不够用, 还可以编写自己的 Handler. 可以通过 addHandler() 方法添加多个多 handler①Handler.setLevel(lel) 说明: 指定被处理的信息级别, 低于 lel 级别的信息将被忽略.
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
②Handler.setFormatter() 说明: 给这个 handler 选择一个格式
ch_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")#生成格式,
具体需要什么格式看需求ch.setFormatter(ch_formatter)#设置格式
③Handler.addFilter(filt),Handler.removeFilter(filt) 说明: 新增或删除一个 filter 对象
三, handler"终极奥义" 详解
1,logging.StreamHandler 说明: 使用这个 Handler 可以向类似与 sys.stdout 或者 sys.stderr 的任何文件对象 (file object) 输出信息, 也就是屏幕输出. 它的构造函数是: StreamHandler([strm]), 其中 strm 参数是一个文件对象, 默认是 sys.stderr. 具体用法看下面代码:
import logging
logger = logging.getLogger("TEST-LOG")
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler() #创建一个 StreamHandler 对象
ch.setLevel(logging.DEBUG) #设置输出 StreamHandler 日志级别
ch_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") #格式
ch.setFormatter(ch_formatter) #传入格式参数
logger.addHandler(ch) #增加 handler
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
输出
2018-01-28 21:15:02,111 - TEST-LOG - DEBUG - debug message
2018-01-28 21:15:02,112 - TEST-LOG - INFO - info message
2018-01-28 21:15:02,112 - TEST-LOG - WARNING - warn message
2018-01-28 21:15:02,112 - TEST-LOG - ERROR - error message
2018-01-28 21:15:02,112 - TEST-LOG - CRITICAL - critical message
2,logging.FileHandler 说明: 和 StreamHandler 类似, 用于向一个文件输出日志信息, 不过 FileHandler 会帮你打开这个文件. 它的构造函数是: FileHandler(filename[,mode]).filename 是文件名, 必须指定一个文件名. mode 是文件的打开方式. 参见 Python 内置函数 open() 的用法. 默认是'a', 即添加到文件末尾. 用法看下面代码:
import logging
#create logging
logger = logging.getLogger("TEST-LOG")
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler("debug.log",encoding="utf-8") #日志输出到 debug.log 文件中
fh.setLevel(logging.INFO) #设置 FileHandler 日志级别
fh_formatter = logging.Formatter("%(asctime)s %(module)s:%(levelname)s %(message)s")
fh.setFormatter(fh_formatter)
logger.addHandler(fh)
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')
文件 debug.log 里的输出:
2018-01-28 21:19:45,221 logging 文件输出: INFO info message
2018-01-28 21:19:45,221 logging 文件输出: WARNING warning message
2018-01-28 21:19:45,221 logging 文件输出: ERROR error message
2018-01-28 21:19:45,221 logging 文件输出: CRITICAL critical message
3,logging.handlers.RotatingFileHandler 说明: 这个 Handler 类似于上面的 FileHandler, 但是它可以管理文件大小. 当文件达到一定大小之后, 它会自动将当前日志文件改名, 然后创建 一个新的同名日志文件继续输出. 比如日志文件是 chat.log. 当 chat.log 达到指定的大小之后, RotatingFileHandler 自动把 文件改名为 chat.log.1. 不过, 如果 chat.log.1 已经存在, 会先把 chat.log.1 重命名为 chat.log.2... 最后重新创建 chat.log, 继续输出日志信息. 它的构造函数是: RotatingFileHandler(filename[, mode[, maxBytes[, backupCount]]]), 其中 filename 和 mode 两个参数和 FileHandler 一样. maxBytes 用于指定日志文件的最大文件大小. 如果 maxBytes 为 0, 意味着日志文件可以无限大, 这时上面描述的重命名过程就不会发生. backupCount 用于指定保留的备份文件的个数. 比如, 如果指定为 2, 当上面描述的重命名过程发生时, 原有的 chat.log.2 并不会被更名, 而是被删除. 用法看下面代码:
import logging
from logging import handlers #需要导入 handlers
logger = logging.getLogger(__name__)
log_file = "timelog.log"
#按文件大小来分割, 10 个字节 maxBytes, 保留个数是 3 个
fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3,encoding="utf-8")
formatter = logging.Formatter('%(asctime)s %(module)s: %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.warning("test11")
logger.warning("test12")
logger.warning("test13")
logger.warning("test14")
logger.warning("test15")
文件的结果就是
timelog.log 2018-01-28 21:31:38,513 logging 文件个数保留: test15
timelog.log1 2018-01-28 21:31:38,500 logging 文件个数保留: test14
timelog.log2 2018-01-28 21:31:38,487 logging 文件个数保留: test13
timelog.log3 2018-01-28 21:31:38,471 logging 文件个数保留: test12
4,logging.handlers.TimedRotatingFileHandler 说明: 这个 Handler 和 RotatingFileHandler 类似, 不过, 它没有通过判断文件大小来决定何时重新创建日志文件, 而是间隔一定时间就 自动创建新的日志文件. 重命名的过程与 RotatingFileHandler 类似, 不过新的文件不是附加数字, 而是当前时间. 它的构造函数是: TimedRotatingFileHandler(filename [,when [,interval [,backupCount]]]), 其中 filename 参数和 backupCount 参数和 RotatingFileHandler 具有相同的意义. interval 是时间间隔. when 参数是一个字符串. 表示时间间隔的单位, 不区分大小写. 它有以下取值:①S: 秒②M: 分③H: 小时④D: 天⑤W : 每星期 (interval==0 时代表星期一)⑥midnight: 每天凌晨
import logging
from logging import handlers
import time
logger = logging.getLogger("duoduo-LOG")
log_file = "timelog.log"
#按时间来分割文件, 按 5 秒一次分割, 保留日志个数是 3 个
fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3,encoding="utf-8")
formatter = logging.Formatter('%(asctime)s %(module)s: %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.warning("test11")
time.sleep(2)
logger.warning("test12")
time.sleep(2)
logger.warning("test13")
time.sleep(2)
logger.warning("test14")
logger.warning("test15")
#文件输出
#这里就区分了两个, 前面 11,12,13 属于一个文件的内容, 后面 14,15 一个文件的内容, 但是后面读秒少所以先创建的文件是后面的 14,15
5, 控制台和文件日志共同输出需要什么样的输出, 只需要添加相应的 handler 就 ok 了. 逻辑图:
代码如下:
import logging
#create logging
logger = logging.getLogger("TEST-LOG")
logger.setLevel(logging.DEBUG)
#屏幕 handler
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
#文件 handler
fh = logging.FileHandler("debug.log",encoding="utf-8")
fh.setLevel(logging.INFO)
#分别创建输出日志格式
ch_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
fh_formatter = logging.Formatter("%(asctime)s %(module)s:%(levelname)s %(message)s")
#设置 handler 的输出格式
ch.setFormatter(ch_formatter)
fh.setFormatter(fh_formatter)
#添加 handler
logger.addHandler(ch)
logger.addHandler(fh)
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
注: 如果添加时间分割或者文件大小分割, 再修改上述代码写入文件的 handler 即可.
来源: https://www.cnblogs.com/ManyQian/p/8372593.html