diff --git a/Cargo.lock b/Cargo.lock index f0a66d580..9203795a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -490,6 +490,24 @@ dependencies = [ "syn 2.0.32", ] +[[package]] +name = "axum-tracing-opentelemetry" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96874d8776ed9834dbb02c234a51f4f439529cd833bc3ccc4bfbbe6d1821d0d2" +dependencies = [ + "axum", + "futures-core", + "futures-util", + "http", + "opentelemetry", + "pin-project-lite", + "tower", + "tracing", + "tracing-opentelemetry", + "tracing-opentelemetry-instrumentation-sdk", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -1007,6 +1025,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-epoch" version = "0.9.15" @@ -1022,12 +1049,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crunchy" @@ -3617,6 +3641,7 @@ dependencies = [ "async-stream", "async-trait", "axum", + "axum-tracing-opentelemetry", "bytes", "cid", "iroh-car", @@ -3714,6 +3739,7 @@ dependencies = [ "anyhow", "async-trait", "axum", + "axum-tracing-opentelemetry", "cid", "clap", "futures", @@ -3912,12 +3938,69 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "opentelemetry" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" +dependencies = [ + "futures-core", + "futures-sink", + "indexmap 2.0.0", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", + "urlencoding", +] + +[[package]] +name = "opentelemetry-http" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f51189ce8be654f9b5f7e70e49967ed894e84a06fc35c6c042e64ac1fc5399e" +dependencies = [ + "async-trait", + "bytes", + "http", + "opentelemetry", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f16aec8a98a457a52664d69e0091bac3a0abd18ead9b641cb00202ba4e0efe4" +dependencies = [ + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "once_cell", + "opentelemetry", + "ordered-float", + "percent-encoding", + "rand", + "thiserror", +] + [[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" +dependencies = [ + "num-traits", +] + [[package]] name = "overload" version = "0.1.1" @@ -5771,6 +5854,37 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-opentelemetry" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c67ac25c5407e7b961fafc6f7e9aa5958fd297aada2d20fa2ae1737357e55596" +dependencies = [ + "js-sys", + "once_cell", + "opentelemetry", + "opentelemetry_sdk", + "smallvec", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", + "web-time", +] + +[[package]] +name = "tracing-opentelemetry-instrumentation-sdk" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b16403065e0fb78b4708ed0e7245024a5e2eaeeed043dcadba4f32af327a397" +dependencies = [ + "http", + "opentelemetry", + "opentelemetry-http", + "tracing", + "tracing-opentelemetry", +] + [[package]] name = "tracing-serde" version = "0.1.3" @@ -6062,6 +6176,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" @@ -6307,6 +6427,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki" version = "0.21.4" diff --git a/Cargo.toml b/Cargo.toml index 4e14e7769..3af955692 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ anyhow = { version = "1" } async-recursion = { version = "1" } async-stream = { version = "0.3" } axum = { version = "^0.6.18" } +axum-tracing-opentelemetry = { version = "0.15.0" } base64 = { version = "^0.21" } byteorder = { version = "~1.4" } # keep in sync with pinned libipld-* crates bytes = { version = "^1" } diff --git a/rust/noosphere-cli/Cargo.toml b/rust/noosphere-cli/Cargo.toml index 99c1dc029..ad1592918 100644 --- a/rust/noosphere-cli/Cargo.toml +++ b/rust/noosphere-cli/Cargo.toml @@ -17,9 +17,10 @@ homepage = "https://github.com/subconsciousnetwork/noosphere" readme = "README.md" [features] -default = [] +default = ["observability"] helpers = ["tracing-subscriber", "noosphere-ns"] rocksdb = ["noosphere/rocksdb"] +observability = ["noosphere-gateway/observability"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/rust/noosphere-gateway/Cargo.toml b/rust/noosphere-gateway/Cargo.toml index 8f32f4059..a9539c58a 100644 --- a/rust/noosphere-gateway/Cargo.toml +++ b/rust/noosphere-gateway/Cargo.toml @@ -16,7 +16,9 @@ homepage = "https://github.com/subconsciousnetwork/noosphere" readme = "README.md" [features] +default = [] test-kubo = [] +observability = ["axum-tracing-opentelemetry"] [dependencies] tracing = { workspace = true } @@ -29,6 +31,7 @@ noosphere-core = { version = "0.18.0", path = "../noosphere-core", features = [" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] anyhow = { workspace = true } axum = { workspace = true, features = ["headers", "macros"] } +axum-tracing-opentelemetry = { workspace = true, optional = true } iroh-car = { workspace = true } thiserror = { workspace = true } strum = { workspace = true } diff --git a/rust/noosphere-gateway/src/gateway.rs b/rust/noosphere-gateway/src/gateway.rs index d37c5f70f..7d82d444d 100644 --- a/rust/noosphere-gateway/src/gateway.rs +++ b/rust/noosphere-gateway/src/gateway.rs @@ -1,3 +1,11 @@ +use crate::GatewayManager; +use crate::{ + handlers, + worker::{ + start_cleanup, start_ipfs_syndication, start_name_system, NameSystemConfiguration, + NameSystemConnectionType, + }, +}; use anyhow::Result; use axum::extract::DefaultBodyLimit; use axum::http::{HeaderValue, Method}; @@ -14,14 +22,8 @@ use tower_http::cors::{Any, CorsLayer}; use tower_http::trace::TraceLayer; use url::Url; -use crate::GatewayManager; -use crate::{ - handlers, - worker::{ - start_cleanup, start_ipfs_syndication, start_name_system, NameSystemConfiguration, - NameSystemConnectionType, - }, -}; +#[cfg(feature = "observability")] +use axum_tracing_opentelemetry::middleware::{OtelAxumLayer, OtelInResponseLayer}; const DEFAULT_BODY_LENGTH_LIMIT: usize = 100 /* MB */ * 1000 * 1000; @@ -112,7 +114,15 @@ impl Gateway { .layer(Extension(name_system_tx)) .layer(Extension(cleanup_tx)) .layer(DefaultBodyLimit::max(DEFAULT_BODY_LENGTH_LIMIT)) - .layer(cors) + .layer(cors); + + #[cfg(feature = "observability")] + let app = { + app.layer(OtelInResponseLayer) // include trace context in response + .layer(OtelAxumLayer::default()) // initialize otel trace on incoming request + }; + + let app = app .layer(TraceLayer::new_for_http()) .with_state(Arc::new(manager)); diff --git a/rust/noosphere-ns/Cargo.toml b/rust/noosphere-ns/Cargo.toml index 0f7ec6d7d..3d91cd415 100644 --- a/rust/noosphere-ns/Cargo.toml +++ b/rust/noosphere-ns/Cargo.toml @@ -51,6 +51,7 @@ toml = { version = "~0.8", optional = true } # noosphere_ns::server axum = { workspace = true, features = ["json", "headers", "macros"], optional = true } +axum-tracing-opentelemetry = { workspace = true, optional = true } reqwest = { version = "~0.11", default-features = false, features = ["json", "rustls-tls"], optional = true } tower-http = { workspace = true, features = ["trace"], optional = true } url = { version = "^2", features = [ "serde" ], optional = true } @@ -63,9 +64,10 @@ libipld-cbor = { workspace = true } tempfile = { workspace = true } [features] -default = ["orb-ns", "api-server"] +default = ["orb-ns", "api-server", "observability"] api-server = ["axum", "reqwest", "url", "tower-http"] orb-ns = ["clap", "noosphere", "home", "toml", "noosphere-ipfs"] +observability = ["axum-tracing-opentelemetry"] [[bin]] name = "orb-ns" diff --git a/rust/noosphere-ns/src/server/implementation.rs b/rust/noosphere-ns/src/server/implementation.rs index a14d67caa..f58f2ce35 100644 --- a/rust/noosphere-ns/src/server/implementation.rs +++ b/rust/noosphere-ns/src/server/implementation.rs @@ -7,6 +7,9 @@ use std::net::TcpListener; use std::sync::Arc; use tower_http::trace::TraceLayer; +#[cfg(feature = "observability")] +use axum_tracing_opentelemetry::middleware::{OtelAxumLayer, OtelInResponseLayer}; + pub async fn start_name_system_api_server( ns: Arc, listener: TcpListener, @@ -29,9 +32,17 @@ pub async fn start_name_system_api_server( .route(&Route::Address.to_string(), get(handlers::get_address)) .route(&Route::GetRecord.to_string(), get(handlers::get_record)) .route(&Route::PostRecord.to_string(), post(handlers::post_record)) - .route(&Route::Bootstrap.to_string(), post(handlers::bootstrap)) - .with_state(handlers::RouterState { ns, peer_id }) - .layer(TraceLayer::new_for_http()); + .route(&Route::Bootstrap.to_string(), post(handlers::bootstrap)); + + #[cfg(feature = "observability")] + let app = { + app.layer(OtelInResponseLayer) // include trace context in response + .layer(OtelAxumLayer::default()) // initialize otel trace on incoming request + }; + + let app = app + .layer(TraceLayer::new_for_http()) + .with_state(handlers::RouterState { ns, peer_id }); Server::from_tcp(listener)? .serve(app.into_make_service())