Python 日志系统设计与实践
技术百科
冰川箭仙
发布时间:2026-01-26
浏览: 次 logging.getLogger() 总返回同一实例,因模块用字典缓存logger名称;子logger自动继承父级handler和level,但propagate=True易致重复输出;多进程需避免共用FileHandler,推荐独立文件或QueueHandler;JSON日志需预处理字段并确保换行。
为什么 logging.getLogger() 返回的总是同一个实例
因为 Python 的 logging 模块内部用字典缓存了所有已创建的 logger 实例,键是 logger 名称。调用 logging.getLogger("a.b.c") 时,如果该名称已存在,就直接返回缓存对象;不存在才新建并缓存。
这导致两个常见误解:
- 以为多次调用
getLogger()会新建 logger —— 实际不会,配置需在首次获取后统一设置 - 子 logger(如
getLogger("a.b.c"))自动继承父 logger("a"或"a.b")的 handler 和 level,但propagate=True是默认行为,容易造成日志重复输出 - 模块内习惯写
logger = logging.getLogger(__name__),这是推荐做法,能天然形成层级结构,但必须确保根 logger 或上级 logger 已配置 handler,否则日志“发出去却没人收”
Handler 写入失败却不报错的典型原因
FileHandler 或 RotatingFileHandler 在目录不存在、权限不足、磁盘满等情况下,通常静默失败——日志不写入,也不抛异常,只在内部记录一次 warning 到 logging.lastResort(一个默认的 StreamHandler),而这个 lastResort 默认只在 root logger 未配置 handler 时启用,且输出到 stderr,很容易被忽略。
排查建议:
- 手动检查目标路径是否存在且可写:
os.path.isdir(os.path.dirname(log_path)) and os.access(os.path.dirname(log_path), os.W_OK) - 显式捕获 handler 初始化异常:
try: handler = RotatingFileHandler(...) except OSError as e: print(f"Handler init failed: {e}") - 避免依赖
lastResort,始终为 root 或关键 logger 显式添加至少一个可用 handler(如StreamHandler(sys.stderr))
如何安全地在多进程环境下写同一个日志文件
标准 FileHandler 不支持多进程并发写入,直接共用会导致内容错乱或丢失。Python 官方不提供跨进程安全的内置 handler,必须绕开或封装。
可行方案:
- 每个进程写独立文件(推荐):用进程 ID 或名称区分,如
app.log.12345,后续用 logrotate 或脚本合并分析 - 用
QueueHandler+ 单独日志进程:主进程将日志 record 发送到multiprocessing.Queue,由唯一消费者进程用FileHandler写入,避免竞争 - 改用支持原子写入的外部服务:如
syslog(SysLogHandler)、redis、或fluentd等,把并发压力交给中间件 - 绝对不要在多进程里直接共享
FileHandler实例,即使加锁也难保底层 OS write 调用的原子性
JSON 格式日志输出的坑与务实做法
想让每行日志是合法 JSON(便于 ELK、Loki 解析),不能只靠重写 Formatter.format() —— 因为 LogRecord 中的 exc_info、stack_info 是元组或字符串,直接 json.dumps() 会失败;且默认字段(如 asctime、funcName)可能含不可序列化对象或特殊字符。
稳妥方式:
- 继承
logging.Formatter,在format()中预处理:把record.exc_text(已格式化的异常字符串)加入 dict,跳过record.exc_info原始元组 - 对字段做白名单过滤和类型规整:例如
str(getattr(record, 'asctime', ''))、record.levelname or 'UNKNOWN' - 避免在 format 中调用
traceback.format_exc()等耗时操
作,应在
makeRecord()或日志产生侧完成 - 若用第三方库(如
python-json-logger),注意其默认不处理extra中的嵌套 dict 或 datetime 对象,仍需自定义json_default函数
最易被忽略的一点:日志行末尾必须有换行符,否则多条 JSON 日志会粘连成非法格式——json.dumps(...)+ '\n' 是必须的。
# ai
# python
# app
# redis
# js
# json
# format
# 并发
# 对象
# stream
# 字符串
# access
# 为什么
# gate
# red
# 封装
# 继承
# try
# 中间件
# elk
# print
# Logging
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Win11右键反应慢怎么办 Win11优化右键菜单
- php会话怎么开启_session_start函数
- 作用域操作符会影响性能吗_php静态调用性能分析【
- c++怎么设置线程优先级与cpu亲和性_c++ 多
- Python日志系统设计与实现_高可观测性架构实战
- 如何使用正则表达式批量替换重复的星号-短横模式为固
- c++中如何使用auto关键字_c++11类型推导
- Win11如何设置ipv6 Win11开启IPv6
- 电脑的“网络和共享中心”去哪了_Windows 1
- windows系统如何安装cab更新补丁_wind
- c++怎么编写动态链接库dll_c++ __dec
- Win11怎么查看激活状态_查询Windows 1
- PHP的Workerman对架构扩展有啥帮助_应用
- Win11怎么设置虚拟内存最佳大小_Windows
- Windows10电脑怎么设置自动连接WiFi_W
- LINUX如何查看文件类型_Linux中file命
- Win11如何添加/删除输入法 Win11切换中英
- Win11怎么关闭定位服务 Win11禁止应用获取
- 如何优化Golang Web性能_Golang H
- Windows10如何删除恢复分区_Win10 D
- Go 中实现 Python urllib.quot
- Mac如何使用听写功能_Mac语音输入打字【效率技
- php能控制zigbee模块吗_php通过串口与c
- phpstudy本地环境mysql忘记密码_重置m
- Win11时间怎么同步到原子钟 Win11高精度时
- Win11时间格式怎么改成12小时制 Win11时
- Win11怎么关闭任务栏小组件_Windows11
- php怎么下载安装后测试是否成功_简单脚本验证方法
- Win11如何设置计划任务 Win11定时执行程序
- Django 密码修改后会话失效的解决方案
- Windows10电脑怎么设置防火墙出站规则_Wi
- MAC如何隐藏文件夹及文件_MAC终端命令隐藏与第
- Win11怎么设置ip地址_Windows 11手
- Win11局域网共享怎么设置 Win11文件夹网络
- Win11怎么设置默认终端应用_Windows11
- Go语言中slice追加操作的底层共享机制解析
- Windows10系统怎么查看硬盘健康_Win10
- 如何使用Golang实现负载均衡_分发请求到多个服
- 如何在 Go 中创建包含 map 的 slice(
- c++ nullptr与NULL区别_c++11空
- Win11如何设置环境变量 Win11添加和修改系
- Win11怎么关闭键盘按键音_Win11禁用打字声
- Win11怎么关闭通知中心_Windows11系统
- Win11如何更改用户账户文件夹名称 Win11修
- Win11怎么更改任务栏位置_修改注册表将Win1
- Windows执行文件被SmartScreen拦截
- Win11怎么关闭触摸屏_禁用Win11笔记本触摸
- Win10系统映像怎么恢复 Win10使用系统映像
- PythonPandas数据分析项目教程_时间序列
- 如何解决Windows字体显示模糊的问题?(Cle


QQ客服