Files
Alicho/docs/api/common.md
2025-10-28 10:27:49 +08:00

22 KiB
Raw Blame History

公共组件API参考

目录

概述

公共组件模块提供了错误处理和日志记录等基础功能,为整个音频后端系统提供统一的异常处理和日志记录机制。这些组件设计为高性能、线程安全且跨平台,使开发者能够轻松地进行错误处理和系统状态监控。

核心特性:

  • 统一的错误处理:提供清晰的错误码、异常层次结构和错误处理工具
  • 灵活的日志系统:支持多级别、多目标(控制台/文件)的日志记录
  • 模块化设计:允许按模块进行日志和错误处理
  • 高性能:所有组件设计时考虑了性能开销,适用于实时音频处理
  • 线程安全:支持多线程环境下的并发操作

错误处理系统

错误处理系统提供了一套完整的错误处理机制,包括错误码、异常类和错误处理工具。

错误码

ErrorCode枚举定义了系统中所有可能的错误状态:

enum class ErrorCode {
    // 通用错误 (0-999)
    SUCCESS = 0,
    UNKNOWN_ERROR = 1,
    INVALID_ARGUMENT = 2,
    INVALID_OPERATION = 3,
    NOT_IMPLEMENTED = 4,
    OUT_OF_MEMORY = 5,
    OPERATION_TIMED_OUT = 6,
    NOT_INITIALIZED = 7,
    ALREADY_INITIALIZED = 8,
    
    // 文件和I/O错误 (1000-1999)
    FILE_NOT_FOUND = 1000,
    FILE_ACCESS_DENIED = 1001,
    FILE_READ_ERROR = 1002,
    FILE_WRITE_ERROR = 1003,
    FILE_FORMAT_ERROR = 1004,
    
    // 音频处理错误 (2000-2999)
    AUDIO_FORMAT_ERROR = 2000,
    AUDIO_DEVICE_ERROR = 2001,
    AUDIO_BUFFER_UNDERRUN = 2002,
    AUDIO_BUFFER_OVERRUN = 2003,
    AUDIO_SAMPLE_RATE_MISMATCH = 2004,
    AUDIO_CHANNEL_COUNT_MISMATCH = 2005,
    
    // 插件错误 (3000-3999)
    PLUGIN_LOAD_ERROR = 3000,
    PLUGIN_NOT_FOUND = 3001,
    PLUGIN_VERSION_MISMATCH = 3002,
    PLUGIN_INITIALIZATION_ERROR = 3003,
    PLUGIN_COMMUNICATION_ERROR = 3004,
    PLUGIN_EXECUTION_ERROR = 3005,
    PLUGIN_TIMEOUT = 3006,
    
    // 通信错误 (4000-4999)
    COMMUNICATION_ERROR = 4000,
    CONNECTION_ERROR = 4001,
    DISCONNECTED = 4002,
    MESSAGE_FORMAT_ERROR = 4003,
    PROTOCOL_ERROR = 4004,
    TIMEOUT = 4005,
    
    // 系统错误 (5000-5999)
    SYSTEM_ERROR = 5000,
    PERMISSION_DENIED = 5001,
    NOT_ENOUGH_RESOURCES = 5002
};

错误码按功能域分组,便于管理和扩展。系统还提供了错误分类枚举:

enum class ErrorCategory {
    GENERAL,
    FILE,
    AUDIO,
    PLUGIN,
    COMMUNICATION,
    SYSTEM
};

获取错误描述和分类的辅助函数:

// 获取错误分类
ErrorCategory get_error_category(ErrorCode code);

// 获取错误码对应的描述信息
std::string get_error_description(ErrorCode code);

异常层次结构

系统定义了一个异常层次结构,以AudioBackendException为基类:

// 基础异常类
class AudioBackendException : public std::exception {
public:
    explicit AudioBackendException(ErrorCode code, 
                                  const std::string& message = "", 
                                  const std::string& details = "");
    
    // 获取错误码
    ErrorCode code() const noexcept;
    
    // 获取错误消息
    const std::string& message() const noexcept;
    
    // 获取详细信息
    const std::string& details() const noexcept;
    
    // 获取完整错误消息
    const char* what() const noexcept override;
    
    // 获取错误分类
    ErrorCategory category() const noexcept;
    
    // 获取std::error_code
    std::error_code error_code() const noexcept;
    
    // 是否是某个错误码
    bool is(ErrorCode code) const noexcept;
    
    // 是否属于某个错误分类
    bool is_category(ErrorCategory category) const noexcept;
};

基于基础异常类,系统还定义了特定领域的异常类:

// 文件异常
class FileException : public AudioBackendException;

// 音频异常
class AudioException : public AudioBackendException;

// 插件异常
class PluginException : public AudioBackendException;

// 通信异常
class CommunicationException : public AudioBackendException;

// 系统异常
class SystemException : public AudioBackendException;

错误处理器

ErrorHandler类提供了集中式的错误处理机制:

class ErrorHandler {
public:
    // 错误处理回调函数类型
    using ErrorCallback = std::function<void(const AudioBackendException&)>;
    
    // 获取单例实例
    static ErrorHandler& instance();
    
    // 添加全局错误处理回调
    void add_error_handler(const ErrorCallback& handler);
    
    // 移除错误处理回调
    void remove_error_handler(const ErrorCallback& handler);
    
    // 处理错误
    void handle_error(const AudioBackendException& exception);
    
    // 处理错误(从错误码创建异常)
    void handle_error(ErrorCode code, 
                      const std::string& message = "", 
                      const std::string& details = "");
    
    // 处理异常捕获
    void handle_exception(std::exception_ptr eptr);
    
    // 清除所有错误处理回调
    void clear_handlers();
};

系统还提供了RAII风格的错误处理辅助类

class ScopedErrorHandler {
public:
    explicit ScopedErrorHandler(ErrorHandler::ErrorCallback handler);
    ~ScopedErrorHandler();
};

工具函数

错误处理系统提供了一系列工具函数:

// 根据错误码创建适当的异常
std::exception_ptr create_exception(ErrorCode code, 
                                   const std::string& message = "", 
                                   const std::string& details = "");

// 根据错误码抛出异常
[[noreturn]] void throw_exception(ErrorCode code, 
                                 const std::string& message = "", 
                                 const std::string& details = "");

// 错误检查函数条件为true时抛出异常
void check_error(bool condition, 
                ErrorCode error_code, 
                const std::string& message = "", 
                const std::string& details = "");

// 将系统错误码转换为AudioBackend错误码
ErrorCode system_error_to_error_code(int system_error);

// 将std::error_code转换为AudioBackend错误码
ErrorCode std_error_to_error_code(const std::error_code& ec);

宏定义

系统提供了便捷的错误处理宏:

// 检查条件并抛出异常
#define AUDIO_CHECK_ERROR(condition, error_code, message)

// 直接抛出异常
#define AUDIO_THROW_ERROR(error_code, message)

// 处理错误调用ErrorHandler
#define AUDIO_HANDLE_ERROR(error_code, message)

// try-catch简化宏
#define AUDIO_TRY_CATCH_ALL()
#define AUDIO_CATCH_ALL()

日志系统

日志系统基于spdlog库实现提供了灵活、高性能的日志记录功能。

日志级别

日志系统定义了以下日志级别:

enum class LogLevel {
    TRACE,    // 最详细的调试信息
    DEBUG,    // 详细的调试信息
    INFO,     // 一般信息
    WARN,     // 警告信息
    ERR,      // 错误信息
    CRITICAL, // 严重错误
    OFF       // 关闭日志
};

日志接口

Logger类提供了日志系统的核心功能:

class Logger {
public:
    // 获取单例实例
    static Logger& instance();
    
    // 初始化日志系统
    void initialize(const std::string& app_name = "AudioBackend",
                   LogLevel console_level = LogLevel::INFO,
                   bool file_logging = true,
                   const std::string& log_dir = "logs",
                   size_t max_file_size = 5 * 1024 * 1024,
                   size_t max_files = 3);
    
    // 关闭日志系统
    void shutdown();
    
    // 设置全局日志级别
    void set_level(LogLevel level);
    
    // 设置控制台日志级别
    void set_console_level(LogLevel level);
    
    // 设置文件日志级别
    void set_file_level(LogLevel level);
    
    // 获取当前日志级别
    LogLevel get_level() const;
    
    // 获取日志器
    std::shared_ptr<spdlog::logger> get_logger() const;
    
    // 获取特定模块的日志器
    std::shared_ptr<spdlog::logger> get_module_logger(const std::string& module_name);
    
    // 刷新日志缓冲区
    void flush();
    
    // 便捷的日志记录方法
    template<typename... Args>
    void trace(std::string_view fmt, const Args&... args);
    
    template<typename... Args>
    void debug(std::string_view fmt, const Args&... args);
    
    template<typename... Args>
    void info(std::string_view fmt, const Args&... args);
    
    template<typename... Args>
    void warn(std::string_view fmt, const Args&... args);
    
    template<typename... Args>
    void err(std::string_view fmt, const Args&... args);
    
    template<typename... Args>
    void critical(std::string_view fmt, const Args&... args);
};

系统还提供了全局日志函数无需直接访问Logger实例

template<typename... Args>
inline void log_trace(std::string_view fmt, const Args&... args);

template<typename... Args>
inline void log_debug(std::string_view fmt, const Args&... args);

template<typename... Args>
inline void log_info(std::string_view fmt, const Args&... args);

template<typename... Args>
inline void log_warn(std::string_view fmt, const Args&... args);

template<typename... Args>
inline void log_err(std::string_view fmt, const Args&... args);

template<typename... Args>
inline void log_critical(std::string_view fmt, const Args&... args);

模块日志

系统支持按模块进行日志记录,提供了便捷的模块日志宏:

// 创建模块日志器
#define AUDIO_MODULE_LOGGER(module_name)

// 模块级别日志记录宏
#define AUDIO_LOG_TRACE(...)
#define AUDIO_LOG_DEBUG(...)
#define AUDIO_LOG_INFO(...)
#define AUDIO_LOG_WARN(...)
#define AUDIO_LOG_ERR(...)
#define AUDIO_LOG_CRITICAL(...)

日志配置

日志系统支持以下配置选项:

  • 应用名称:日志中显示的应用名称
  • 控制台日志级别:显示在控制台的最低日志级别
  • 文件日志:是否启用文件日志
  • 日志目录:日志文件的存储目录
  • 最大文件大小:单个日志文件的最大大小
  • 最大文件数:轮转日志的最大文件数量

使用示例

错误处理示例

基本错误检查和处理:

#include "common/error.h"
#include <iostream>

using namespace audio_backend::common;

void example_error_handling() {
    // 使用错误检查宏
    try {
        bool condition = false;  // 假设这是一个失败条件
        AUDIO_CHECK_ERROR(condition, ErrorCode::INVALID_OPERATION, 
                         "操作无法执行,参数无效");
    } catch (const AudioBackendException& e) {
        std::cout << "捕获异常: " << e.what() << std::endl;
        std::cout << "错误码: " << static_cast<int>(e.code()) << std::endl;
        std::cout << "错误类别: " << static_cast<int>(e.category()) << std::endl;
    }
    
    // 直接抛出异常
    try {
        AUDIO_THROW_ERROR(ErrorCode::FILE_NOT_FOUND, "找不到配置文件");
    } catch (const FileException& e) {
        std::cout << "捕获文件异常: " << e.what() << std::endl;
    } catch (const AudioBackendException& e) {
        std::cout << "这不会被执行因为FileException已经捕获" << std::endl;
    }
    
    // 使用错误处理器
    ErrorHandler::instance().add_error_handler([](const AudioBackendException& e) {
        std::cout << "错误处理器: " << e.what() << std::endl;
    });
    
    // 当触发错误时,会调用错误处理器
    AUDIO_HANDLE_ERROR(ErrorCode::AUDIO_DEVICE_ERROR, "音频设备初始化失败");
}

使用作用域错误处理器:

void example_scoped_error_handler() {
    // 创建一个局部作用域的错误处理器
    {
        ScopedErrorHandler handler([](const AudioBackendException& e) {
            std::cout << "作用域错误处理器: " << e.what() << std::endl;
        });
        
        // 在这个作用域内,所有错误都会被处理器捕获
        AUDIO_HANDLE_ERROR(ErrorCode::COMMUNICATION_ERROR, "连接超时");
        
    } // 离开作用域,错误处理器自动移除
    
    // 此处的错误不会被上面的处理器捕获
    AUDIO_HANDLE_ERROR(ErrorCode::PLUGIN_LOAD_ERROR, "插件加载失败");
}

全局错误处理:

// 全局错误处理函数
void global_error_handler(const AudioBackendException& e) {
    std::cerr << "全局错误处理: " << e.what() << std::endl;
    
    // 可以根据错误类别执行不同操作
    switch (e.category()) {
        case ErrorCategory::FILE:
            // 处理文件错误...
            break;
        case ErrorCategory::AUDIO:
            // 处理音频错误...
            break;
        // 其他错误类别...
    }
}

int main() {
    // 设置全局错误处理器
    ErrorHandler::instance().add_error_handler(global_error_handler);
    
    // 现在所有通过ErrorHandler处理的错误都会被全局处理器捕获
    
    try {
        // 应用代码...
    } catch (...) {
        // 捕获所有未处理的异常
        ErrorHandler::instance().handle_exception(std::current_exception());
    }
    
    return 0;
}

日志记录示例

基本日志记录:

#include "common/logger.h"

using namespace audio_backend::common;

void example_logging() {
    // 初始化日志系统
    Logger::instance().initialize("MyAudioApp", LogLevel::DEBUG);
    
    // 使用全局日志函数
    log_info("应用启动");
    log_debug("调试信息: {}", "测试消息");
    
    // 带参数的日志
    int param1 = 42;
    std::string param2 = "测试";
    log_info("参数值: {} 和 {}", param1, param2);
    
    // 错误日志
    log_err("发生错误: {}", "找不到文件");
    
    // 直接使用Logger实例
    Logger::instance().critical("严重错误: 系统无法继续运行");
    
    // 刷新日志缓冲区(确保写入磁盘)
    Logger::instance().flush();
}

模块日志:

#include "common/logger.h"

using namespace audio_backend::common;

// 定义模块日志器
AUDIO_MODULE_LOGGER("AudioEngine");

class AudioEngine {
public:
    void initialize() {
        AUDIO_LOG_INFO("音频引擎初始化中...");
        
        // 一些初始化代码...
        
        if (true /* 初始化成功 */) {
            AUDIO_LOG_INFO("音频引擎初始化成功");
        } else {
            AUDIO_LOG_ERR("音频引擎初始化失败");
        }
    }
    
    void process() {
        AUDIO_LOG_TRACE("处理音频数据...");
        
        // 一些处理代码...
        
        if (false /* 假设发生了问题 */) {
            AUDIO_LOG_WARN("处理过程中发生警告");
        }
        
        AUDIO_LOG_DEBUG("完成音频处理,输出样本数: {}", 1024);
    }
};

日志配置:

void configure_logging() {
    // 完整配置
    Logger::instance().initialize(
        "MyAudioApp",             // 应用名称
        LogLevel::INFO,           // 控制台日志级别
        true,                     // 启用文件日志
        "logs",                   // 日志目录
        5 * 1024 * 1024,          // 最大文件大小5MB
        3                         // 最大文件数
    );
    
    // 动态调整日志级别
    Logger::instance().set_level(LogLevel::DEBUG);  // 全局级别
    Logger::instance().set_console_level(LogLevel::INFO);  // 控制台级别
    Logger::instance().set_file_level(LogLevel::TRACE);  // 文件级别
    
    // 为特定模块创建日志器
    auto engine_logger = Logger::instance().get_module_logger("Engine");
    auto plugin_logger = Logger::instance().get_module_logger("Plugin");
    
    // 使用模块日志器
    engine_logger->info("引擎模块信息");
    plugin_logger->debug("插件模块调试信息");
}

集成示例

以下示例展示了如何将错误处理和日志系统集成在一起:

#include "common/error.h"
#include "common/logger.h"
#include <iostream>
#include <stdexcept>

using namespace audio_backend::common;

// 自定义错误处理器,将错误记录到日志
void log_error_handler(const AudioBackendException& e) {
    // 根据错误严重性选择合适的日志级别
    switch (e.category()) {
        case ErrorCategory::SYSTEM:
        case ErrorCategory::PLUGIN:
            log_critical("严重错误: {}", e.what());
            break;
            
        case ErrorCategory::AUDIO:
        case ErrorCategory::COMMUNICATION:
            log_err("错误: {}", e.what());
            break;
            
        case ErrorCategory::FILE:
        case ErrorCategory::GENERAL:
        default:
            log_warn("警告: {}", e.what());
            break;
    }
}

// 示例函数,模拟可能出错的操作
void perform_risky_operation() {
    // 假设条件检查失败
    bool success = false;
    
    if (!success) {
        // 记录错误并抛出异常
        log_debug("操作失败,即将抛出异常");
        AUDIO_THROW_ERROR(ErrorCode::OPERATION_TIMED_OUT, "操作超时");
    }
}

// 完整集成示例
int main() {
    try {
        // 初始化日志系统
        Logger::instance().initialize("AudioApp", LogLevel::DEBUG);
        log_info("应用程序启动");
        
        // 设置将错误记录到日志的处理器
        ErrorHandler::instance().add_error_handler(log_error_handler);
        log_debug("已注册错误处理器");
        
        try {
            // 尝试执行风险操作
            log_info("开始执行操作...");
            perform_risky_operation();
        } catch (const AudioBackendException& e) {
            // 本地处理异常,但不退出
            log_debug("捕获到异常: {}", e.what());
            
            // 也可以重新抛出
            throw;
        }
        
        log_info("应用程序正常退出");
    } catch (const std::exception& e) {
        // 捕获所有未处理的异常
        log_critical("未处理的异常: {}", e.what());
        return 1;
    }
    
    return 0;
}

最佳实践

错误处理最佳实践

  1. 选择适当的错误处理方式

    • 使用错误码返回值处理预期的错误
    • 使用异常处理非预期的错误和严重故障
    • 在性能关键路径上避免使用异常
  2. 合理分类错误

    • 按照错误的性质和严重程度分类
    • 为不同类型的错误提供明确的上下文信息
  3. 提供详细的错误信息

    • 描述发生了什么(错误内容)
    • 解释为什么会发生(错误原因)
    • 建议如何解决(修复方法)
  4. 集中处理错误

    • 使用全局错误处理器统一处理错误
    • 将错误处理与业务逻辑分离
  5. 实时系统的错误处理

    • 避免在音频回调线程中抛出异常
    • 使用错误标志和延迟处理
    • 预先分配错误处理资源,避免动态内存分配

日志记录最佳实践

  1. 选择适当的日志级别

    • TRACE: 最详细的调试信息,通常仅在开发时启用
    • DEBUG: 详细的调试信息,有助于问题诊断
    • INFO: 正常操作信息,记录系统状态和重要事件
    • WARN: 警告信息,表明可能的问题,但不会导致系统失败
    • ERR: 错误信息,表明功能无法正常工作
    • CRITICAL: 严重错误,系统无法继续运行
  2. 结构化日志信息

    • 使用一致的格式记录日志
    • 包含时间戳、日志级别、模块名称、线程ID等元数据
    • 使用参数化日志而不是字符串拼接
  3. 避免过多日志

    • 避免在高频率循环中记录日志
    • 使用采样或节流技术减少重复日志
    • 注意日志对性能的影响
  4. 按模块记录日志

    • 每个模块使用专门的日志器
    • 模块名称应反映代码结构
  5. 实时系统的日志记录

    • 使用异步日志记录,避免阻塞音频线程
    • 考虑使用环形缓冲区进行临时日志存储
    • 定期刷新日志,而不是每条日志都刷新

跨平台考虑

公共组件模块设计为跨平台工作,但在不同平台上仍有一些注意事项:

  1. 文件路径处理

    • Windows使用反斜杠\)作为路径分隔符
    • Unix/Linux/macOS使用正斜杠/)作为路径分隔符
    • 使用平台无关的路径处理函数
  2. 文件权限和访问

    • 不同平台的文件权限模型存在差异
    • 日志文件创建可能受系统权限限制
  3. 系统错误转换

    • 不同平台的系统错误码不同
    • 使用system_error_to_error_code()函数进行统一转换
  4. 线程安全性

    • 所有公共组件都设计为线程安全
    • 不同平台的线程模型存在差异但API保持一致
  5. 性能考虑

    • 不同平台上日志和错误处理的性能特性可能有所不同
    • 高性能应用应当在各目标平台上进行性能测试