Python logging可通过%(funcName)s和%(lineno)d自动添加函数名与行号,需在Formatter中配置格式字符串,封装日志函数时应设stacklevel=2以准确定位调用位置。

在 Python 的 logging 模块中,可以通过配置日志格式自动添加当前函数名和行号,无需每次手动传入。关键在于使用内置的格式化占位符 %(funcName)s 和 %(lineno)d,并确保日志记录时能正确捕获调用位置(默认即可,无需额外设置)。
配置 logging 格式字符串
在初始化 logger 时,通过 Formatter 指定包含函数名和行号的格式:
-
%(funcName)s:自动提取日志调用所在函数的名称(注意:不是被调用函数,而是写logger.info(...)那一行所在的函数) -
%(lineno)d:自动提取该日志语句所在的源代码行号 - 其他常用占位符如
%(filename)s、%(levelname)s、%(asctime)s可一并加入,提升可读性
示例:
import loggingformatter = logging.Formatter( '[%(asctime)s] %(levelname)-8s %(filename)s:%(lineno)d %(funcName)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S' )
handler = logging.StreamHandler() handler.setFormatter(formatter)
logger = logging.getLogger(name) logger.setLevel(logging.DEBUG) logger.addHandler(handler)
def example(): logger.debug("这条日志会显示函数名和行号")
example()
输出类似:[2024-05-20 10:30:45] DEBUG test.py:12 example - 这条日志会显示函数名和行号
立即学习“Python免费学习笔记(深入)”;
确保 logger 记录的是调用点而非内部位置
logging 默认使用 stacklevel=1,即向上查 1 层栈帧,正好定位到你写 logger.xxx() 的那行。一般无需改动。但如果你封装了日志函数(比如写了个 log_debug(msg)),则需显式设 stacklevel=2,否则函数名会显示为 log_debug,行号也会是封装函数内的行号。
- 封装日志时,在调用
logger.xxx()时加参数:extra={...}, stacklevel=2 - 或直接用
logger.log(level, msg, stacklevel=2)
推荐的最小实用配置(支持文件+控制台)
兼顾开发调试与简单部署:
import loggingdef setup_logger(name=name, level=logging.DEBUG): logger = logging.getLogger(name) logger.setLevel(level)
# 避免重复添加 handler if not logger.handlers: formatter = logging.Formatter( '%(asctime)s | %(levelname)-8s | %(funcName)s:%(lineno)d | %(message)s', datefmt='%H:%M:%S' ) ch = logging.StreamHandler() ch.setFormatter(formatter) logger.addHandler(ch) # 可选:添加文件 handler # fh = logging.FileHandler('app.log') # fh.setFormatter(formatter) # logger.addHandler(fh) return loggerlogger = setup_logger()
注意事项
- 函数名取自调用日志语句的函数,不是被调用函数;若在 lambda 或顶层模块中调用,
funcName会显示为或 -
lineno是日志语句本身所在行,不是函数定义行 - 多线程下信息仍准确,logging 内部基于当前线程栈帧获取
- 性能影响极小,格式化只在实际输出时发生(除非设置了
logging.basicConfig(level=logging.DEBUG)且日志未被过滤)










