python日志输出的内容修改为json格式

业务中要求,python项目的日志输出为json串,同时包括异常;经过查看python logging相关的源码,发现还不能完全的兼容;比如异常的源码那里:

class Formatter(object):
    """省略"""
    def format(self, record):
        record.message = record.getMessage()
        if self.usesTime():
            record.asctime = self.formatTime(record, self.datefmt)
        s = self.formatMessage(record)
        if record.exc_info:
            # Cache the traceback text to avoid converting it multiple times
            # (it's constant anyway)
            if not record.exc_text:
                record.exc_text = self.formatException(record.exc_info)
        if record.exc_text:
            if s[-1:] != "\n":
                s = s + "\n"
            s = s + record.exc_text
        if record.stack_info:
            if s[-1:] != "\n":
                s = s + "\n"
            s = s + self.formatStack(record.stack_info)
        return s

logging.Formatter的format方法,首先会按照格式化串格式化message,然后如果出现异常,是直接再message后面加上异常;此时格式已经不是指定的格式,因此这里需要修自定义。

# -*- coding:utf-8 -*-
import json
import logging
import os
import traceback



BASE_DIR = os.path.abspath(os.getcwd())
LOG_DIR = os.path.join(BASE_DIR,  "logs")

host_ip ="localhost"

JSON_LOGGING_FORMAT = json.dumps({
    "ip": "%(ip)s",
    "app": "%(app)s",
    "level": "%(levelname)s",
    "trace": "%(stack_msg)s",
    "filepath": "%(pathname)s",
    "line_number": "%(lineno)s",
    "time": "%(asctime)s",
    "message": "%(message)s",
    "stack_trace": "%(exc_text)s"
})


class JsonLoggingFilter(logging.Filter):
    def __init__(self, name, ip, app):
        logging.Filter.__init__(self, name=name)
        self.ip = ip
        self.app = app

    def filter(self, record):
        record.ip = self.ip
        record.app = self.app
        # 为record 添加异常堆栈信息字段; 当有多个handler 的时候,这里会判断多次
        if hasattr(record, "stack_msg") and hasattr(record, "stack_trace"):
            return True

        if record.exc_info:
            ex_type, ex_val, ex_stack = record.exc_info
            stack_list = []
            for stack in traceback.extract_tb(ex_stack):
                stack_list.append("%s" % stack)

            record.stack_msg = ex_val
            record.stack_trace = "#".join(stack_list)
        else:
            record.stack_msg, record.stack_trace = "", ""

        return True


class JsonFormatter(logging.Formatter):
    def __init__(self, fmt=None):
        logging.Formatter.__init__(self, fmt=fmt)

    def format(self, record):
        record.message = record.getMessage()
        if self.usesTime():
            record.asctime = self.formatTime(record, self.datefmt)

        if record.exc_info:
            # Cache the traceback text to avoid converting it multiple times
            # (it's constant anyway)
            if not record.exc_text:
                record.exc_text = self.formatException(record.exc_info).replace("\n", " ").replace("\"", "'")

        s = self.formatMessage(record)
        return s


class JsonLogger(logging.Logger):
    logger = None
    level = None
    mode = None

    def __init__(self, app_name, level=logging.DEBUG, console_level=logging.INFO, mode="w"):
        self.name = app_name
        self.app_name = app_name

        logging.Logger.__init__(self, name=app_name)

        self.logger = logging.Logger(name=app_name)
        self.logger.setLevel(level)

        if not os.path.exists(LOG_DIR):
            os.makedirs(LOG_DIR)
        log_file_path = os.path.join(LOG_DIR, "%s.json" % app_name)
        json_logging_filter = JsonLoggingFilter(app_name, ip=host_ip, app=app_name)
        json_formatter = JsonFormatter(JSON_LOGGING_FORMAT)

        # 文件日志
        file_handle = logging.FileHandler(log_file_path, mode=mode)
        file_handle.setLevel(level)
        file_handle.setFormatter(json_formatter)
        file_handle.addFilter(json_logging_filter)
        # 控制台日志
        console_handle = logging.StreamHandler()
        console_handle.setLevel(console_level)
        console_handle.setFormatter(json_formatter)
        console_handle.addFilter(json_logging_filter)

        self.logger.addHandler(file_handle)
        self.logger.addHandler(console_handle)

    def getLogger(self):
        return self.logger

    def setLevel(self, level):
        self.logger.level = level


if __name__ == '__main__':
    my_logger = JsonLogger("python-common").getLogger()
    my_logger.info("info  level log")
    try:
        open('/path/to/does/not/exist', 'rb')
    except FileNotFoundError as e:
        my_logger.exception("file exception", exc_info=e)

 

参考:

https://www.cnblogs.com/yyds/p/6901864.html

关于异常 https://www.jianshu.com/p/b342b19657fc

日志输出 json串 https://blog.csdn.net/diqiuyi7777/article/details/86498203

posted @ 2019-10-21 20:09  百变小超  阅读(3264)  评论(1编辑  收藏  举报