Python 日志格式:让一条记录说明发生了什么

程序出了问题时,一句“执行失败”通常不够。我们还需要知道何时发生、来自哪个模块、严重程度如何,以及对应的异常。日志格式的作用,就是把这些上下文稳定地放在同一条记录里。

一个清晰的起点

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(name)s | "
           "%(filename)s:%(lineno)d | %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger(__name__)

def load_documents():
    logger.info("开始读取资料,文件数=%d", 3)

load_documents()

输出包含时间、等级、记录器名、源文件与行号、消息。实际时间和行号随运行环境而变化,不应把示例输出中的固定行号当成程序保证。

常用字段

字段 意义
%(asctime)s 格式化时间
%(levelname)s INFO、WARNING、ERROR 等等级名称
%(name)s 记录器名称
%(message)s 完成参数替换后的消息
%(filename)s 发出日志的源文件名
%(pathname)s 源文件完整路径
%(module)s 源模块名
%(funcName)s 发出日志的函数名
%(lineno)d 源代码行号
%(process)d 进程 ID
%(thread)d 线程 ID

这些字段来自 LogRecord;格式中的 %s 与 %d
分别表示字符串与整数形式。Python logging
文档

不是字段越多越好。单进程小脚本通常不必为每条日志增加进程和线程
ID;多服务请求排查时,能够关联请求的标识往往更有价值。

basicConfig 为什么有时不生效

如果根记录器已经配置了处理器,basicConfig()
通常不会再次配置。因此在
Notebook、测试框架或其他库先配置日志之后,重复调用可能看不到变化。

应用一般在入口统一配置,各模块使用
logging.getLogger(__name__)
获取记录器。不要在每个模块里重复初始化全局日志。

force=True
可以重配根记录器,但会移除已有处理器。只有当应用明确拥有日志配置权时才应使用,库代码不应擅自覆盖宿主配置。

错误消息与异常堆栈

try:
    int("not-a-number")
except ValueError:
    logger.exception("配置中的数量字段无法解析")

logger.exception
应在异常处理块中使用,它会同时记录异常堆栈。相比只记录
str(exc),堆栈更容易定位调用路径。

日常消息可以写成 logger.info("文件数=%d", count),让
logging
在需要输出时完成字符串格式化。若参数本身由昂贵函数计算,这种写法并不会省掉函数求值,仍需另行判断日志等级。

让日志服务于排查

RAG
流程可以记录阶段、耗时、召回条数、知识库版本和请求标识。默认不必把完整用户问题、检索原文或凭据写进日志;记录摘要信息通常就足以判断处理链路是否异常。

格式解决“怎样读”,日志等级与采样解决“输出多少”,文件轮转和保留策略解决“保存多久”。三者配合,日志才不会从排障工具变成新的负担。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇