通用技术 logging 模块简介

李宏伟 · 2017年04月06日 · 最后由 thanksdanny 回复于 2017年08月13日 · 1369 次阅读

简单的日志输出

  • logging 模块提供了灵活的日志处理相关功能,可用来跟踪程序的运行情况,调试程序
  • 默认情况下 logging 将日志打印到 stdout 上,默认级别是 WARNING,只有高于 WARNING 级别的日志信息才会输出
  • 可以使用 logging.basicConfig 函数对 root logger 进行简单的配置,包括日志文件、输出格式等
import logging

logging.debug('This is debug message.')
logging.info('This is info message.')
logging.warning('This is warning message.')
logging.error('This is error message.')
logging.critical('This is critical message.')
# Output in stdout:

# WARNING:root:This is warning message.
# ERROR:root:This is error message.
# CRITICAL:root:This is critical message.
import logging

logging.basicConfig(level=logging.INFO,
                    filename='log.log',
                    filemode='w',
                    format='%(levelname)s - %(name)s - %(message)s')

logging.debug('This is debug message.')
logging.info('This is info message.')
logging.warning('This is warning message.')
logging.error('This is error message.')
logging.critical('This is critical message.')

# Output in log.log:

# INFO - root - This is info message. 
# WARNING - root - This is warning message.
# ERROR root - This - is error message.
# CRITICAL - root - This is critical message.

日志等级

日志共有 5 个级别,由高到低分别是 CRITICAL > ERROR > WARNING > INFO > DEBUG

级别 使用
DEBUG 详细信息,调试问题时用到
INFO 表明程序按照预期运行
WARNING 程序出现一些意外,或者在将来可能出现问题,目前程序仍可正常运行
ERROR 程序的部分功能已经不可用
CRITICAL 程序崩溃,已经不能继续运行

几个重要的概念

Logger

  • logger 是 logging.Logger 类的实例,提供了操作日志的接口
  • Logger 是一个树形层次结构,在使用 debug,info,warn,error,critical 方法之前必须创建 Logger 的实例
  • 如果没有显式的创建,则默认创建一个 root logger,默认 level 是 WARNING,默认 Handler 是 StreamHandler,默认 Formatter 是 Formatter(fmt='%(levelname) s:%(name) s:%(message) s')
  • logger 通过 logging.getLogger(name) 来创建,每个 Logger 实例都有一个 name,并且实例间存在继承关系。如名为'foo'的 logger 是名为'foo.bar'logger 的父类(他们以点号分割)。root 是所有 logger 的父类
  • 若未对 logger 进行显示配置,则 logger 继承自父类的配置。若父类也未配置,则可一直上溯到 root
  • 创建 logger 后,可使用 setLevel(level_name) 来设置日志级别,使用 addHandler(handler_name) 来添加处理器,使用 removeHandler(handler_name) 来删除处理器
import logging

logger = logging.getLogger('foo')
logger.setLevel(logging.INFO)

sh = logging.StreamHandler()
sh.setLevel(logging.INFO)

fmt = logging.Formatter(fmt='%(levelname)s - %(name)s - %(message)s')
sh.setFormatter(fmt)

logger.addHandler(sh)

logger.info('info message')

# INFO - foo - info message

Handler

  • handler 会将日志记录发送到合适的目的地,比如文件、标准输出、socket 等,一个 logger 对象可同通过 addHandler 方法添加多个 handler,每个 handler 可以定义不同的日志级别,实现日志的分级过滤
  • 常用的 handler 有 StreamHandler, FileHandler, NullHandler
  • 创建 Handler 之后,可以通过以下几个方法设置 Handler
    • setLevel(level_name) 指定 Handler 处理的日志级别
    • setFormatter(formatter_name) 指定 Handler 的格式化器
    • addFilter(filter_name) 添加一个过滤器
    • removeFilter(filter_name) 删除一个过滤器
"""StreamHandler example
"""

import logging
import sys

logger = logging.getLogger('stream')
logger.setLevel(logging.INFO)

sh = logging.StreamHandler(stream=sys.stdout)
sh.setLevel(logging.INFO)

fmt = Formatter(fmt='%(levelname)s:%(name)s:%(message)s')
sh.setFormatter(fmt)

logger.addHandler(sh)
logger.info('info message')

# INFO:stream:info message
"""FileHandler example
"""

import logging

logger = logging.getLogger('file')
logger.setLevel(logging.DEBUG)

fh = logging.FileHandler(filename='log.log', mode='w')
fh.setLevel(logging.DEBUG)

fmt = Formatter('%(name)s - line:%(lineno)d - %s')
fh.setFormatter(fmt)

logger.addHandler(fh)
logger.debug('debug message')

# file - line:16 - debug message
"""RotatingFileHandler example
"""

import logging
import glob
from logging.handlers import RotatingFileHandler

logger = logging.getLogger('rotatingfile')
logger.setLevel(logging.INFO)

rfh = RotatingFileHandler(
        filename='log.out',
        maxBytes=20,
        backupCount=5)

logger.addHandler(rfh)

for i in range(20):
    logger.info('i = {}'.format(i))

logfiles = glob.glob('log.out*')

for filename in logfiles:
    print filename

# log.out
# log.out.1
# log.out.2
# log.out.3
# log.out.4
# log.out.5

Filter

Handler 和 Logger 可以使用 Filter 来完成比级别更复杂的过滤。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='')

Formatter

指定日志记录输出的具体格式。Formatter 的构造方法需要两个参数:消息的格式字符串 fmt 和日期字符串 datefmt,这两个参数都是可选的。

  • fmt
格式 描述
%(name) s 日志名称
%(levelno) s 日志级别数值
%(levelname) s 日志级别
%(pathname) s 当前程序执行路径
%(filename) s 当前执行程序名称
%(module) s 当前模块
%(funcName) s 当前函数名称
%(lineno) d 行号
%(asctime) s 日志时间
%(thread) d 线程 ID
%(threadName) s 线程名称
%(process) d 进程 ID
%(message) s 日志消息

配置

显式配置

# -*- coding: utf-8 -*-

import logging

logger_name = "example"
logger = logging.getLogger(logger_name)
logger.setLevel(logging.DEBUG)

log_path = "./log.log"
fh = logging.FileHandler(log_path)
fh.setLevel(logging.WARN)

fmt = "%(asctime)-15s %(levelname)s %(lineno)d %(process)d %(message)s"
datefmt = "%a %d %b %Y %H:%M:%S"
formatter = logging.Formatter(fmt, datefmt)

fh.setFormatter(formatter)
logger.addHandler(fh)

logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

# Thu 06 Apr 2017 16:58:09 WARNING 20 7472 warn message
# Thu 06 Apr 2017 16:58:09 ERROR 21 7472 error message
# Thu 06 Apr 2017 16:58:09 CRITICAL 22 7472 critical message

配置文件

# logging.conf

[loggers]
keys=runner,monitor

[handlers]
keys=runnerHand,monitorHand

[formatters]
keys=defaultForm

[logger_runner]
level=DEBUG
handlers=runnerHand
propagate=0
qualname=runner

[logger_monitor]
level=INFO
handlers=monitorHand
propagate=0
qualname=monitor

[handler_runnerHand]
class=FileHandler
level=DEBUG
formatter=defaultForm
args=('log\\runner.log', 'w')

[handler_monitorHand]
class=FileHandler
level=INFO
formatter=defaultForm
args=('log\\monitor.log', 'w')

[formatter_defaultForm]
format=%(asctime)s - %(levelname)s - %(message)s
# -*- coding: utf-8 -*-

import logging
from logging.config import fileConfig

fileConfig('logging.conf')

if __name__ == '__main__':
    monitor_logger = logging.getLogger('monitor')
    monitor_logger.info('This is monitor log')

    runer_logger = logging.getLogger('runner')
    runer_logger.info('This is runner log')

字典配置

# -*- coding: utf-8 -*-

import logging
from logging.config import dictConfig

logging_conf = dict(
    version=1,
    loggers={
        'runner': {
            'level': logging.INFO,
            'handlers': ['runnerHand'],
            'propagate': 0,
            'qualname': 'runner'
        },
        'monitor': {
            'level': logging.INFO,
            'handlers': ['monitorHand'],
            'propagate': 0,
            'qualname': 'monitor'
        }
    },
    handlers={
        'runnerHand': {
            'class': 'logging.FileHandler',
            'level': logging.INFO,
            'formatter': 'defaultFormatter',
            'filename': 'runner.log',
            'mode': 'w'
        },
        'monitorHand': {
            'class': 'logging.FileHandler',
            'level': logging.INFO,
            'formatter': 'defaultFormatter',
            'filename': 'monitor.log',
            'mode': 'w'
        }
    },
    formatters={
        'defaultFormatter': {
            'format': '%(asctime)s - %(levelname)s - %(message)s'
        }
    }
)

dictConfig(logging_conf)

if __name__ == '__main__':
    monitor_logger = logging.getLogger('monitor')
    monitor_logger.info('This is monitor log')

    runer_logger = logging.getLogger('runner')
    runer_logger.info('This is runner log')
共收到 5 条回复 时间 点赞

最难用的模块

codeskyblue 回复

其实还好,弄清楚了就不难用了

学习了,很受用,谢谢你

logging 是不是不能实现函数调用的功能?
比如:
def test1(self):
today = datetime.date.today()
print(today)
logging.info("明天时间是:",today)
试过,好像不可以的。

回来复习一遍

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册