Skip to main content

Send Telemetry to Honeycomb from WebAssembly in Rust

Observability in Honeycomb from WebAssembly


Enables the extraction of traces/spans from Wasm modules executing in a Golang program/application and emits them to


The Rust Observe SDK requires a tokio runtime. If your application already uses tokio, you don't need to make any modifications. If not, you can run the async SDK functions inside a tokio::runtime::Runtime that you initialize yourself.


Install the dylibso-observe-sdk crate from GitHub:

# ...
# consider pinning to a commit instead of the `main` branch, using: commit = "..."
dylibso-observe-sdk = { git = "", branch = "main" }


A basic Rust program that loads a Wasm module and executes it using the Wasmtime WebAssembly runtime.

use dylibso_observe_sdk::adapter::honeycomb::{
AdapterMetadata, Attribute, HoneycombAdapter, HoneycombConfig, Options, SpanFilter, Value,

pub async fn main() -> anyhow::Result<()> {
let args: Vec<_> = std::env::args().skip(1).collect();
let data = std::fs::read(&args[0])?;
let function_name = "_start";
let config = wasmtime::Config::new();

// Create instance
let engine = wasmtime::Engine::new(&config)?;
let module = wasmtime::Module::new(&engine, &data)?;

// Create a new instance of the Honeycomb adapter with custom configuration
let config = HoneycombConfig {
api_key: String::from(std::env::var("HONEYCOMB_API_KEY")?),
host: String::from(""),
dataset: String::from("rust"),
let adapter = HoneycombAdapter::create(config);

// Setup WASI
let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new()

let mut store = wasmtime::Store::new(&engine, wasi_ctx);
let mut linker = wasmtime::Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |wasi| wasi)?;

// create an optional filter that instructs the adapter to throw away any
// spans below a configured threshold
let options = Options {
span_filter: SpanFilter {
min_duration_microseconds: std::time::Duration::from_micros(20),

// Provide the observability functions to the `Linker` to be made available
// to the instrumented guest code. These are safe to add and are a no-op
// if guest code is uninstrumented.
let trace_ctx = adapter.start(&mut linker, &data, options)?;

let instance = linker.instantiate(&mut store, &module)?;

// get the function and run it, the events pop into the queue
// as the function is running
let f = instance
.get_func(&mut store, function_name)
.expect("function exists"); store, &[], &mut []).unwrap();

// (optional) set metadata onto the trace
let meta: Vec<Attribute> = vec![
Attribute {
key: "http.url".into(),
value: Value {
string_value: Some("".into()),
int_value: None,
Attribute {
key: "http.client_ip".into(),
value: Value {
string_value: Some("".into()),
int_value: None,
Attribute {
key: "http.status_code".into(),
value: Value {
string_value: None,
int_value: Some(200),

// associate additional metadata with the trace using the Otel format

// stop the trace


Adapter Configuration

You may modify the behavior of your adapter by passing in a configuration when initializing the adapter. A configuration has the following fields:

let config = HoneycombConfig{
// the URL of your OpenTelemetry collector
host: String::from(""),
// your Honeycomb API key
api_key: String::from(std::env::var("HONEYCOMB_API_KEY")?),
// the dataset to group your observability data under
dataset: String::from("rust"),
let adapter = HoneycombAdapter::create(config);