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

17 KiB
Raw Permalink Blame History

日志记录是脚本开发中不可或缺的调试和监控手段,它能帮助您跟踪脚本执行过程、记录错误信息、排查问题根源。本页面将指导您为 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 级别时,DEBUGINFO 级别的消息将被忽略。

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 脚本。