Python 问题定位的日志分析方法

1次阅读

python进程静默退出主因是未捕获信号(如sigsegv)或c扩展崩溃;Logging.basicconfig仅首次生效,需置于getlogger前;多进程需隔离日志handler,避免权限、磁盘或竞态问题。

Python 问题定位的日志分析方法

日志里找不到 Traceback,但程序就是挂了

Python 进程静默退出,stdoutstderr 都没留下痕迹,大概率是未捕获的信号(比如 SIGSEGV)或 C 扩展崩溃。标准日志模块根本收不到这类事件

实操建议:

立即学习Python免费学习笔记(深入)”;

  • strace -f -e trace=signal,clone,execve python your_script.py 观察是否收到致命信号
  • 检查是否启用了 faulthandler.enable() —— 它能在 SIGSEGV/SIGFPE 时强制 dump Python 调用
  • 若用了多进程,确认子进程是否调用了 logging.basicConfig();默认情况下子进程不继承父进程的日志配置,print() 可能被缓冲且未刷出

logging.basicConfig() 不生效的常见原因

写了 basicConfig 却看不到日志,不是代码写错了,而是它只在首次调用 logging.getLogger() 前有效。一旦 logger 实例化过,再调 basicConfig 就完全被忽略。

实操建议:

立即学习Python免费学习笔记(深入)”;

  • basicConfig 放在所有 import logging 之后、任何 getLogger() 之前 —— 最好是脚本最顶部
  • 如果用的是第三方库(如 requestsurllib3),它们可能已提前触发了 root logger 初始化;此时需显式获取并配置对应 logger:logging.getLogger("urllib3").setLevel(logging.DEBUG)
  • 注意 level 参数:默认是 WARNINGINFODEBUG 日志不会输出,别只改格式不调等级

线上环境日志中看不到真实行号和函数名

本地跑得好好的,一上生产,%(funcName)s 变成 <module></module>%(lineno)d 总是固定值。通常是日志被封装线程/协程上下文,或者使用了不保留栈帧的异步日志方案。

实操建议:

立即学习Python免费学习笔记(深入)”;

  • 确认没误用 Threading.Thread(target=...) 启动裸函数 —— 这种方式会丢失原始调用栈;改用 functools.partial闭包包装
  • 异步场景(如 asyncio)下,logging 默认不感知协程上下文;需配合 contextvars 手动注入 request_id 等字段,而非依赖 %(funcName)s
  • 检查是否启用了 sys.settrace 或某些 APM 工具(如 dd-trace-py),它们可能干扰帧对象提取;可临时禁用验证

RotatingFileHandler 后日志突然变少甚至中断

看起来配置没问题,但运行几小时后日志停止写入,或者轮转后新文件为空。根本原因常是权限、磁盘空间,或 handler 在多进程下未正确隔离。

实操建议:

立即学习Python免费学习笔记(深入)”;

  • 确保日志目录对运行用户有 w+ x 权限(x 是进入目录必需的);ls -ld /var/log/myapp 比猜更可靠
  • 轮转前先检查磁盘剩余空间:shutil.disk_usage(log_dir).free,避免因 ENOSPC 导致 handler 内部静默失败
  • 多进程场景下,RotatingFileHandler 不是进程安全的 —— 不要用多个进程共用同一个 handler 实例;改用 QueueHandler + 单独日志进程,或直接用 TimedRotatingFileHandler(它对轮转时间点的处理更鲁棒)

日志分析真正的难点不在格式或级别,而在于“谁在什么时候、以什么身份、经过哪条路径”触发了某条日志 —— 这需要主动埋点、上下文透传和进程模型理解,不是加一行 basicConfig 就能解决的。

text=ZqhQzanResources