#tracing #logging #wasm-module

no-std tracing-tunnel

Tunnelling tracing information across API boundary

2 unstable releases

0.2.0-beta.1 Mar 3, 2024
0.1.0 Dec 9, 2022

#105 in Debugging

Download history 160/week @ 2024-02-15 135/week @ 2024-02-22 509/week @ 2024-02-29 323/week @ 2024-03-07 1179/week @ 2024-03-14 2152/week @ 2024-03-21 2366/week @ 2024-03-28 2828/week @ 2024-04-04 2025/week @ 2024-04-11 1643/week @ 2024-04-18 1351/week @ 2024-04-25 1644/week @ 2024-05-02 1731/week @ 2024-05-09 1785/week @ 2024-05-16 1675/week @ 2024-05-23 1573/week @ 2024-05-30

7,137 downloads per month
Used in 2 crates (via tracing-capture)


1.5K SLoC

Tunnelling Tracing Information Across API Boundary

Build Status License: MIT OR Apache-2.0 rust 1.70+ required no_std tested

Documentation: Docs.rs crate docs (main)

This crate provides tracing infrastructure helpers allowing to transfer tracing events across an API boundary:

  • TracingEventSender is a tracing Subscriber that converts tracing events into (de)serializable presentation that can be sent elsewhere using a customizable hook.
  • TracingEventReceiver consumes events produced by a TracingEventSender and relays them to the tracing infrastructure. It is assumed that the source of events may outlive both the lifetime of a particular TracingEventReceiver instance, and the lifetime of the program encapsulating the receiver. To deal with this, the receiver provides the means to persist / restore its state.

This solves the problem of having dynamic call sites for tracing spans / events, i.e., ones not known during compilation. This may occur if call sites are defined in dynamically loaded modules the execution of which is embedded into the program, such as WASM modules.

See the crate docs for the details about the crate design and potential use cases.


Add this to your Crate.toml:

tracing-tunnel = "0.2.0-beta.1"

Note that the both pieces of functionality described above are gated behind opt-in features; consult the crate docs for details.

Sending tracing events

use std::sync::mpsc;
use tracing_tunnel::{TracingEvent, TracingEventSender, TracingEventReceiver};

// Let's collect tracing events using an MPSC channel.
let (events_sx, events_rx) = mpsc::sync_channel(10);
let subscriber = TracingEventSender::new(move |event| {

tracing::subscriber::with_default(subscriber, || {
    tracing::info_span!("test", num = 42_i64).in_scope(|| {
        tracing::warn!("I feel disturbance in the Force...");

let events: Vec<TracingEvent> = events_rx.iter().collect();
// Do something with events...

Receiving tracing events

use std::sync::mpsc;
use tracing_tunnel::{
    LocalSpans, PersistedMetadata, PersistedSpans, TracingEvent, TracingEventReceiver,


fn replay_events(events: &[TracingEvent]) {
    let mut spans = PersistedSpans::default();
    let mut local_spans = LocalSpans::default();
    let mut receiver = TracingEventReceiver::default();
    for event in events {
        if let Err(err) = receiver.try_receive(event.clone()) {
            tracing::warn!(%err, "received invalid tracing event");

    // Persist the resulting receiver state. There are two pieces
    // of the state: metadata and alive spans. The spans are further split
    // into the persisted and local parts.
    let metadata = receiver.persist_metadata();
    let (spans, local_spans) = receiver.persist();
    // Store `metadata` and `spans`, e.g., in a DB, and `local_spans`
    // in a local data struct such as `HashMap` keyed by the executable ID.


Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in tracing-toolbox by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.


~27K SLoC