Python - 如何打印所有日志级别甚至外部异常的堆栈跟踪?

Python - How to print stack trace at all log levels and even outside exceptions?

我的代码库非常大,我们想打印所有日志的堆栈。

这包括所有级别的日志(甚至 INFO 和 DEBUG)。

这还包括异常发生之外的日志。

我学到的东西:

你走在正确的轨道上。我建议您阅读 logging HOWTO,它解释了很多事情。

您可以使用级别和过滤器来决定您的处理程序是否应该处理日志。并且您可以使用 Formatter 将堆栈跟踪添加到日志记录消息中。

这是一个概念验证:

import logging
import traceback
import sys


logger = logging.getLogger("app.main")


class StacktraceLogFormatter(logging.Formatter):
    def format(self, record: logging.LogRecord) -> str:
        stack_lines = traceback.format_stack()
        # we have the lines down to ^^^^^^ this call, but we don't want to show it, nor the internal logging ones
        stack_lines_without_logging_intermediary_calls = filter(
            lambda line: ("lib/logging/__init__.py" not in line)
                         and ("lib\logging\__init__.py") not in line,
            stack_lines[:-1]
        )
        return record.msg + "\nOrigin :\n" + "".join(stack_lines_without_logging_intermediary_calls)
        # beware of : 


class InfoOrLessFilter(logging.Filter):
    def filter(self, record: logging.LogRecord) -> bool:
        return record.levelno <= logging.INFO


def do_some_logging():
    logger.debug("debug message")
    logger.info("info message")
    logger.warning("warning message")
    logger.error("error message")
    try:
        1/0
    except ZeroDivisionError:
        logger.exception("exception message")
    logger.critical("critical message")


def setup_custom_logging():
    logger.handlers = []  # remove the existing handlers from the logger

    regular_handler_for_info_messages = logging.StreamHandler(sys.stdout)
    regular_handler_for_info_messages.setLevel(logging.DEBUG)  # at least DEBUG
    regular_handler_for_info_messages.addFilter(InfoOrLessFilter())  # but not more than INFO
    logger.addHandler(regular_handler_for_info_messages)

    stacktrace_handler_for_important_messages = logging.StreamHandler(sys.stderr)
    stacktrace_handler_for_important_messages.setLevel(logging.INFO + 1)  # more than INFO
    stacktrace_handler_for_important_messages.setFormatter(StacktraceLogFormatter())
    logger.addHandler(stacktrace_handler_for_important_messages)

    logger.propagate = False


def main():
    logging.basicConfig(level=logging.DEBUG)
    do_some_logging()
    setup_custom_logging()
    do_some_logging()


if __name__ == "__main__":
    main()

它产生于:

debug message
info message
DEBUG:app.main:debug message
INFO:app.main:info message
WARNING:app.main:warning message
ERROR:app.main:error message
ERROR:app.main:exception message
Traceback (most recent call last):
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 43, in do_some_logging
    1/0
ZeroDivisionError: division by zero
CRITICAL:app.main:critical message

及之后:

warning message
Origin :
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 73, in <module>
    main()
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 69, in main
    do_some_logging()
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 40, in do_some_logging
    logger.warning("warning message")

error message
Origin :
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 73, in <module>
    main()
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 69, in main
    do_some_logging()
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 41, in do_some_logging
    logger.error("error message")

exception message
Origin :
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 73, in <module>
    main()
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 69, in main
    do_some_logging()
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 45, in do_some_logging
    logger.exception("exception message")

critical message
Origin :
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 73, in <module>
    main()
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 69, in main
    do_some_logging()
  File "C:/PycharmProjects/stack_overflow/67846424.py", line 46, in do_some_logging
    logger.critical("critical message")