Python Logging库HTTPHandler的消息格式化的方法
admin
2023-07-31 01:51:50
0

问题

Python的logging库是标准库中用来实现日志的库,功能强大,而且使用起来也算是方便。该库提供了很多个不同的Handler,用来对日志进行不同的处理。例如FileHandler用来将日志记录到文件,RotateFileHandler用来将日志记录到文件而且支持日志文件滚动备份,还有本文中所说的HttpHandler,可以将日志通过HTTP请求发送到服务器上。

使用Python的logging模块的过程大约有如下几个步骤:

  1. 根据配置文件、配置字典或者调用方法的方式初始化日志配置,并获取一个logger。

  2. 调用logger实例的如下方法来发出一条日志:critical, error, warning, info, debug。这些方法的定义如下,以info为例:

    logger.info(fmt, *args, exc_info, extra)
    

    P.S. 本文的目的不是说明logging如何使用,所以具体的用法请参考官方文档。

当logger对象调用info等方法发出一条日志时,他可以接受像C语言中的printf函数或者Python3中的pritnf函数一样的前两个参数:格式化字符串和对应的参数列表,用来表示要发出的日志的内容。当logging模块真的要发出这条日志时,才会对字符串进行格式化,并且加入最终的日志字符串中。因此,在Python参考手册(第4版)中(19.7节,289页)有强调了如下这一点:发出日志消息时,应该避免在发出消息时带有字符串格式化的代码(即格式化一条消息,然后把结果传递到日志记录模块中)。原因是,直接传递格式化后的字符串会导致参数被完全求值,这个有可能是非必要的,会导致日志性能下降。举个例子:

正确方式:
logger.info(\"hello, %s\", \"myname\")

错误方式:
logger.info(\"hello, %s\" % \"myname\")

那么问题来了,如果一个logger的handler使用了HttpHandler,这个坑爹货居然不会在发出日志前对日志内容部分进行格式化,而是只发送了前面的fmt字符串到http服务器,结果就像下面这样:

WARNING Tue Jan 27 15:27:34 2015 admin.config 192.168.100.126 POST /user/login User [%s] logged in failed.
而我们期待的应该是:

WARNING Fri Jan 23 11:36:45 2015 admin.config 192.168.100.126 POST /user/login User [admin] logged in failed.

解决方法

使用logging模块提供的Filter功能。

直接给出实例代码:

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

import logging
import logging.config
import logging.handlers


log_config_dict = {
    \"version\": 1,
    \"formatters\": {
        \"format_def\": {
            \"format\": \"%(levelname)-8s %(asctime)s %(name)s %(ip)s \"
            \"%(method)s %(path)s %(message)s\",
        },
    },
    \"handlers\": {
        \"handler_http\": {
            \"class\": \"logging.handlers.HTTPHandler\",
            \"formatter\": \"format_def\",
            \"level\": \"INFO\",
            \"host\": \"192.168.100.1:8888\",
            \"url\": \"/log/admin\",
            \"method\": \"POST\",
        },
    },
    \"loggers\": {
        \"admin.config\": {
            \"level\": \"INFO\",
            \"propagate\": 0,
            \"handlers\": [\"handler_http\"],
        },
        \"admin.api\": {
            \"level\": \"INFO\",
            \"propagate\": 0,
            \"handlers\": [\"handler_http\"],
        }
    },
}


class RequestFilter(logging.Filter):
    \"\"\"A filter used to add extra information to a record.

    Add ip, method and path information to a record for a HTTP request.

    Attributes:
        name: logger\'s name
    \"\"\"
    def __init__(self, name):
        self.name = name

    def filter(self, record):
        # 这里调用getMessage()方法得到格式化后的日志内容,
        # HTTP服务器上只要读取POST中的message参数即可。
        record.message = record.getMessage()
        return True


def init_log():
    logging.config.dictConfig(log_config_dict)


def get_logger(name):
    if type(name) is not str:
        return None

    log = logging.getLogger(name)
    log.addFilter(RequestFilter(name))  # 添加一个过滤器用来进行消息格式化
    log.addHandler(logging.NullHandler())

    return log


def get_config_logger():
    return get_logger(\"admin.config\")


def get_api_logger():
    return get_logger(\"admin.api\")

上面的中的中文注释部分直接说明了解决方案。

相关内容

热门资讯

500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
python查找阿姆斯特朗数 题目解释 如果一个n位正整数等于其各位数字的n次方之和,则称该数为阿姆斯特朗数。 例如1^3 + 5...
Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...