日志记录是脚本开发中不可或缺的调试和监控手段,它能帮助您跟踪脚本执行过程、记录错误信息、排查问题根源。本页面将指导您为 SunvStation 脚本配置一个功能完善的日志记录器,让您的脚本具备专业级的日志管理能力。 ## 日志系统架构概览 SunvStation 的日志功能通过 **LogMixin** 混入类实现,这是 SSProcessManager 继承体系中的基础模块之一。日志系统采用**依赖注入**设计模式,允许您将自定义配置的日志记录器注入到 SSProcess 中,从而灵活控制日志的输出方式、格式和级别。 ```mermaid classDiagram class Logger { <> +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[文件处理器
FileHandler] C --> C2[控制台处理器
StreamHandler] C --> C3[综合配置
多处理器] 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 脚本。