Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
317 changes: 163 additions & 154 deletions rust/Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ members = ["examples/*"]
[workspace.dependencies]
anyhow = "1"
opentelemetry = "0.30.0"
opentelemetry_sdk = "0.30.0"
opentelemetry_sdk = { version = "0.30.0", features = ["experimental_metrics_custom_reader"] }
spin-sdk = "3.1.0"
tracing = "0.1.41"
tracing-opentelemetry = "0.31.0"
Expand Down
19 changes: 16 additions & 3 deletions rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ Enables using OpenTelemetry within Rust WebAssembly components backed by [WASI O
Build a version of [Spin](https://github.com/spinframework/spin) from this [branch](https://github.com/calebschoepp/spin/tree/wasi-otel) and then run the example of your choosing.

```sh
git clone https://github.com/calebschoepp/spin
git clone --branch wasi-otel --depth 1 https://github.com/calebschoepp/spin
cd spin
git fetch origin wasi-otel
git checkout wasi-otel
cargo install --path .
spin plugin update
spin plugin install otel
Expand All @@ -23,3 +21,18 @@ spin otel setup
spin otel up
curl localhost:3000
```

## Notes about Tracing

## Notes about Metrics

We based the metrics portion of the Rust SDK on [`opentelemetry-prometheus`](https://github.com/open-telemetry/opentelemetry-rust/tree/c811cde1ae21c624870c1b952190e687b16f76b8/opentelemetry-prometheus).

To summarize:
- We initialize a [`ManualReader`](https://github.com/open-telemetry/opentelemetry-rust/blob/c811cde1ae21c624870c1b952190e687b16f76b8/opentelemetry-sdk/src/metrics/manual_reader.rs), and `Arc::clone` the reader into an _**Exporter**_ and a _**Collector**_.
- We pass the _**Exporter**_ into an [`SdkMeterProvider`](https://github.com/open-telemetry/opentelemetry-rust/blob/c811cde1ae21c624870c1b952190e687b16f76b8/opentelemetry-sdk/src/metrics/meter_provider.rs), and when the various instruments associated with the provider are invoked, they will fill the `ManualReader` with metric data.
- When we are ready to pass the metric information from the Wasm guest to the Wasm host, we will use the _**Collector**_ to retrieve a [`ResourceMetrics`](https://github.com/open-telemetry/opentelemetry-rust/blob/c811cde1ae21c624870c1b952190e687b16f76b8/opentelemetry-sdk/src/metrics/data/mod.rs#L13) struct from the `ManualReader` and pass it to the host.

TODO: Add notes about the host implementation

## Notes about Logs
2 changes: 2 additions & 0 deletions rust/examples/spin-metrics/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target/
.spin/
18 changes: 18 additions & 0 deletions rust/examples/spin-metrics/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "spin-metrics"
authors = ["Andrew Steurer <[email protected]>"]
description = ""
version = "0.1.0"
rust-version = "1.78"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
anyhow = { workspace = true }
opentelemetry = { workspace = true }
opentelemetry_sdk = { workspace = true }
opentelemetry-wasi = { path = "../../" }
spin-sdk = { workspace = true }
once_cell = "1.21.3"
18 changes: 18 additions & 0 deletions rust/examples/spin-metrics/spin.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
spin_manifest_version = 2

[application]
name = "spin-metrics"
version = "0.1.0"
authors = ["Andrew Steurer <[email protected]>"]
description = "A Spin application demonstrating the use of OpenTelemetry metrics"

[[trigger.http]]
route = "/..."
component = "spin-metrics"

[component.spin-metrics]
source = "../../target/wasm32-wasip2/release/spin_metrics.wasm"
allowed_outbound_hosts = []
[component.spin-metrics.build]
command = "cargo build --target wasm32-wasip2 --release"
watch = ["src/**/*.rs", "Cargo.toml"]
107 changes: 107 additions & 0 deletions rust/examples/spin-metrics/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use once_cell::sync::Lazy;
use opentelemetry::{
global,
metrics::{Counter, Gauge, Histogram, UpDownCounter},
KeyValue,
};
use opentelemetry_sdk::{
metrics::{ManualReader, SdkMeterProvider},
Resource,
};
use opentelemetry_wasi::WasiMetricCollector;
use spin_sdk::{
http::{IntoResponse, Request, Response},
http_component,
};
use std::sync::Arc;

static READER: Lazy<Arc<ManualReader>> = Lazy::new(|| Arc::new(ManualReader::builder().build()));

static _PROVIDER: Lazy<()> = Lazy::new(|| {
Lazy::force(&READER);
let exporter = opentelemetry_wasi::WasiMetricExporter::new(Arc::clone(&READER));
let provider = SdkMeterProvider::builder()
.with_reader(exporter)
.with_resource(
Resource::builder()
.with_service_name("spin-metrics")
.build(),
)
.build();

global::set_meter_provider(provider);
});

static COLLECTOR: Lazy<WasiMetricCollector> = Lazy::new(|| {
Lazy::force(&READER);
opentelemetry_wasi::WasiMetricCollector::new(Arc::clone(&READER))
});

static COUNTER: Lazy<Counter<u64>> = Lazy::new(|| {
Lazy::force(&_PROVIDER);
global::meter("spin_meter")
.u64_counter("spin_counter")
.build()
});

static UPDOWNCOUNTER: Lazy<UpDownCounter<i64>> = Lazy::new(|| {
Lazy::force(&_PROVIDER);
global::meter("spin_meter")
.i64_up_down_counter("spin_up_down_counter")
.build()
});

static HISTOGRAM: Lazy<Histogram<u64>> = Lazy::new(|| {
Lazy::force(&_PROVIDER);
global::meter("spin_meter")
.u64_histogram("spin_histogram")
.build()
});

static GAUGE: Lazy<Gauge<u64>> = Lazy::new(|| {
Lazy::force(&_PROVIDER);
global::meter("spin_meter").u64_gauge("spin_gauge").build()
});

#[http_component]
fn handle_spin_metrics(_req: Request) -> anyhow::Result<impl IntoResponse> {
COUNTER.add(
10,
&[
KeyValue::new("counterkey1", "countervalue1"),
KeyValue::new("counterkey2", "countervalue2"),
],
);

UPDOWNCOUNTER.add(
-1,
&[
KeyValue::new("updowncounterkey1", "updowncountervalue1"),
KeyValue::new("updowncounterkey2", "updowncountervalue2"),
],
);

HISTOGRAM.record(
9,
&[
KeyValue::new("histogramkey1", "histogramvalue1"),
KeyValue::new("histogramkey2", "histogramvalue2"),
],
);

GAUGE.record(
8,
&[
KeyValue::new("gaugekey1", "gaugevalue1"),
KeyValue::new("gaugekey2", "gaugevalue2"),
],
);

COLLECTOR.collect()?;

Ok(Response::builder()
.status(200)
.header("content-type", "text/plain")
.body("Hello, World!")
.build())
}
2 changes: 2 additions & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
mod metrics;
mod tracing;
mod types;

pub use metrics::*;
pub use tracing::*;

#[doc(hidden)]
Expand Down
5 changes: 5 additions & 0 deletions rust/src/metrics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod conversion;
mod exporter;

pub use exporter::WasiMetricCollector;
pub use exporter::WasiMetricExporter;
Loading