Skip to content

Conversation

@asteurer
Copy link

@asteurer asteurer commented Nov 4, 2025

Overview

This is an implementation of WASI OTel which gives guest applications the ability to export traces and metrics (and eventually logs) with OpenTelemetry SDKs.

We see two options for adding this to Spin:

  • Require the user to compile Spin with configuration flags that will enable this feature.
  • Have this feature available out-of-the-box and add an experimental-features flag that the user will pass to spin up.

To help ease adoption, we would prefer to pass a flag to spin up, but we know there was some previous work around this with WASI P3 so we're happy to defer to that process.

Some additional notes:

  • In the root Cargo.toml, you will notice that certain experimental features are being enabled for the opentelemetry-sdk dependency, and that we are using reqwest-client instead of reqwest-blocking-client in the opentelemetry dependency. These are all to prevent runtime errors that come from the default OpenTelemetry dependency configurations conflicting with Spin's async runtime.
  • Currently wasi-otel is a phase 0 proposal but we are presenting it to the WASI subgroup this month in the hopes of moving it to phase 1.

Usage

To try the features added in this PR, do the following:

  1. Build Spin from this branch:
git clone --branch wasi-otel --depth 1 https://github.com/asteurer/spin
cd spin
cargo install --path .
spin plugin update
spin plugin install otel
  1. Clone OpenTelemetry WASI and try the Rust example applications:
git clone https://github.com/calebschoepp/opentelemetry-wasi
spin otel setup

###############
### Tracing ###
###############
cd opentelemetry-wasi/rust/examples/spin-basic
spin build
spin otel up

# In a different terminal window
spin otel open jaeger
curl localhost:3000

################
###  Metrics ###
################
cd opentelemetry-wasi/rust/examples/spin-metrics
spin build
spin otel up

# In a different terminal window
spin otel open prometheus
curl localhost:3000

Signed-off-by: Caleb Schoepp <[email protected]>

fix: update opentelemetry version

Signed-off-by: Andrew Steurer <[email protected]>

Fix out of date Cargo.lock from bad rebase

Signed-off-by: Caleb Schoepp <[email protected]>

feat(factor-otel): adding metrics

Signed-off-by: Andrew Steurer <[email protected]>

feat(factor-otel): refactoring otel conversions

Signed-off-by: Andrew Steurer <[email protected]>

fix(telemetry): update BatchLogProcessor to be async

Signed-off-by: Andrew Steurer <[email protected]>

feat(factor-otel): refactoring WIT and updating conversions

Signed-off-by: Andrew Steurer <[email protected]>

fix(factor-otel): updating WIT

Signed-off-by: Andrew Steurer <[email protected]>

feat(factor-otel): updating WIT and conversions

Signed-off-by: Andrew Steurer <[email protected]>

feat(factor-otel): small refactors

Signed-off-by: Andrew Steurer <[email protected]>

fix(factor-otel): rebasing wasi-otel branch on main

Signed-off-by: Andrew Steurer <[email protected]>
@calebschoepp
Copy link
Collaborator

@lann I think you would likely be the most relevant reviewer for this

@calebschoepp calebschoepp requested review from lann and rylev November 18, 2025 18:24
Copy link
Collaborator

@lann lann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was mostly just a shallow pass here with a few surface-level comments.

Looking at how this code is working, I think there may be a viable approach that wouldn't require changes to the outbound factors. I only have a minute right now but briefly:

  • Use a task-local to manage state through the host-guest-host burger
  • Use an otel span processor that uses that task-local to conditionally reparent spans

}
}

mod otel {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My immediate hot take is that we should break this out into a separate wasi-otel crate. We've been discussing removing this spin-world crate entirely and I think these conversion impls are a good example of why we'd rather split up these import bindings.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a separate crate for wasi-otel; however, the src/lib.rs file is an exact copy of the world/src/lib.rs file. It works; however, I could use some assistance tailoring the bindgen! macro to wasi-otel.

Signed-off-by: Andrew Steurer <[email protected]>
@asteurer asteurer requested a review from lann November 21, 2025 00:05
impl OtelFactor {
pub fn new() -> anyhow::Result<Self> {
// This is a hack b/c we know the version of this crate will be the same as the version of Spin
let spin_version = env!("CARGO_PKG_VERSION").to_string();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably just take the version as an argument to the new constructor and do this 'hack' from inside the actual Spin crate.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I did this correctly. Will you double check and let me know?

/// This MUST only be called from a factor host implementation function that is instrumented.
///
/// This MUST be called at the very start of the function before any awaits.
pub fn reparent_tracing_span(&self) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean the host loses these spans for its own tracing? Ideally we would emit these spans both for host and guest tracing.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@calebschoepp Will you address this when you have a sec?

use opentelemetry_sdk::metrics::exporter::PushMetricExporter;
use opentelemetry_sdk::trace::SpanProcessor;
use tracing_opentelemetry::OpenTelemetrySpanExt;
// TODO: This feels weird. I'm wondering if it can be fixed by wrangling the bindgen macro in `crates/wasi_otel/lib.rs`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: comments like this saying things like "this feels weird" is rarely helpful. It's usually not clear to readers (likely even the author themselves in the future) what feels weird. We should either address this issue now, write a more descriptive comment, or remove the comment entirely.

Copy link
Author

@asteurer asteurer Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this was intended as something to solicit help from reviewers, not to be merged. If it's not something we want to worry about, happy to remove it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This app doesn't seem to do anything specific with wasi. Why do we need this instead of using any of the existing components?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@calebschoepp Will you address this when you have a sec?

http = "0.2"
opentelemetry = "0.30.0"
opentelemetry_sdk = "0.30.0"
opentelemetry-wasi = { git = "https://github.com/calebschoepp/opentelemetry-wasi", rev = "60df119fbc03ed83d0e2cc1300f6a8eb055d23f7" }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the plan is to upstream this back somewhere? Do we know where?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@calebschoepp Will you address this when you have a sec?

Comment on lines +128 to +130
// TODO: Test what happens if start is called but not end
// TODO: Test what happens if end is called but not start
// TODO: What happens if child span outlives parent
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's answer all these questions

asteurer and others added 4 commits November 24, 2025 09:35
Signed-off-by: Andrew Steurer <[email protected]>
Signed-off-by: Andrew Steurer <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants