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 logging
<p>formatter = logging.Formatter(
'[%(asctime)s] %(levelname)-8s %(filename)s:%(lineno)d %(funcName)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)</p><p>handler = logging.StreamHandler()
handler.setFormatter(formatter)</p><p>logger = logging.getLogger(<strong>name</strong>)
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)</p><p>def example():
logger.debug("这条日志会显示函数名和行号")</p><p>example()
</p>输出类似:[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 logging
<p>def setup_logger(name=<strong>name</strong>, level=logging.DEBUG):
logger = logging.getLogger(name)
logger.setLevel(level)</p><pre class="brush:php;toolbar:false;"># 避免重复添加 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会显示为<module></module>或<lambda></lambda> -
lineno是日志语句本身所在行,不是函数定义行 - 多线程下信息仍准确,logging 内部基于当前线程栈帧获取
- 性能影响极小,格式化只在实际输出时发生(除非设置了
logging.basicConfig(level=logging.DEBUG)且日志未被过滤)










