可用于日志格式的调用堆栈

2 投票
3 回答
1190 浏览
提问于 2025-04-16 13:55

我有一个方法,它可以返回一个简单的调用栈字符串。


def call_stack():
    if "inspect" not in globals().keys():
        import inspect
    stack = [frame[3] for frame in inspect.stack() if frame[3] not in [inspect.stack()[0][3],"<module>"]]
    s = ""
    for method in stack: 
        if len(s) > 0:
            s += "." 
        s += method
    return s

def modify_logger(logger):
    logger.args["call_stack"] = call_stack()

这样做是可能的吗?


import logging
logging.basicConfig(level=logging.DEBUG, format="%(call_stack)s -- %(levelname)s:  %(message)s")

def bar():
    logging.debug("test")

def foo():
    bar()

def monkey():
    foo()

# modify root logger.
modify_logger(logging.getLogger())

# write to the log.
monkey()

这会得到以下的结果:


monkey.foo.bar -- DEBUG: test

3 个回答

1

我试了你的解决方案,结合了其他的方法,结果得到了我想要的效果。

    import logging
    DEBUG = True
    TRACE = True

    #-----------------------------------------------------------------------------
    # TRACE BACK
    #-----------------------------------------------------------------------------
    class ContextFilter(logging.Filter):
        def filter(self, record):
            import traceback
            if "inspect" not in globals().keys():
                import inspect
            stack = [frame[3] for frame in inspect.stack()
            if frame[3] not in [inspect.stack()[0][3],"<module>"]]
            record.stack ='.'.join(reversed(stack))
            return True

    #-----------------------------------------------------------------------        ---------
    # LOGGER
    #--------------------------------------------------------------------------------
    logger = logging.getLogger()
    if TRACE:
        logger.addFilter(ContextFilter())

    if DEBUG:
        logger.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(stack)s\n{%(pathname)s:%(lineno)d} - %(asctime)s - %(levelname)s - %(message)s')

        fh = logging.FileHandler('log_filename.txt')
        fh.setLevel(logging.DEBUG)
        fh.setFormatter(formatter)
        logger.addHandler(fh)

        ch = logging.StreamHandler()
        ch.setLevel(logging.DEBUG)
        ch.setFormatter(formatter)
        logger.addHandler(ch)

    else:
        logger.setLevel(logging.NOTSET)
        print 'NOT IN DEBUG MODE: Logging.NOTSET'
1

也许最简单的方法就是定义一个自定义的 debug 函数:

import logging

def call_stack():
    if "inspect" not in globals().keys():
        import inspect
    stack = [frame[3] for frame in inspect.stack()
             if frame[3] not in [inspect.stack()[0][3],"<module>"]]
    s='.'.join(reversed(stack))
    return s

def debug(message):
    logging.debug('{s} -- DEBUG: {m}'.format(
        s=call_stack()[:-len('debug.')],m=message))

logging.basicConfig(level=logging.DEBUG, format="%(message)s")

def bar():
    debug("test")

def foo():
    bar()

def monkey():
    foo()

monkey()

这样就能得到

monkey.foo.bar -- DEBUG: test
1

昨天我研究了这个模式,发现我想要的在Python语言中并不合理。毕竟,Python是一种解释型语言,这意味着总是有一个文件存在于一个已知的路径下,并且可以引用到某一行。这样的做法在像C#、Java、C或C++这样的编译型语言中是有意义的,因为在这些语言中,你有命名空间、对象和方法,但没有文件/路径和行号的好处。

故事的道理

当有人告诉你你所问的问题没有意义时,不要急着否定他们。花点时间仔细审视一下你最初的请求和你的观点,然后再去追求一个可能在你的特定情况下或背景下并不合理的答案。

撰写回答