Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add heap-allocated string to Panic handler

Tags:

rust

I am in the context of a web application, where each request is assigned a unique correlation ID. I am running in a wasm environment with the wasm32-unknown-unknown target. One request is always served by one thread, and the entire environment is torn down afterwards.

I would like to register a panic handler that if a request panics, it also logs this request ID.

This has proven to be difficult, as anything that has to go into the set_hook method needs the 'static lifetime constraint, which a request ID obviously doesn't have.

I would like code along the following lines to compile

   // 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
    }));

Potential solutions

I have a few potential approaches. I am not sure which one is best, or if there are any approaches that I am missing.

1. Leaking the memory

As I know my environment is torn down after each request, one way to get a String moved into the 'static lifetime to leak it is like this

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

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

    request_id

This will work in practice, as the request id is theoretically 'static (as after the request is served, the application is closed) - however it has the disadvantage that if I ever move this code into a non-wasm environment, we'll end up leaking memory pretty quickly.

2. Threadlocal

As I know that each request is served by one thread, I could stuff the request id into a ThreadLocal, and read from that ThreadLocal on panics.

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(),
    }
});

This seems like the "best" solution I can come up with. However I'm not sure what the perf. implications are of initializing a ThreadLocal on each request is, particularly because we panic extremely rarely, so I'd hate to pay a big cost up-front for something I almost never use.

3. Catch_unwind

I experimented with the catch_unwind API, as that seemed like a good choice. I would then wrap the handling of each request with catch_unwind. However it seems like wasm32-unknown-unknown currently doesn't respect catch_unwind


What is the best solution here? Is there any way to get something that's heap-allocated into a Rust panic hook that I'm not aware of?

like image 640
Gustav Wengel Avatar asked Nov 04 '25 10:11

Gustav Wengel


1 Answers

As per your example, you could move the id into the 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

like image 134
Netwave Avatar answered Nov 07 '25 15:11

Netwave



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!