apppublic/aidocs/log.md
2025-10-05 11:23:33 +08:00

223 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 技术文档Python 日志记录模块
```markdown
# MyLogger 模块技术文档
## 概述
本模块提供一个轻量级、线程安全(基于单例模式)的日志记录工具,支持多级别日志输出,并可将日志写入文件或标准输出。该模块通过 `inspect` 模块自动获取调用上下文信息(如文件名、行号),并结合时间戳生成结构化日志。
---
## 依赖项
- `sys`: 用于访问标准输出流。
- `codecs`: 提供带编码支持的文件读写功能UTF-8
- `traceback.format_exc`: 用于捕获和格式化异常堆栈信息。
- `appPublic.timeUtils.timestampstr`: 返回当前时间的时间戳字符串(格式依赖具体实现)。
- `appPublic.Singleton.SingletonDecorator`: 单例装饰器,确保 `MyLogger` 类为单例模式。
- `inspect`: 获取运行时调用栈信息,用于提取调用者文件名和行号。
> ⚠️ 注意:`appPublic.*` 是自定义公共库,请确保其已正确安装并可导入。
---
## 核心类:`MyLogger`
### 装饰器
```python
@SingletonDecorator
class MyLogger:
```
使用 `SingletonDecorator` 确保每个 `name` 对应唯一的 `MyLogger` 实例(注意:当前实现中未按 name 区分实例,可能存在设计缺陷 —— 见【注意事项】)。
---
### 属性说明
| 属性 | 类型 | 描述 |
|------|------|------|
| `levels` | `dict` | 定义日志级别及其优先级数值(数字越大,优先级越高)。 |
| `formater` | `str` | 日志输出格式模板,兼容 `%` 格式化语法。 |
#### 日志级别说明(按优先级从高到低)
| 级别名称 | 数值 | 用途 |
|---------|-----|------|
| `clientinfo` | 7 | 客户端相关信息,最高优先级 |
| `info` | 6 | 常规信息提示 |
| `debug` | 5 | 调试信息 |
| `warning` | 4 | 警告信息 |
| `error` | 3 | 错误信息 |
| `exception` | 2 | 异常堆栈信息 |
| `critical` | 1 | 致命错误,最低优先级 |
> 输出规则:仅当日志条目的级别 ≤ 当前 logger 的 `level` 时才会被记录。
---
### 构造函数:`__init__(self, name, levelname='debug', logfile=None)`
#### 参数
| 参数 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `name` | `str` | 必填 | 日志记录器名称,用于标识来源 |
| `levelname` | `str` | `'debug'` | 初始日志级别,决定哪些消息会被输出 |
| `logfile` | `str or None` | `None` | 日志文件路径;若为 `None`,则输出到 `sys.stdout` |
#### 初始化行为
- 设置 `self.name`, `self.levelname`, `self.level`(对应数值)
- 设置 `self.logfile` 和初始 `self.logger = None`
---
### 方法说明
#### `open_logger(self)`
打开日志目标:
- 若指定了 `logfile`,以追加模式 (`a`) 打开文件,使用 UTF-8 编码。
- 否则,使用 `sys.stdout` 作为输出流。
> 使用 `codecs.open` 确保 Unicode 写入安全。
#### `close_logger(self)`
关闭已打开的日志文件句柄(如果存在),并将 `self.logger` 设为 `None`
> ❗ 存在 bug最后一行重复赋值 `self.logger = None` 多余且无意义。
#### `log(self, levelname, message, frame_info)`
核心日志写入方法。
##### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `levelname` | `str` | 要记录的日志级别 |
| `message` | `str` | 日志内容 |
| `frame_info` | `frame object` | 调用栈帧对象,来自 `inspect.currentframe()` |
##### 行为流程
1. 获取调用者的栈帧 → 提取 `filename``lineno`
2. 查询 `levelname` 对应的等级值
3. 如果该等级高于当前 logger 允许的最高等级(即 `level > self.level`),则跳过输出
4. 构造日志数据字典 `data`
5. 调用 `open_logger()` 打开输出
6. 使用 `formater % data` 格式化日志字符串
7. 写入并刷新缓冲区
8. 调用 `close_logger()` 关闭资源
> ✅ 特点:每次写日志都临时打开/关闭文件,适合低频日志场景,避免长期占用句柄。
---
## 辅助函数(全局接口)
提供简洁的日志调用方式,封装了 `MyLogger` 的创建与调用过程。
| 函数 | 等效操作 | 示例 |
|------|----------|-------|
| `clientinfo(message)` | `logger.log('clientinfo', message, ...)` | `clientinfo("用户登录")` |
| `info(message)` | `logger.log('info', message, ...)` | `info("任务开始执行")` |
| `debug(message)` | `logger.log('debug', message, ...)` | `debug("变量x值为: " + str(x))` |
| `warning(message)` | `logger.log('warning', message, ...)` | `warning("配置项缺失")` |
| `error(message)` | `logger.log('error', message, ...)` | `error("数据库连接失败")` |
| `critical(message)` | `logger.log('critical', message, ...)` | `critical("系统即将退出")` |
| `exception(message)` | 记录异常堆栈 + 自定义消息 | `exception("处理请求出错")` |
> 📌 `exception()` 特殊处理:自动附加 `traceback.format_exc()` 的完整堆栈跟踪。
---
## 使用示例
```python
from mylogger import info, debug, error, exception
def main():
info("程序启动")
debug("正在加载配置...")
try:
1 / 0
except Exception:
exception("发生数学错误")
if __name__ == '__main__':
main()
```
输出示例(假设输出到控制台):
```
2025-04-05 10:20:30.123[Test][info][main.py:5]程序启动
2025-04-05 10:20:30.124[Test][debug][main.py:6]正在加载配置...
2025-04-05 10:20:30.125[exception][exception][main.py:8]发生数学错误
Traceback (most recent call last):
File "main.py", line 7, in main
1 / 0
ZeroDivisionError: division by zero
```
---
## 配置说明
### 日志格式化字符串
```python
'%(timestamp)s[%(name)s][%(levelname)s][%(filename)s:%(lineno)s]%(message)s\n'
```
字段解释:
| 字段 | 含义 |
|------|------|
| `%(timestamp)s` | 时间戳(由 `timestampstr()` 提供) |
| `%(name)s` | Logger 名称 |
| `%(levelname)s` | 日志级别名称 |
| `%(filename)s` | 发生日志的源文件名 |
| `%(lineno)s` | 源代码行号 |
| `%(message)s` | 用户提供的日志消息 |
---
## 注意事项与改进建议
### ⚠️ 已知问题
1. **单例逻辑可能失效**
- `SingletonDecorator` 可能对所有 `MyLogger` 创建返回同一实例,无法区分不同 `name`
- 建议改为基于 `name` 的单例缓存机制。
2. **`close_logger()` 中冗余赋值**
```python
self.logger.close()
self.logger = None
self.logger = None # 重复
```
第二个赋值无效,应删除。
3. **性能考量**
- 每次 `log()` 都打开/关闭文件,在高频日志场景下效率低下。
- 建议引入持久化文件句柄管理(如首次打开后保持打开状态)。
4. **缺乏线程安全性**
- 尽管是“单例”,但未加锁,多线程同时写日志可能导致混乱。
- 如需多线程支持,建议添加 `threading.Lock`。
5. **硬编码 logger 名称**
- 所有辅助函数中 `logger = MyLogger('Test')` 固定为 `'Test'`,不合理。
- 应允许传参或动态设置默认名称。
---
## 总结
本模块实现了基本的日志功能,具备以下优点:
✅ 结构清晰
✅ 支持自动定位调用位置
✅ 支持文件/控制台双输出
✅ 提供丰富的日志级别
✅ 易于使用的全局函数接口
适用于小型项目或嵌入式脚本环境。对于大型应用,建议升级至标准库 `logging` 模块或增强此模块的功能与健壮性。
```