40 Python logging

logger处理流程

这里写图片描述

学习logging的使用

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
参考网址
http://www.cnblogs.com/captain_jack/archive/2011/01/21/1941453.html
http://www.jianshu.com/p/feb86c06c4f4
# 官网的解释比较好
http://python.usyiyi.cn/translate/python_278/howto/logging.html#logging-basic-tutorial
http://python.usyiyi.cn/translate/python_278/howto/logging-cookbook.html#logging-cookbook
http://python.usyiyi.cn/documents/python_278/library/logging.config.html#logging-config-fileformat

logging: 记录log, 灵活性较大
logger(记录器)
日志级别
级别  数字值
CRITICAL    50
ERROR   40
WARNING 30
INFO    20
DEBUG   10
NOTSET  0

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

记录日志通过调用Logger的实例的方法来完成,每个实例都有名字,它们在概念上组织成一个层级式的命名空间,使用点(.)作为分隔符。
例如,名为"app"的Logger是Logger:app.qq, app.weixin的父节点, 记录器名字可以任意命名, 用以表示记录的信息是在应用的哪个部分所产生的.

Logger对象要做三件事情,
1. 它们向应用代码暴露了许多方法, 这样应用可以在运行时记录消息;
2. 记录器对象通过严重程度或者过滤器对象来决定哪些日志消息需要记录下来;
3. 记录器对象将相关的日志消息传递给所有感兴趣的handler;

logger对象有效级别(level)的概念, 如果一个记录器没有显式地设置级别, 那它父节点的级别被用作有效级别, 如果父节点也没有明确设置级别,
那么检查父节点的父节点. 如此反复, 直至找到一个有明确地设置级别, 根记录器总是明确地设置级别(默认为WARNING), 在决定是否要处理事件时,
logger的level用来判断是否要将事件传递给logger的handler.

子记录器可以将消息传播给父记录器的处理器, 正因为如此, 没有必要给应用中用到的所有的记录器都配置处理器,
完全可以只给顶级的记录器配置处理器, 只在必要的时候创建子记录器.(可通过将记录器的propagate属性设置为False来关闭传播, 默认为True)

root logger = logging.getLogger()/ logging.getLogger("");
logger是层级结构, logger会向父logger传递日志信息, root logger是最顶层的logger对象,即根节点(需要配置root logger);
logger的层级结构和packet很相似, app.qq是app的孩子, app.qq会将日志传递给app的handler处理;
logging.getLogger('loggername')会返回对同一个logger对象的引用, 跨模块也是一样, 只要它们在同一个Python解释器进程中, 它们都是指向同一个对象;
应用程序代码可以在一个模块中定义和配置父记录器, 并在单独的模块中创建, 并且所有对子进程的记录器调用将传递给父级;
只有>=logger对象设置的level才交给handler处理
"""
import logging
import logging.config
logName = "test_logging"


def record(logger):
    logger.debug('debug')
    logger.info('info')
    logger.warning('warning')
    logger.error('error')
    logger.critical('critical')

def build_in_logger():
    """logging中内置了logger对象, 直接使用即可, 默认情况下将log输出到控制台, 级别为WARNING(只有日志级别>=WARNING的日志信息才会输出)"""
    record(logging)

def config_build_in_logger():
    """将内置logger的log输出到文件中, 并设置级别为DEBUG"""
    # 根据应用和调试, 设置信息级别(level), 调整输出, filename=输出到文件
    logging.basicConfig(level=logging.DEBUG, filename=logName, filemode="wb")
    record(logging)

def define_logger():
    """自定义logger对象, 注意层级结构"""
    # logging.getLogger([name]), 返回一个logger对象, 如果没有指定名字或name等于"", 将返回root logger
    logger = logging.getLogger("test_logger")
    # 指定最低的日志级别, 低于level的级别将被忽略, debug是最低的内置级别,critical为最高
    logger.setLevel(logging.DEBUG)
    # 未添加任何处理器, 不会有输出, 会提示没有任何handler
    logger.debug("debug before add handler")
    # 定义输出格式
    formatter = logging.Formatter("%(asctime)s %(name)-12s %(levelname)-12s %(message)s")
    # 创建向控制台输出的handler, 只输出>=WARNING的信息
    console = logging.StreamHandler()
    # 设置输出的最低级别, 只输出>=WARNING的日志
    console.setLevel(logging.WARNING)
    # 设置log输出格式
    console.setFormatter(formatter)
    # Logger.addHandler(hdlr), Logger.removeHandler(hdlr), 增加或删除指定的处理器
    logger.addHandler(console)
    # 创建向文件输出的处理器, 输出所有信息
    fh = logging.FileHandler(logName, encoding="utf-8", mode="w")
    # 记录leve>=DEBUG的日志
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    record(logger)

def test_logger_pass_up1():
    """配置 root logger,测试向上传递log信息, 并设置logger的level"""
    root = logging.getLogger()
    # 设置最低级的level
    root.setLevel(logging.ERROR)
    console = logging.StreamHandler()
    console.setLevel(logging.WARN)
    console.setFormatter(logging.Formatter("%(name)-12s %(levelname)-12s %(message)s"))
    root.addHandler(console)
    # root logger是所有自定义logger的父logger, 但是需要给root logger配置
    # 尽管没有给test logger配置handler, , 但是还是会打印log, 由于向root logger传递了log
    logger = logging.getLogger("test")
    print logger.propagate # 向上传播
    record(logger)

def test_logger_pass_up2():
    """测试子logger向父节点传递日志"""
    root = logging.getLogger()
    console = logging.StreamHandler()
    console.setLevel(logging.DEBUG)
    console.setFormatter(logging.Formatter("root   %(levelname)-12s %(message)s"))
    root.addHandler(console)

    parent = logging.getLogger("app")
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    console.setFormatter(logging.Formatter("parent %(levelname)-12s %(message)s"))
    parent.addHandler(console)

    # 虽然child没有定义向控制台输出的handler, 但是子logger child会将日志信息传递给父logger parent
    # 可以认为child继承了父logger的handler
    child = logging.getLogger("app.qq")
    record(child)
    # child定义了相同类型的handler, 还是会将日志信息传递给父logger, 顺序child->parent->root
    console = logging.StreamHandler()
    console.setLevel(logging.WARNING)
    console.setFormatter(logging.Formatter("child  %(levelname)-12s %(message)s"))
    child.addHandler(console)
    record(child)

def example():
    root_logger = logging.getLogger()
    console = logging.StreamHandler()
    console.setLevel(logging.DEBUG)
    formatter = logging.Formatter('root_logger %(name)-12s: %(levelname)-8s %(message)s')
    console.setFormatter(formatter)
    root_logger.addHandler(console)

    # logger对象会将日志信息传递给root logger
    app_logger = logging.getLogger("app")
    # >=app_logger设置的level, 然后才交给handler处理, 如果小于app_logger的level, 不会讲该消息传递给上层
    app_logger.setLevel(logging.INFO)
    console = logging.StreamHandler()
    console.setLevel(logging.DEBUG)
    formatter = logging.Formatter('app_logger  %(name)-12s: %(levelname)-8s %(message)s')
    console.setFormatter(formatter)
    app_logger.addHandler(console)

    # logger是层级结构, 层次结构如下=root->app->weixin,qq, 虽然logger1和logger2没有任何handler,
    # 但是会将日志信息传递给root logger
    logger1 = logging.getLogger('app.qq')
    logger2 = logging.getLogger('app.weixin')
    record(logger1)
    logger1.info("%s%s", 1, 2)
    # record(logger2)

def test_logging_file_config():
    """根据配置文件配置logger对象, 只要理解了上面写配置文件很简单"""
    logging.config.fileConfig("logging.config")
    # mylog 输出到控制台
    # test, 未在配置文件中定义, 继承root logger
    logger_names = ("mylog", "test")
    # 不在配置文件中的handler会继承root的设置
    for logger_name in logger_names:
        logger = logging.getLogger(logger_name)
        record(logger)
if __name__ == '__main__':
    # object.__getattribute__
    # build_in_logger()
    # config_build_in_logger()
    # define_logger()
    test_logger_pass_up1()
    # test_logger_pass_up2()
    # example()
    # test_logging_file_config()

logging.config

# 文件中必须包含[loggers],[handlers],[formatters]
# [loggers], 定义logger对象
# [handlers], 定义handler对象
# [formatters], 定义formatter对象

# logger对象的命名是分层次的, 所有的logger对象是root的子节点, 会继承root的配置
# example, app.qq, app.weixin, 都是app的子节点, 如果有logger app, 并且app的子节点未指定相关配置, app的子节点会继承app的配置

# logger对象的声明, 定义了名为root和mylog的logger对象, 相当于变量名
[loggers]
keys = root, mylog

# handler对象的声明, 定义了名为handler_console, handler_file的handler对象
[handlers]
keys = handler_console, handler_file

# formatter对象的声明, 定义了名为format_console, format_file的对象
[formatters]
keys = format_console, format_file

# handler的配置项
# level     DEBUG, INFO, WARNING, ERROR, CRITICAL, NOTSET中的一个
# NOTSET    表示所有的消息都要记录, 只对root logger有效, 其他logger对象无效, 如果非根logger的级别为NOTSET, level=父节点的level
# handlers  "逗号"分隔的handler对象的列表, handler对象必需出现在[handlers]中, 且在配置文件中有相应的定义
# propagate 为1表示将消息传递给高层次logger的handler, 为0表示不传播, 一般设为0,
#           propagate(传播, 广播)即将日志信息传递给父节点, 父节点也会处理该日志信息
# qualname  logger对象在层次中的名字, 通过name属性得到logger name, 即logging.getLogger(name)中的name, 这个才是程序中logger对象的名字

# 对声明对象的实现
# logger对象中root logger的实现, root logger必需指定级别和handler列表, root logger不指定名字(qualname)和propagate
[logger_root]
level = ERROR
handlers = handler_console, handler_file

# logger对象中mylog logger的实现
[logger_mylog]
handlers = handler_console
qualname = mylog
propagate = 0
level = DEBUG

# handler对象的实现
# formatter  输出格式, 默认使用logging._defaultFormatter
# args       构造handler对象的参数, 即__init__中的参数

# handler_console对象的实现, 和程序中的初始化相似
[handler_handler_console]
class = StreamHandler
level = INFO
formatter = format_console
args=(sys.stderr,)

# handler_file对象的实现
[handler_handler_file]
class = FileHandler
level = DEBUG
formatter = format_file
args = ('log', 'a')

# 输出格式对象的实现
# format  整体格式化字符串
# datafmt 时间格式化字符串, 如果为空, 默认使用'%Y-%m-%d %H:%M:%S' 最后还会输出ms
# class   可选参数, 表示formatter类名

# format_console的实现
[formatter_format_console]
class = logging.Formatter
datafmt =
format = %(name)s %(levelname)s %(message)s

# format_file的实现
[formatter_format_file]
format = %(asctime)s %(filename)-15s [line:%(lineno)-3d] %(name)s %(levelname)s %(message)s

推荐:https://blog.csdn.net/luanpeng825485697/article/details/79470478

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值