我在一个web应用程序的上下文中,每个请求都被分配一个唯一的关联ID.

我想注册一个panic 处理程序,如果请求panic ,它也会记录这个请求ID.

这已经被证明是困难的,因为任何必须进入set_hook方法的东西都需要'static生存期约束,而请求ID显然没有这个约束.

我希望编译以下代码

   // Assume we have a request here from somewhere.
   let request = get_request_from_framework();
    // This is at the start of the request
    panic::set_hook(Box::new(|info| {
        let request_id = request.get_request_id();

        // Log panic messages here with request_id here
    }));

潜在解决方案

我有一些潜在的方法.我不确定哪一个是最好的,也不确定是否有我遗漏的方法.

1.内存泄漏

据我所知,我的环境在每次请求后都会被 destruct ,一种方法是将字符串移动到'static生命周期中来泄漏它,如下所示

    let request_id = uuid::Uuid::new_v4().to_string();

    let request_id: &'static str = Box::leak(request_id.into_boxed_str());

    request_id

这在实践中是可行的,因为从理论上讲,请求id是"静态的"(在请求被服务之后,应用程序被关闭)——但是它的缺点是,如果我将此代码移动到非wasm环境中,我们会很快泄漏内存.

2.本地

我知道每一个本地线程的请求都是由本地线程提供的.

pub fn create_request_id() -> &'static str {
    let request_id = uuid::Uuid::new_v4().to_string();

    CURRENT_REQUEST_ID.with(|current_request_id| {
        *current_request_id.borrow_mut() = request_id;
    });
}

thread_local! {
    pub static CURRENT_REQUEST_ID: RefCell<String> = RefCell::new(uuid::Uuid::new_v4().to_string());
}


// And then inside the panic handler get the request_id with something like
let request_id = CURRENT_REQUEST_ID.with(|current_request_id| {
    let current_request_id = current_request_id.try_borrow();

    match current_request_id {
        Ok(current_request_id) => current_request_id.clone(),
        Err(err) => "Unknown".to_string(),
    }
});

"这似乎是我能想出的最好的解决办法.".但我不确定性能如何.在每个请求上初始化ThreadLocal的含义是,尤其是因为我们很少panic ,所以我不想为我几乎从未使用过的东西预先支付大量费用.

3.抓住你放松

我try 了catch_unwind API,因为这似乎是一个不错的 Select .然后,我会用catch_unwind来结束对每个请求的处理.然而,它似乎是wasm32-unknown-unknown currently doesn't respect catch_unwind


这里最好的解决方案是什么?有没有办法将堆分配到我不知道的Rust panic 挂钩中的东西?

推荐答案

根据您的示例,您可以将id输入clusure:

// Assume we have a request here from somewhere.
let request = get_request_from_framework();
let request_id = request.get_request_id();
// This is at the start of the request
panic::set_hook(Box::new(move |info| {
    let panic_message = format!("Request {} failed", request_id);
    // Log panic messages here with request_id here
}));

Playground

Rust相关问答推荐

从Rust调用C++虚拟方法即使在成功执行之后也会引发Access违规错误

如何在Tauri中将变量从后端传递到前端

为什么Option类型try块需要类型注释?

Rust移动/复制涉及实际复制时进行检测

不同类型泛型的映射

装箱特性如何影响传递给它的参数的生命周期 ?(举一个非常具体的例子)

.在 Rust 模块标识符中

RUST 中的读写器锁定模式

如何使用tracing-subscriberRust crate 构建多编写者、全局过滤订阅者

面临意外的未对齐指针取消引用:地址必须是 0x8 的倍数,但为 0x__错误

使用在功能标志后面导入的类型,即使未启用功能标志

Rust 中的方法调用有什么区别?

Rust 中函数的类型同义词

为什么指定生命周期让我返回一个引用?

`use std::error::Error` 声明中断编译

覆盖类型的要求到底是什么?为什么单个元素元组满足它?

Rust:如果我知道只有一个实例,那么将可变borrow 转换为指针并返回(以安抚borrow 判断器)是否安全?

为什么这里需要类型注解?

为什么当borrow 变量发生变化时,borrow 变量不会改变?

加入动态数量的期货