RKVision开发日志(一)
一、前言
今天开始正式的视觉框架开发,第一步是构建一个多进程的日志系统。日志系统是大型项目的基石,记录每个模块的正常运行,防止程序突然异常退出却无法定位问题点。
二、Python日志模块
日志是在Python自2.7版本就存在的官方模块之一。官方文档地址:日志指南 — Python 3.14.2 文档
1. 日志分级
简单的理解日志,就是一个高级的print。log存在几种级别:
| 级别 | 何时使用 |
|---|---|
DEBUG |
细节信息,仅当诊断问题时适用。 |
INFO |
确认程序按预期运行。 |
WARNING |
表明有已经或即将发生的意外(例如:磁盘空间不足)。程序仍按预期进行。 |
ERROR |
由于严重的问题,程序的某些功能已经不能正常执行 |
CRITICAL |
严重的错误,表明程序已不能继续执行 |
| 在程序调用中,可以用以下的方式写日志: |
1 | |
输出:
1 | |
2. 日志定位
而在实际开发中,往往是多文件的结构,日志中最好包含文件名。因此可以用以下方式实现:
1 | |
输出:
1 | |
上述程序中通过设置logger.basicConfig()的默认配置实现了文件名的打印,减少我们排查代码的负担。logger.basicConfig()是一个日志配置器,功能异常强大。
3. 日志保存
当我们的程序已经稳定的部署在生产端后,突然出现了问题, 需要我们排查,显然不可能去控制台一点点翻。所以logging贴心的提出了在打印控制台的同时保存到文件中,日志信息一式两份。具体的写法参考官方文档:
1 | |
可以看到他是保存到了example.log文件中。
4. 其他
此外还有一些无关紧要的功能,比如说更改日志内容,记录时间戳信息;多模块日志有选择性的聚合等,功能很全,这里就不做过多介绍了。
三、多进程日志
众所周知,进程是CPU调度的基本单位。程序从单个硬件单元运行扩展至多个硬件单元并行,可以充分利用硬件,提高程序运行效率。
而Python语言中,由于全局解释器锁(GIL - global interpreter lock)的存在,同一时刻只有一个线程可以执行 Python 代码。这就导致多线程程序并没有在硬件层面并行运行,限制了程序的性能。
与多线程不同的是,多进程绕过了GIL锁,能充分利用多个处理器,但进程之间相对独立,并不会共享变量,这意味着一个多进程程序需要依赖进程间通信(IPC - Inter Process Communication)的方式进行。
这和日志系统又有什么关系呢?如果每个进程都有一个单独的log,并不利于程序员的调试;而如果写入一个文件,还要考虑多进程访问的冲突问题;而日志聚合到一个主进程,则要考虑进程间通信的问题。
我最开始是想手搓几个通信通道聚合到主进程中然后记录日志的,但是我是一个比较懒的人,在查资料的时候发现了一个支持多进程日志的模块Delgan/loguru: Python logging made (stupidly) simple,其内部使用multiprocessing.Queue进行通信。就像多线程开发一样,只需from loguru import logger就可以像引入全局变量一样打日志。为了简化开发,我们就先采用这个库进行。如果以后有性能问题也可以再重新开发日志系统。
三、多进程架构
个人理解的多进程程序中,会存在多个if __name__ == "__main__":,并创建一个管理器去寻找单个模块的main入口,然后程序从这里运行。所以在测试多进程的日志系统之前,我们要先搭建一个多进程的框架。
进程有几种启动方法,如spawn, fork, forkserver等,参见multiprocessing — 基于进程的并行 — Python 3.14.2 文档,为了多平台运行和调试的方便,直接使用spawn方法新建进程即可。
为了防止主进程空转,在充分查阅了相关资料和参考程序后决定将控制进程作为主进程进行。