17 KiB
日志记录是脚本开发中不可或缺的调试和监控手段,它能帮助您跟踪脚本执行过程、记录错误信息、排查问题根源。本页面将指导您为 SunvStation 脚本配置一个功能完善的日志记录器,让您的脚本具备专业级的日志管理能力。
日志系统架构概览
SunvStation 的日志功能通过 LogMixin 混入类实现,这是 SSProcessManager 继承体系中的基础模块之一。日志系统采用依赖注入设计模式,允许您将自定义配置的日志记录器注入到 SSProcess 中,从而灵活控制日志的输出方式、格式和级别。
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, PySSProcess.py
配置日志记录器流程
配置日志记录器的过程分为四个关键步骤,下图清晰地展示了从创建到使用的完整流程:
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
步骤一:创建 Logger 实例
首先需要创建一个 Python 标准库的 Logger 实例。建议使用脚本文件名作为 Logger 的名称,这样可以在日志输出中清晰地识别日志来源。
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 模块
步骤二:配置 Handler 处理器
Handler 决定了日志消息的输出目的地。常用的 Handler 类型包括文件处理器和控制台处理器。您可以根据需求选择单一处理器或组合使用多个处理器。
文件处理器配置
将日志输出到文件是最常见的需求,特别适合长期运行的脚本和自动化任务。
# 创建文件处理器,指定日志文件路径
file_handler = logging.FileHandler(
'script_log.log', # 日志文件路径
mode='a', # 追加模式('a'追加,'w'覆盖)
encoding='utf-8' # 文件编码
)
file_handler.setLevel(logging.INFO) # 文件处理器只记录 INFO 级别及以上
控制台处理器配置
控制台处理器将日志输出到标准输出流,适合开发调试阶段使用。
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING) # 控制台只显示 WARNING 级别及以上
Sources: log_mixin.py
步骤三:设置 Formatter 格式化器
Formatter 定义了日志消息的输出格式,包括时间戳、日志级别、消息内容等信息。合理的格式能让日志更易于阅读和分析。
基本格式示例
# 创建格式化器
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 |
完整格式示例(包含位置信息)
# 包含文件名、行号和函数名的详细格式
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
完成上述配置后,需要将处理器添加到 Logger,然后将 Logger 注入到 SSProcess 中。
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, geo_edit_mixin.py
常用日志配置方案
不同的应用场景需要不同的日志配置策略。以下是几种常用的配置方案,您可以根据实际需求选择或修改。
方案一:开发调试配置
适合开发阶段,输出详细调试信息到控制台,同时记录警告和错误到文件。
| 配置项 | 控制台处理器 | 文件处理器 |
|---|---|---|
| 日志级别 | DEBUG | WARNING |
| 格式 | 简洁格式 | 详细格式 |
| 用途 | 实时查看 | 长期保存 |
# 开发调试配置
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 |
| 格式 | 包含时间戳和位置信息 |
| 文件轮转 | 按大小或日期自动分割 |
# 生产环境配置(带日志文件轮转)
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)
方案三:简洁配置
如果您只需要最基础的日志功能,可以使用以下最简配置:
# 最简配置
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 的许多内部操作会在遇到错误时自动调用 log_error_msg() 记录日志,例如在无法获取数据源时会自动记录错误。
# SSProcess 内部代码示例(来自 geo_edit_mixin.py)
if ds_eps is None:
self.log_error_msg("无法获取当前数据源!")
return self.curNewObj
这类日志无需您手动触发,会在问题发生时自动记录。
Sources: geo_edit_mixin.py
手动记录(自定义日志)
您也可以在脚本中手动记录自定义日志消息,帮助跟踪脚本执行过程。
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
完整示例:带日志的属性修改脚本
以下是一个完整的示例脚本,展示如何在实际项目中集成日志功能。该脚本修改选择集中对象的属性,并在每个关键步骤记录日志。
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, geo_edit_mixin.py
故障排查
日志文件未生成
问题:脚本运行后没有生成预期的日志文件。
可能原因及解决方法:
| 原因 | 解决方法 |
|---|---|
| 日志级别设置过高 | 检查 Logger 和 Handler 的级别设置,确保有符合条件的日志消息 |
| 未将 Handler 添加到 Logger | 确认调用了 logger.addHandler(handler) |
| 文件路径无写入权限 | 检查文件路径是否存在且具有写入权限 |
| 日志消息级别低于配置 | 确保记录的日志消息级别不低于 Handler 的级别 |
日志输出乱码
问题:日志文件中出现中文乱码。
解决方法:在创建 FileHandler 时显式指定 encoding='utf-8' 参数:
file_handler = logging.FileHandler('log.txt', encoding='utf-8')
未记录 SSProcess 内部错误
问题:SSProcess 操作出错时没有记录到日志文件。
原因:SSProcess 内部的 log_error_msg() 方法只在配置了 logger 时才会记录日志。
解决方法:确保在调用任何 SSProcess 方法之前调用 SSProcess.set_logger(logger)。
# 正确的顺序
logger = configure_logger() # 先配置 logger
SSProcess.set_logger(logger) # 然后注入
SSProcess.selectFilter() # 最后才执行操作
Sources: log_mixin.py
下一步学习
完成日志配置的学习后,建议您继续探索以下相关主题:
结合日志记录、进度显示和错误处理,您将能够开发出健壮、易调试、用户友好的专业级 SunvStation 脚本。