错误处理 - OpenZeppelin 文档

本文档介绍了OpenZeppelin Monitor的结构化错误处理系统,该系统提供了丰富的上下文信息和跨服务边界的跟踪能力。文档通过实例展示了错误如何在系统中传播,并详细描述了错误结构,包括错误上下文和特定于域的错误类型。此外,还提供了错误处理指南,说明了何时使用哪种模式来创建和跟踪错误,例如跨域边界时转换为特定于域的错误类型,以及使用with_context()添加信息。

错误处理

概述

OpenZeppelin Monitor 使用结构化的错误处理系统,该系统在服务边界之间提供丰富的上下文和跟踪功能。 让我们从一个现实世界的例子开始,了解错误如何在我们的系统中流动。

错误流示例

让我们跟踪错误如何在我们的区块链监控系统中传播:

底层传输 ( endpoint_manager.rs)

// 使用特定上下文创建基本错误
async fn send_raw_request(...) -> Result<Value, anyhow::Error> {
    let response = client.post(...)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to send request: {}", e))?;

    if !status.is_success() {
        return Err(anyhow::anyhow!("HTTP error {}: {}", status, error_body));
    }
}

客户端层 ( evm/client.rs)

// 向底层错误添加业务上下文
async fn get_transaction_receipt(...) -> Result<EVMTransactionReceipt, anyhow::Error> {
    let response = self.alloy_client
        .send_raw_request(...)
        .await
        .with_context(|| format!("Failed to get transaction receipt: {}", tx_hash))?;

    if receipt_data.is_null() {
        return Err(anyhow::anyhow!("Transaction receipt not found"));
    }
}

过滤器层 ( evm/filter.rs)

// 转换为特定于域的错误
async fn filter_block(...) -> Result<Vec<MonitorMatch>, FilterError> {
    let receipts = match futures::future::join_all(receipt_futures).await {
        Ok(receipts) => receipts,
        Err(e) => {
            return Err(FilterError::network_error(
                format!("Failed to get transaction receipts for block {}", block_num),
                Some(e.into()),
                None,
            ));
        }
    };
}

发生此错误时,它会生成以下日志:

ERROR filter_block: openzeppelin_monitor::utils::error: Error occurred,
    error.message: Failed to get transaction receipts for block 15092829,
    error.trace_id: a464d73c-5992-4cb5-a002-c8d705bfef8d,
    error.timestamp: 2025-03-14T09:42:03.412341+00:00,
    error.chain: Failed to get receipt for transaction 0x7722194b65953085fe1e9ec01003f1d7bdd6258a0ea5c91a59da80419513d95d
  Caused by: HTTP error 429 Too Many Requests: {"code":-32007,"message":"[Exceeded request limit per second]"}
 network: ethereum_mainnet

错误结构

错误上下文

我们系统中的每个错误都包含详细的上下文信息:

pub struct ErrorContext {
    /// 错误消息
    pub message: String,
    /// 源错误(如果有)
    pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
    /// 用于错误跟踪的唯一跟踪 ID
    pub trace_id: String,
    /// 发生错误时的时间戳
    pub timestamp: DateTime<Utc>,
    /// 可选的键值元数据
    pub metadata: HashMap<String, String>,
}

领域特定的错误类型

模块 错误类型 描述
Configuration ConfigError - ValidationError - 配置验证失败<br> <br>- ParseError - 配置解析问题<br> <br>- FileError - 文件系统相关错误<br> <br>- Other - 未分类的错误
Security SecurityError - ValidationError - 安全验证失败<br> <br>- ParseError - 安全数据解析问题<br> <br>- NetworkError - 安全服务连接问题<br> <br>- Other - 未分类的安全相关错误
Blockchain Service BlockChainError - ConnectionError - 网络连接问题<br> <br>- RequestError - 格式错误的请求或无效的响应<br> <br>- BlockNotFound - 找不到请求的区块<br> <br>- TransactionError - 交易处理失败<br> <br>- InternalError - 内部客户端错误<br> <br>- ClientPoolError - 客户端池相关问题<br> <br>- Other - 未分类的错误
Block Watcher Service BlockWatcherError - SchedulerError - 区块监视调度问题<br> <br>- NetworkError - 网络连接问题<br> <br>- ProcessingError - 区块处理失败<br> <br>- StorageError - 存储操作失败<br> <br>- BlockTrackerError - 区块跟踪问题<br> <br>- Other - 未分类的错误
Filter Service FilterError - BlockTypeMismatch - 区块类型验证失败<br> <br>- NetworkError - 网络连接问题<br> <br>- InternalError - 内部处理错误<br> <br>- Other - 未分类的错误
Notification Service NotificationError - NetworkError - 网络连接问题<br> <br>- ConfigError - 配置问题<br> <br>- InternalError - 内部处理错误<br> <br>- ExecutionError - 脚本执行失败<br> <br>- Other - 未分类的错误
Repository RepositoryError - ValidationError - 数据验证失败<br> <br>- LoadError - 数据加载问题<br> <br>- InternalError - 内部处理错误<br> <br>- Other - 未分类的错误
Script Utils ScriptError - NotFound - 找不到资源错误<br> <br>- ExecutionError - 脚本执行失败<br> <br>- ParseError - 脚本解析问题<br> <br>- SystemError - 系统级错误<br> <br>- Other - 未分类的错误
Trigger Service TriggerError - NotFound - 找不到资源错误<br> <br>- ExecutionError - 触发器执行失败<br> <br>- ConfigurationError - 触发器配置问题<br> <br>- Other - 未分类的错误
Monitor Executor MonitorExecutionError - NotFound - 找不到资源错误<br> <br>- ExecutionError - 监控器执行失败<br> <br>- Other - 未分类的错误

错误处理指南

何时使用每种模式

场景 方法
跨越域边界 使用自定义错误构造器转换为特定于域的错误类型
同一域内 使用 .with_context() 添加信息,同时保持错误类型
外部 API 边界 始终转换为你的域的错误类型,以避免泄漏实现细节

错误创建示例

创建没有源的配置错误

let error = ConfigError::validation_error(
    "Invalid network configuration",
    None,
    Some(HashMap::from([\
        ("network", "ethereum"),\
        ("field", "rpc_url")\
    ]))
);

创建带有源的配置错误

let io_error = std::io::Error::new(std::io::ErrorKind::Other, "Failed to read file");

let error = ConfigError::validation_error(
    "Invalid network configuration",
    Some(io_error.into()),
    None
);

使用 #[instrument] 进行追踪

##[instrument(skip_all, fields(network = %_network.slug))]
async fn filter_block(
    &self,
    client: &T,
    _network: &Network,
    block: &BlockType,
    monitors: &[Monitor],
) -> Result&lt;Vec&lt;MonitorMatch>, FilterError> {
    tracing::debug!("Processing block {}", block_number);
    // ...
}

关键方面:

  1. skip_all - 跳过函数参数的自动检测以提高性能

  2. fields(…​) - 添加我们要跟踪的特定字段(如网络 slug)

  3. tracing::debug! - 为重要操作添加调试级别的 span

← 自定义脚本

测试 →

  • 原文链接: docs.openzeppelin.com/mo...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
OpenZeppelin
OpenZeppelin
江湖只有他的大名,没有他的介绍。