289 lines
8.0 KiB
Markdown
289 lines
8.0 KiB
Markdown
# MyLog 日志系统技术文档
|
||
|
||
本文档描述了一个基于 Python 的轻量级日志管理系统 `MyLog` 与 `LogMan`,用于实现灵活的日志记录和分类控制。该系统支持多类别日志输出、动态注册日志处理器,并结合全局数据管理模块 `public_data` 实现单例模式的日志管理。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
- [概述](#概述)
|
||
- [依赖模块](#依赖模块)
|
||
- [核心常量](#核心常量)
|
||
- [类说明](#类说明)
|
||
- [`MyLog`](#mylog)
|
||
- [`LogMan`](#logman)
|
||
- [函数说明](#函数说明)
|
||
- [`mylog()`](#mylogs-catelogappinfo)
|
||
- [使用示例](#使用示例)
|
||
- [注意事项](#注意事项)
|
||
|
||
---
|
||
|
||
## 概述
|
||
|
||
本日志系统提供以下功能:
|
||
|
||
- 支持按**日志级别/类别**(如 `SYSError`, `APPInfo`, `DEBUG1` 等)进行过滤。
|
||
- 可动态添加、删除或修改日志处理器(logger)。
|
||
- 使用统一入口函数 `mylog()` 记录消息。
|
||
- 日志自动写入文件 `./log/my.log`。
|
||
- 基于 `public_data` 全局变量存储日志管理器实例,避免重复初始化。
|
||
|
||
---
|
||
|
||
## 依赖模块
|
||
|
||
| 模块名 | 用途说明 |
|
||
|----------------|----------|
|
||
| `os` | 路径拼接、文件操作 |
|
||
| `datetime` | 获取当前时间用于日志时间戳 |
|
||
| `PublicData` | 全局数据容器,保存共享对象(如 `ProgramPath`, `mylog` 实例) |
|
||
| `folderUtils.mkdir` | 自动创建目录(确保日志目录存在) |
|
||
|
||
> ⚠️ 注意:`PublicData` 和 `folderUtils` 是项目自定义模块,需保证其可用性。
|
||
|
||
---
|
||
|
||
## 核心常量
|
||
|
||
```python
|
||
AllCatelogs = [
|
||
'SYSError',
|
||
'SYSWarn',
|
||
'APPError',
|
||
'APPWarn',
|
||
'APPInfo',
|
||
'DEBUG1',
|
||
'DEBUG2',
|
||
'DEBUG3',
|
||
'DEBUG4',
|
||
'DEBUG5',
|
||
]
|
||
```
|
||
|
||
- **作用**:预定义所有允许的日志类别(日志等级/标签)。
|
||
- **说明**:
|
||
- 分为系统级 (`SYS*`) 和应用级 (`APP*`)。
|
||
- `DEBUG1` 到 `DEBUG5` 可用于不同级别的调试信息输出。
|
||
- 所有日志操作必须使用这些类别之一,否则将被忽略。
|
||
|
||
---
|
||
|
||
## 类说明
|
||
|
||
### `MyLog`
|
||
|
||
一个简单的日志写入类,负责将日志消息追加到指定路径的文件中。
|
||
|
||
#### 构造函数
|
||
|
||
```python
|
||
def __init__(self, path)
|
||
```
|
||
|
||
- **参数**:
|
||
- `path` (str): 日志根目录路径,默认为 `'.'`(当前目录)
|
||
|
||
#### 方法
|
||
|
||
##### `setLogPath(path='.')`
|
||
|
||
设置日志存储路径,并自动创建 `log` 子目录。
|
||
|
||
- **参数**:
|
||
- `path` (str): 新的日志根目录路径
|
||
- **行为**:
|
||
- 将路径保存至 `self.myLogPath`
|
||
- 创建 `{path}/log/` 目录(若不存在)
|
||
- **注意**:此方法存在逻辑错误 —— 缩进问题导致 `logp` 和 `mkdir(logp)` 不在方法体内(见[注意事项](#注意事项))
|
||
|
||
##### `__call__(msg='')`
|
||
|
||
使对象可调用,用于写入一条日志。
|
||
|
||
- **参数**:
|
||
- `msg` (str): 要写入的消息
|
||
- **行为**:
|
||
1. 构造日志文件路径:`{self.myLogPath}/log/my.log`
|
||
2. 以追加模式打开文件
|
||
3. 写入格式化的时间戳 + 消息
|
||
4. 关闭文件
|
||
- **时间格式**:`YYYY-MM-DD HH:MM:SS <message>`
|
||
|
||
> 示例输出:
|
||
> ```
|
||
> 2025-04-05 10:30:45 User login successful
|
||
> ```
|
||
|
||
---
|
||
|
||
### `LogMan`(日志管理器)
|
||
|
||
用于集中管理和分发日志消息到多个日志处理器(logger),支持基于类别的条件触发。
|
||
|
||
#### 构造函数
|
||
|
||
```python
|
||
def __init__()
|
||
```
|
||
|
||
- 初始化两个属性:
|
||
- `self.logers`: 字典,存储注册的日志处理器
|
||
- `self.catelogs`: 当前允许的日志类别列表(初始为 `AllCatelogs`)
|
||
|
||
#### 方法
|
||
|
||
##### `addCatelog(catelog)`
|
||
|
||
添加新的日志类别到允许列表。
|
||
|
||
- **参数**:
|
||
- `catelog` (str): 要添加的类别名称
|
||
- **行为**:
|
||
- 若类别不在 `self.catelogs` 中,则追加
|
||
|
||
##### `addLoger(name, func, catelog)`
|
||
|
||
注册一个新的日志处理器。
|
||
|
||
- **参数**:
|
||
- `name` (str): 处理器唯一标识名
|
||
- `func` (callable): 可调用对象(如 `MyLog` 实例),用于实际写日志
|
||
- `catelog` (str 或 list of str): 此处理器监听的日志类别
|
||
- **行为**:
|
||
- 若 `catelog` 非列表,则转换为列表
|
||
- 过滤掉不在 `self.catelogs` 中的非法类别
|
||
- 构建日志器字典并存入 `self.logers[name]`
|
||
|
||
##### `delLoger(name)`
|
||
|
||
删除已注册的日志处理器。
|
||
|
||
- **参数**:
|
||
- `name` (str): 要删除的日志处理器名称
|
||
- **行为**:
|
||
- 若存在则从 `self.logers` 中移除
|
||
|
||
##### `setCatelog(name, catelog)`
|
||
|
||
更改某个日志处理器所监听的类别。
|
||
|
||
- **参数**:
|
||
- `name` (str): 已注册的日志处理器名称
|
||
- `catelog` (str 或 list of str): 新的监听类别
|
||
- **行为**:
|
||
- 类别合法性检查与过滤后更新配置
|
||
|
||
##### `__call__(msg='', catelog='APPInfo')`
|
||
|
||
使对象可调用,作为日志分发中心。
|
||
|
||
- **参数**:
|
||
- `msg` (str): 日志内容
|
||
- `catelog` (str): 日志所属类别(默认 `'APPInfo'`)
|
||
- **行为**:
|
||
- 遍历所有注册的日志处理器
|
||
- 如果该处理器监听了当前 `catelog`,则调用其 `func(msg)`
|
||
- **用途**:实现“发布-订阅”式日志分发机制
|
||
|
||
---
|
||
|
||
## 函数说明
|
||
|
||
### `mylog(s, catelog='APPInfo')`
|
||
|
||
全局日志接口函数,用户应通过此函数记录日志。
|
||
|
||
#### 参数
|
||
|
||
- `s` (str): 日志消息
|
||
- `catelog` (str): 日志类别(必须是 `AllCatelogs` 中的一项)
|
||
|
||
#### 返回值
|
||
|
||
- 返回 `LogMan.__call__()` 的结果(无显式返回值)
|
||
|
||
#### 行为流程
|
||
|
||
1. 尝试从 `public_data` 获取已存在的 `logman` 实例
|
||
2. 若未找到:
|
||
- 获取程序运行路径 `ProgramPath`(也来自 `public_data`)
|
||
- 若路径不存在,抛出异常
|
||
- 创建 `MyLog` 实例,指向该路径
|
||
- 创建 `LogMan` 实例
|
||
- 注册名为 `'mylog'` 的日志处理器,绑定 `MyLog` 实例,并监听所有类别
|
||
- 将 `logman` 实例存回 `public_data`,实现单例
|
||
3. 调用 `logman(s, catelog)` 分发日志
|
||
|
||
> ✅ 优点:首次调用自动初始化,后续调用复用实例,线程不安全但适用于单线程场景。
|
||
|
||
---
|
||
|
||
## 使用示例
|
||
|
||
```python
|
||
# 记录一条普通应用信息
|
||
mylog("程序启动完成")
|
||
|
||
# 记录警告信息
|
||
mylog("磁盘空间不足", catelog='APPWarn')
|
||
|
||
# 记录系统错误
|
||
mylog("数据库连接失败", catelog='SYSError')
|
||
|
||
# 记录调试信息
|
||
mylog("变量x的值为: 123", catelog='DEBUG2')
|
||
```
|
||
|
||
日志将写入文件:`./log/my.log`,内容类似:
|
||
|
||
```
|
||
2025-04-05 10:30:45 程序启动完成
|
||
2025-04-05 10:31:12 磁盘空间不足
|
||
2025-04-05 10:31:15 数据库连接失败
|
||
2025-04-05 10:31:16 变量x的值为: 123
|
||
```
|
||
|
||
---
|
||
|
||
## 注意事项
|
||
|
||
1. ❗ **缩进错误**:
|
||
```python
|
||
def setLogPath(self, path='.'):
|
||
self.myLogPath = path
|
||
logp = os.path.join(path, 'log')
|
||
mkdir(logp)
|
||
```
|
||
上述代码中 `logp=...` 和 `mkdir(...)` 不在方法体内部!这会导致语法错误或运行时错误。
|
||
✅ 正确写法应为:
|
||
```python
|
||
def setLogPath(self, path='.'):
|
||
self.myLogPath = path
|
||
logp = os.path.join(path, 'log')
|
||
mkdir(logp)
|
||
```
|
||
|
||
2. 📁 **目录权限**:
|
||
- 确保程序对目标路径有写权限,否则 `open()` 将失败。
|
||
|
||
3. 🔐 **线程安全**:
|
||
- 当前实现未考虑多线程并发写日志的问题,可能造成日志混乱。
|
||
- 建议在高并发环境下增加文件锁或改用标准库 `logging` 模块。
|
||
|
||
4. 💾 **性能考量**:
|
||
- 每次写日志都打开/关闭文件,频繁 I/O 可能影响性能。
|
||
- 可优化为缓存文件句柄或异步写入。
|
||
|
||
5. 🔧 **扩展建议**:
|
||
- 支持按日期分割日志文件(如 `my_2025-04-05.log`)
|
||
- 添加日志级别阈值控制(如仅输出 `>= APPWarn` 的日志)
|
||
- 替换为 Python 标准 `logging` 模块以获得更强大功能
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
本日志系统虽然简单,但具备基本的分类、注册、分发能力,适合小型项目快速集成。通过 `public_data` 实现了全局单例管理,降低了使用复杂度。但在健壮性、性能和可维护性方面仍有提升空间。
|
||
|
||
建议在生产环境中替换为 Python 内置的 `logging` 模块,或在此基础上完善异常处理与资源管理机制。 |