Files
sunvpy-docs/docs/content/37-pei-zhi-ri-zhi-ji-lu-qi.md
2026-04-10 13:47:53 +08:00

536 lines
17 KiB
Markdown
Raw Permalink 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.
日志记录是脚本开发中不可或缺的调试和监控手段,它能帮助您跟踪脚本执行过程、记录错误信息、排查问题根源。本页面将指导您为 SunvStation 脚本配置一个功能完善的日志记录器,让您的脚本具备专业级的日志管理能力。
## 日志系统架构概览
SunvStation 的日志功能通过 **LogMixin** 混入类实现,这是 SSProcessManager 继承体系中的基础模块之一。日志系统采用**依赖注入**设计模式,允许您将自定义配置的日志记录器注入到 SSProcess 中,从而灵活控制日志的输出方式、格式和级别。
```mermaid
classDiagram
class Logger {
<<Python标准库>>
+debug(msg)
+info(msg)
+warning(msg)
+error(msg)
+critical(msg)
}
class LogAware {
<<抽象基类>>
+set_logger(Logger)*
+log_error_msg(str)*
}
class LogMixin {
+Logger logger
+set_logger(Logger)
+log_error_msg(str)
}
class SSProcessManager {
+Logger logger
+set_logger(Logger)
+log_error_msg(str)
}
LogAware <|-- LogMixin
LogMixin <|-- SSProcessManager
SSProcessManager ..> Logger : 使用
```
这个架构设计的核心优势在于**关注点分离**SSProcessManager 只负责调用日志接口,而日志的具体配置(输出到文件、控制台或远程服务器)完全由您控制,不会影响业务逻辑代码。
Sources: [log_mixin.py](ssprocess_mixins/log_mixin.py#L19-L42), [PySSProcess.py](PySSProcess.py#L34-L35)
## 配置日志记录器流程
配置日志记录器的过程分为四个关键步骤,下图清晰地展示了从创建到使用的完整流程:
```mermaid
flowchart TD
A[开始配置日志] --> B[创建Logger实例]
B --> C[配置Handler处理器]
C --> D[设置Formatter格式化器]
D --> E[配置日志级别]
E --> F[注入到SSProcess]
F --> G[开始使用日志功能]
C --> C1[文件处理器<br>FileHandler]
C --> C2[控制台处理器<br>StreamHandler]
C --> C3[综合配置<br>多处理器]
E --> E1[DEBUG]
E --> E2[INFO]
E --> E3[WARNING]
E --> E4[ERROR]
G --> G1[自动记录错误]
G --> G2[手动记录日志]
style F fill:#e1f5ff
style G fill:#fff4e1
```
理解这个流程后,我们将逐步详细解析每个环节的具体实现方法。
Sources: [log_mixin.py](ssprocess_mixins/log_mixin.py#L26-L42)
## 步骤一:创建 Logger 实例
首先需要创建一个 Python 标准库的 Logger 实例。建议使用脚本文件名作为 Logger 的名称,这样可以在日志输出中清晰地识别日志来源。
```python
import logging
# 创建 Logger 实例,使用脚本文件名作为标识符
logger = logging.getLogger('my_script')
logger.setLevel(logging.DEBUG) # 设置最低日志级别
```
### 日志级别说明
Python logging 模块提供了五个标准日志级别,按严重程度从低到高排列:
| 级别 | 数值 | 说明 | 使用场景 |
|-----|------|------|---------|
| DEBUG | 10 | 调试信息 | 详细的程序运行细节,仅在开发和调试时使用 |
| INFO | 20 | 一般信息 | 程序正常运行的关键步骤确认 |
| WARNING | 30 | 警告信息 | 程序可以继续运行但可能存在潜在问题 |
| ERROR | 40 | 错误信息 | 程序遇到错误但能够继续运行 |
| CRITICAL | 50 | 严重错误 | 严重错误导致程序可能无法继续运行 |
设置日志级别后,只有**等于或高于该级别**的日志消息才会被处理。例如,设置 `WARNING` 级别时,`DEBUG``INFO` 级别的消息将被忽略。
Sources: [Python logging 模块](https://docs.python.org/3/library/logging.html#logging-levels)
## 步骤二:配置 Handler 处理器
Handler 决定了日志消息的输出目的地。常用的 Handler 类型包括文件处理器和控制台处理器。您可以根据需求选择单一处理器或组合使用多个处理器。
### 文件处理器配置
将日志输出到文件是最常见的需求,特别适合长期运行的脚本和自动化任务。
```python
# 创建文件处理器,指定日志文件路径
file_handler = logging.FileHandler(
'script_log.log', # 日志文件路径
mode='a', # 追加模式('a'追加,'w'覆盖)
encoding='utf-8' # 文件编码
)
file_handler.setLevel(logging.INFO) # 文件处理器只记录 INFO 级别及以上
```
### 控制台处理器配置
控制台处理器将日志输出到标准输出流,适合开发调试阶段使用。
```python
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING) # 控制台只显示 WARNING 级别及以上
```
Sources: [log_mixin.py](ssprocess_mixins/log_mixin.py#L26-L42)
## 步骤三:设置 Formatter 格式化器
Formatter 定义了日志消息的输出格式,包括时间戳、日志级别、消息内容等信息。合理的格式能让日志更易于阅读和分析。
### 基本格式示例
```python
# 创建格式化器
formatter = logging.Formatter(
fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# 将格式化器应用到处理器
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
```
### 格式化参数说明
| 格式参数 | 说明 | 示例输出 |
|---------|------|---------|
| `%(asctime)s` | 日志记录时间 | 2024-01-15 14:30:25 |
| `%(name)s` | Logger 名称 | my_script |
| `%(levelname)s` | 日志级别名称 | ERROR |
| `%(message)s` | 日志消息内容 | 无法获取当前数据源 |
| `%(filename)s` | 文件名 | geo_edit_mixin.py |
| `%(lineno)d` | 行号 | 175 |
| `%(funcName)s` | 函数名 | createNewObjByCode |
### 完整格式示例(包含位置信息)
```python
# 包含文件名、行号和函数名的详细格式
detailed_formatter = logging.Formatter(
fmt='%(asctime)s [%(filename)s:%(lineno)d - %(funcName)s] %(levelname)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
file_handler.setFormatter(detailed_formatter)
```
这种详细格式在调试问题时特别有用,可以快速定位到产生日志的具体代码位置。
Sources: [log_mixin.py](ssprocess_mixins/log_mixin.py#L36-L42)
## 步骤四:组装并注入到 SSProcess
完成上述配置后,需要将处理器添加到 Logger然后将 Logger 注入到 SSProcess 中。
```python
from sunvpy import SSProcess
# 将处理器添加到 Logger
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 将配置好的 Logger 注入到 SSProcess
SSProcess.set_logger(logger)
# 验证配置成功
print("日志记录器配置完成!")
```
至此SSProcess 已经具备了完整的日志记录能力,所有内部调用 `log_error_msg()` 的错误消息都会按照您的配置输出到指定的目的地。
Sources: [PySSProcess.py](PySSProcess.py#L79), [geo_edit_mixin.py](ssprocess_mixins/geo_edit_mixin.py#L175)
## 常用日志配置方案
不同的应用场景需要不同的日志配置策略。以下是几种常用的配置方案,您可以根据实际需求选择或修改。
### 方案一:开发调试配置
适合开发阶段,输出详细调试信息到控制台,同时记录警告和错误到文件。
| 配置项 | 控制台处理器 | 文件处理器 |
|-------|------------|-----------|
| 日志级别 | DEBUG | WARNING |
| 格式 | 简洁格式 | 详细格式 |
| 用途 | 实时查看 | 长期保存 |
```python
# 开发调试配置
import logging
from sunvpy import SSProcess
logger = logging.getLogger('debug_script')
logger.setLevel(logging.DEBUG)
# 控制台处理器 - 显示所有调试信息
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(logging.Formatter(
'%(levelname)s: %(message)s'
))
# 文件处理器 - 只记录警告和错误
file_handler = logging.FileHandler('debug.log', encoding='utf-8')
file_handler.setLevel(logging.WARNING)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s - %(levelname)s - %(message)s'
))
logger.addHandler(console_handler)
logger.addHandler(file_handler)
SSProcess.set_logger(logger)
```
### 方案二:生产环境配置
适合正式运行的脚本,只记录重要信息到文件,避免输出过多调试信息影响性能。
| 配置项 | 文件处理器 |
|-------|-----------|
| 日志级别 | INFO |
| 格式 | 包含时间戳和位置信息 |
| 文件轮转 | 按大小或日期自动分割 |
```python
# 生产环境配置(带日志文件轮转)
import logging
from logging.handlers import RotatingFileHandler
from sunvpy import SSProcess
logger = logging.getLogger('production_script')
logger.setLevel(logging.INFO)
# 创建支持文件轮转的处理器
# 每个日志文件最大 5MB最多保留 3 个备份文件
file_handler = RotatingFileHandler(
'production.log',
maxBytes=5*1024*1024, # 5MB
backupCount=3,
encoding='utf-8'
)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s [%(levelname)s] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
))
logger.addHandler(file_handler)
SSProcess.set_logger(logger)
```
### 方案三:简洁配置
如果您只需要最基础的日志功能,可以使用以下最简配置:
```python
# 最简配置
import logging
from sunvpy import SSProcess
# 配置根 logger一次性配置所有 logger
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='simple.log',
filemode='a'
)
# 获取并注入 logger
SSProcess.set_logger(logging.getLogger())
```
Sources: [log_mixin.py](ssprocess_mixins/log_mixin.py#L26-L42)
## 日志记录方法说明
配置完成后,您可以通过两种方式记录日志:
### 自动记录(系统内部调用)
SSProcess 的许多内部操作会在遇到错误时自动调用 `log_error_msg()` 记录日志,例如在无法获取数据源时会自动记录错误。
```python
# SSProcess 内部代码示例(来自 geo_edit_mixin.py
if ds_eps is None:
self.log_error_msg("无法获取当前数据源!")
return self.curNewObj
```
这类日志无需您手动触发,会在问题发生时自动记录。
Sources: [geo_edit_mixin.py](ssprocess_mixins/geo_edit_mixin.py#L174-L176)
### 手动记录(自定义日志)
您也可以在脚本中手动记录自定义日志消息,帮助跟踪脚本执行过程。
```python
from sunvpy import SSProcess
import logging
# 配置日志(使用之前介绍的任何方案)
logger = logging.getLogger('manual_logging')
logger.setLevel(logging.INFO)
handler = logging.FileHandler('manual.log', encoding='utf-8')
handler.setFormatter(logging.Formatter(
'%(asctime)s - %(levelname)s - %(message)s'
))
logger.addHandler(handler)
SSProcess.set_logger(logger)
# 记录脚本开始执行
logger.info("脚本开始执行")
# 执行选择集查询
SSProcess.clearSelection()
SSProcess.setSelectCondition("SSObj_Code", "==", "3103013")
SSProcess.selectFilter()
count = SSProcess.getSelGeoCount()
logger.info(f"查询完成,共找到 {count} 个对象")
# 处理结果
if count == 0:
logger.warning("未找到任何符合条件的对象")
else:
logger.info("开始处理对象...")
# 处理逻辑...
logger.info("对象处理完成")
# 记录脚本执行结束
logger.info("脚本执行结束")
```
### 日志记录最佳实践
| 实践 | 说明 |
|-----|------|
| 使用适当的日志级别 | DEBUG 用于调试INFO 用于关键步骤WARNING 用于潜在问题ERROR 用于错误 |
| 包含上下文信息 | 日志消息应包含足够的信息以便理解问题(如对象 ID、条件值等 |
| 避免过度日志 | 生产环境中避免记录大量 DEBUG 级别日志,影响性能 |
| 使用结构化日志 | 对于复杂应用,考虑使用 JSON 格式的结构化日志 |
| 定期清理日志 | 配置日志轮转或定期清理旧日志文件,避免占用过多磁盘空间 |
Sources: [log_mixin.py](ssprocess_mixins/log_mixin.py#L36-L42)
## 完整示例:带日志的属性修改脚本
以下是一个完整的示例脚本,展示如何在实际项目中集成日志功能。该脚本修改选择集中对象的属性,并在每个关键步骤记录日志。
```python
import logging
from logging.handlers import RotatingFileHandler
from sunvpy import SSProcess
# ========== 日志配置开始 ==========
logger = logging.getLogger('attribute_update_script')
logger.setLevel(logging.INFO)
# 配置文件处理器(自动轮转)
file_handler = RotatingFileHandler(
'attribute_update.log',
maxBytes=10*1024*1024, # 10MB
backupCount=5,
encoding='utf-8'
)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s [%(levelname)s] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
))
# 配置控制台处理器(只显示警告和错误)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING)
logger.addHandler(file_handler)
logger.addHandler(console_handler)
SSProcess.set_logger(logger)
# ========== 日志配置结束 ==========
# ========== 主脚本开始 ==========
logger.info("=" * 60)
logger.info("属性修改脚本开始执行")
logger.info("=" * 60)
try:
# 步骤 1清除旧选择集和条件
logger.info("步骤 1清除旧选择集和条件")
SSProcess.clearSelection()
SSProcess.clearSelectCondition()
# 步骤 2设置选择条件
logger.info("步骤 2设置选择条件")
filter_code = "3103013"
SSProcess.setSelectCondition("SSObj_Code", "==", filter_code)
logger.info(f"筛选条件SSObj_Code == {filter_code}")
# 步骤 3执行查询
logger.info("步骤 3执行查询")
SSProcess.selectFilter()
count = SSProcess.getSelGeoCount()
logger.info(f"查询结果:共找到 {count} 个对象")
if count == 0:
logger.warning("选择集为空,脚本退出")
exit()
# 步骤 4批量修改属性
logger.info("步骤 4开始批量修改属性")
success_count = 0
fail_count = 0
for i in range(count):
try:
# 修改属性
SSProcess.setSelGeoValue(i, "SSObj_Color", "16711680") # 红色
SSProcess.setSelGeoValue(i, "SSObj_Name", f"修改后对象_{i}")
success_count += 1
# 每 100 个对象记录一次进度
if (i + 1) % 100 == 0:
logger.info(f"已处理 {i + 1}/{count} 个对象")
except Exception as e:
fail_count += 1
logger.error(f"修改第 {i} 个对象时出错:{str(e)}")
# 步骤 5保存修改
logger.info("步骤 5保存修改到数据库")
SSProcess.saveBufferObjToDatabase()
# 输出统计信息
logger.info("=" * 60)
logger.info("执行统计:")
logger.info(f" 总处理对象数:{count}")
logger.info(f" 成功修改数:{success_count}")
logger.info(f" 失败对象数:{fail_count}")
logger.info("=" * 60)
except Exception as e:
logger.critical(f"脚本执行过程中发生严重错误:{str(e)}", exc_info=True)
raise
logger.info("脚本执行结束")
logger.info("=" * 60)
```
运行此脚本后,日志文件 `attribute_update.log` 将包含完整的执行轨迹,便于后续分析和排查问题。
Sources: [log_mixin.py](ssprocess_mixins/log_mixin.py#L26-L42), [geo_edit_mixin.py](ssprocess_mixins/geo_edit_mixin.py#L67-L200)
## 故障排查
### 日志文件未生成
**问题**:脚本运行后没有生成预期的日志文件。
**可能原因及解决方法**
| 原因 | 解决方法 |
|-----|---------|
| 日志级别设置过高 | 检查 Logger 和 Handler 的级别设置,确保有符合条件的日志消息 |
| 未将 Handler 添加到 Logger | 确认调用了 `logger.addHandler(handler)` |
| 文件路径无写入权限 | 检查文件路径是否存在且具有写入权限 |
| 日志消息级别低于配置 | 确保记录的日志消息级别不低于 Handler 的级别 |
### 日志输出乱码
**问题**:日志文件中出现中文乱码。
**解决方法**:在创建 FileHandler 时显式指定 `encoding='utf-8'` 参数:
```python
file_handler = logging.FileHandler('log.txt', encoding='utf-8')
```
### 未记录 SSProcess 内部错误
**问题**SSProcess 操作出错时没有记录到日志文件。
**原因**SSProcess 内部的 `log_error_msg()` 方法只在配置了 logger 时才会记录日志。
**解决方法**:确保在调用任何 SSProcess 方法之前调用 `SSProcess.set_logger(logger)`
```python
# 正确的顺序
logger = configure_logger() # 先配置 logger
SSProcess.set_logger(logger) # 然后注入
SSProcess.selectFilter() # 最后才执行操作
```
Sources: [log_mixin.py](ssprocess_mixins/log_mixin.py#L36-L42)
## 下一步学习
完成日志配置的学习后,建议您继续探索以下相关主题:
- [进度条使用指南](38-jin-du-tiao-shi-yong-zhi-nan) - 学习如何在长时间运行的脚本中提供进度反馈
- [错误处理与异常管理](39-cuo-wu-chu-li-yu-yi-chang-guan-li) - 掌握更高级的错误处理技巧
- [对象缓存机制](23-dui-xiang-huan-cun-ji-zhi) - 了解如何高效处理大量对象
结合日志记录、进度显示和错误处理,您将能够开发出健壮、易调试、用户友好的专业级 SunvStation 脚本。