22 KiB
22 KiB
公共组件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;
}
最佳实践
错误处理最佳实践
-
选择适当的错误处理方式:
- 使用错误码返回值处理预期的错误
- 使用异常处理非预期的错误和严重故障
- 在性能关键路径上避免使用异常
-
合理分类错误:
- 按照错误的性质和严重程度分类
- 为不同类型的错误提供明确的上下文信息
-
提供详细的错误信息:
- 描述发生了什么(错误内容)
- 解释为什么会发生(错误原因)
- 建议如何解决(修复方法)
-
集中处理错误:
- 使用全局错误处理器统一处理错误
- 将错误处理与业务逻辑分离
-
实时系统的错误处理:
- 避免在音频回调线程中抛出异常
- 使用错误标志和延迟处理
- 预先分配错误处理资源,避免动态内存分配
日志记录最佳实践
-
选择适当的日志级别:
- TRACE: 最详细的调试信息,通常仅在开发时启用
- DEBUG: 详细的调试信息,有助于问题诊断
- INFO: 正常操作信息,记录系统状态和重要事件
- WARN: 警告信息,表明可能的问题,但不会导致系统失败
- ERR: 错误信息,表明功能无法正常工作
- CRITICAL: 严重错误,系统无法继续运行
-
结构化日志信息:
- 使用一致的格式记录日志
- 包含时间戳、日志级别、模块名称、线程ID等元数据
- 使用参数化日志而不是字符串拼接
-
避免过多日志:
- 避免在高频率循环中记录日志
- 使用采样或节流技术减少重复日志
- 注意日志对性能的影响
-
按模块记录日志:
- 每个模块使用专门的日志器
- 模块名称应反映代码结构
-
实时系统的日志记录:
- 使用异步日志记录,避免阻塞音频线程
- 考虑使用环形缓冲区进行临时日志存储
- 定期刷新日志,而不是每条日志都刷新
跨平台考虑
公共组件模块设计为跨平台工作,但在不同平台上仍有一些注意事项:
-
文件路径处理:
- Windows使用反斜杠(
\)作为路径分隔符 - Unix/Linux/macOS使用正斜杠(
/)作为路径分隔符 - 使用平台无关的路径处理函数
- Windows使用反斜杠(
-
文件权限和访问:
- 不同平台的文件权限模型存在差异
- 日志文件创建可能受系统权限限制
-
系统错误转换:
- 不同平台的系统错误码不同
- 使用
system_error_to_error_code()函数进行统一转换
-
线程安全性:
- 所有公共组件都设计为线程安全
- 不同平台的线程模型存在差异,但API保持一致
-
性能考虑:
- 不同平台上日志和错误处理的性能特性可能有所不同
- 高性能应用应当在各目标平台上进行性能测试