diff --git a/Cargo.lock b/Cargo.lock index af7d625080..61b7daa89f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -625,7 +625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.4.5", "axum-macros", "bytes", "futures-util", @@ -635,7 +635,7 @@ dependencies = [ "hyper 1.6.0", "hyper-util", "itoa", - "matchit", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", @@ -653,13 +653,38 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +dependencies = [ + "axum-core 0.5.6", + "bytes", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "itoa", + "matchit 0.8.4", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "sync_wrapper 1.0.2", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-client-ip" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eefda7e2b27e1bda4d6fa8a06b50803b8793769045918bc37ad062d48a6efac" dependencies = [ - "axum", + "axum 0.7.9", "forwarded-header-value", "serde", ] @@ -685,14 +710,32 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-extra" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04" dependencies = [ - "axum", - "axum-core", + "axum 0.7.9", + "axum-core 0.4.5", "bytes", "fastrand", "futures-util", @@ -729,7 +772,7 @@ dependencies = [ "anyhow", "assert-json-diff", "auto-future", - "axum", + "axum 0.7.9", "bytes", "bytesize", "cookie", @@ -4814,6 +4857,12 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "md-5" version = "0.10.6" @@ -5269,7 +5318,7 @@ version = "1.1.73" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.9", "axum-test", "bincode", "bip39", @@ -5477,6 +5526,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", + "tonic 0.14.4", "tracing", "tracing-opentelemetry", "tracing-subscriber", @@ -5905,7 +5955,7 @@ name = "nym-credential-proxy" version = "0.3.0" dependencies = [ "anyhow", - "axum", + "axum 0.7.9", "bip39", "bs58", "cfg-if", @@ -5954,7 +6004,7 @@ name = "nym-credential-proxy-lib" version = "1.20.4" dependencies = [ "anyhow", - "axum", + "axum 0.7.9", "bip39", "bs58", "futures", @@ -6163,7 +6213,7 @@ version = "1.0.1" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.9", "blake3", "chrono", "clap", @@ -6593,7 +6643,7 @@ dependencies = [ name = "nym-http-api-common" version = "1.20.4" dependencies = [ - "axum", + "axum 0.7.9", "axum-client-ip", "bincode", "bytes", @@ -6973,7 +7023,7 @@ name = "nym-network-monitor" version = "1.0.2" dependencies = [ "anyhow", - "axum", + "axum 0.7.9", "clap", "dashmap", "futures", @@ -7061,7 +7111,7 @@ dependencies = [ "arc-swap", "arrayref", "async-trait", - "axum", + "axum 0.7.9", "bip39", "blake2 0.8.1", "bloomfilter", @@ -7210,7 +7260,7 @@ version = "4.1.0" dependencies = [ "ammonia", "anyhow", - "axum", + "axum 0.7.9", "axum-test", "bip39", "bs58", @@ -7877,7 +7927,7 @@ name = "nym-statistics-api" version = "0.3.1" dependencies = [ "anyhow", - "axum", + "axum 0.7.9", "axum-client-ip", "axum-extra", "celes", @@ -8358,7 +8408,7 @@ version = "1.20.4" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.9", "futures", "nym-credential-verification", "nym-credentials-interface", @@ -8376,7 +8426,7 @@ dependencies = [ name = "nym-wireguard-private-metadata-shared" version = "1.20.4" dependencies = [ - "axum", + "axum 0.7.9", "bincode", "nym-credentials-interface", "schemars 0.8.22", @@ -8390,7 +8440,7 @@ name = "nym-wireguard-private-metadata-tests" version = "1.20.4" dependencies = [ "async-trait", - "axum", + "axum 0.7.9", "futures", "nym-credential-verification", "nym-credentials-interface", @@ -8457,7 +8507,7 @@ version = "0.1.15" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.9", "chrono", "clap", "nym-bin-common", @@ -11834,7 +11884,7 @@ checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", - "axum", + "axum 0.7.9", "base64 0.22.1", "bytes", "h2 0.4.11", @@ -11891,8 +11941,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f32a6f80051a4111560201420c7885d0082ba9efe2ab61875c587bb6b18b9a0" dependencies = [ "async-trait", + "axum 0.8.8", "base64 0.22.1", "bytes", + "h2 0.4.11", "http 1.3.1", "http-body 1.0.1", "http-body-util", @@ -11901,8 +11953,11 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", + "rustls-native-certs 0.8.3", + "socket2 0.6.0", "sync_wrapper 1.0.2", "tokio", + "tokio-rustls 0.26.2", "tokio-stream", "tower 0.5.2", "tower-layer", @@ -12697,7 +12752,7 @@ version = "8.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db4b5ac679cc6dfc5ea3f2823b0291c777750ffd5e13b21137e0f7ac0e8f9617" dependencies = [ - "axum", + "axum 0.7.9", "base64 0.22.1", "mime_guess", "regex", diff --git a/Cargo.toml b/Cargo.toml index 89f1add4c1..99451a3484 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -312,6 +312,7 @@ once_cell = "1.21.3" opentelemetry = "0.31.0" opentelemetry_sdk = "0.31.0" opentelemetry-otlp = "0.31.0" +tonic = "0.14.4" parking_lot = "0.12.3" pem = "0.8" petgraph = "0.6.5" diff --git a/common/bin-common/Cargo.toml b/common/bin-common/Cargo.toml index 3a69311369..b10a29b84c 100644 --- a/common/bin-common/Cargo.toml +++ b/common/bin-common/Cargo.toml @@ -26,7 +26,8 @@ opentelemetry = { workspace = true, features = ["trace"], optional = true } ## otel-otlp (modern OTLP export to SigNoz/any OTLP collector) opentelemetry_sdk = { workspace = true, features = ["trace"], optional = true } -opentelemetry-otlp = { workspace = true, features = ["grpc-tonic", "trace"], optional = true } +opentelemetry-otlp = { workspace = true, features = ["grpc-tonic", "trace", "tls-roots"], optional = true } +tonic = { workspace = true, optional = true } [build-dependencies] @@ -44,6 +45,7 @@ otel-otlp = [ "dep:opentelemetry_sdk", "dep:opentelemetry-otlp", "dep:tracing-opentelemetry", + "dep:tonic", ] clap = ["dep:clap", "dep:clap_complete", "dep:clap_complete_fig"] models = [] diff --git a/common/bin-common/src/logging/mod.rs b/common/bin-common/src/logging/mod.rs index 9b13d32628..56815b93a5 100644 --- a/common/bin-common/src/logging/mod.rs +++ b/common/bin-common/src/logging/mod.rs @@ -72,11 +72,15 @@ pub fn setup_tracing_logger() { /// /// # Arguments /// * `service_name` - The service name reported to the collector (e.g. "nym-node") -/// * `endpoint` - The OTLP/gRPC collector endpoint (e.g. "http://localhost:4317") +/// * `endpoint` - The OTLP/gRPC collector endpoint (e.g. "http://localhost:4317" +/// or "https://ingest.eu.signoz.cloud:443" for SigNoz Cloud) +/// * `ingestion_key` - Optional SigNoz Cloud ingestion key. When provided, it is +/// sent as the `signoz-ingestion-key` gRPC metadata header on every export. #[cfg(feature = "otel-otlp")] pub fn init_otel_layer( service_name: &str, endpoint: &str, + ingestion_key: Option<&str>, ) -> Result< ( tracing_opentelemetry::OpenTelemetryLayer, @@ -90,10 +94,21 @@ where use opentelemetry::trace::TracerProvider as _; use opentelemetry_otlp::WithExportConfig; - let exporter = opentelemetry_otlp::SpanExporter::builder() + let mut builder = opentelemetry_otlp::SpanExporter::builder() .with_tonic() - .with_endpoint(endpoint) - .build()?; + .with_endpoint(endpoint); + + if let Some(key) = ingestion_key { + use opentelemetry_otlp::WithTonicConfig; + let mut metadata = tonic::metadata::MetadataMap::new(); + metadata.insert( + "signoz-ingestion-key", + key.parse().map_err(|_| "invalid ingestion key value")?, + ); + builder = builder.with_metadata(metadata); + } + + let exporter = builder.build()?; let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder() .with_batch_exporter(exporter) diff --git a/nym-node/src/cli/mod.rs b/nym-node/src/cli/mod.rs index 837003bcc0..175b3022f5 100644 --- a/nym-node/src/cli/mod.rs +++ b/nym-node/src/cli/mod.rs @@ -46,6 +46,7 @@ pub(crate) struct Cli { /// OpenTelemetry OTLP collector endpoint (gRPC). /// Only used when --otel is enabled. + /// For SigNoz Cloud use https://ingest..signoz.cloud:443 #[clap( long, env = "NYMNODE_OTEL_ENDPOINT", @@ -53,6 +54,11 @@ pub(crate) struct Cli { )] pub(crate) otel_endpoint: String, + /// SigNoz Cloud ingestion key for authenticated OTLP export. + /// Only needed for SigNoz Cloud (not self-hosted). + #[clap(long, env = "NYMNODE_OTEL_KEY")] + pub(crate) otel_key: Option, + #[clap(subcommand)] command: Commands, } @@ -114,6 +120,7 @@ impl Cli { Some(crate::logging::OtelConfig { endpoint: self.otel_endpoint.clone(), service_name: "nym-node".to_string(), + ingestion_key: self.otel_key.clone(), }) } else { None @@ -130,6 +137,7 @@ impl Cli { Some(crate::logging::OtelConfig { endpoint: self.otel_endpoint.clone(), service_name: "nym-node".to_string(), + ingestion_key: self.otel_key.clone(), }) } else { None diff --git a/nym-node/src/logging.rs b/nym-node/src/logging.rs index f35ac8e2a9..c36e1d776c 100644 --- a/nym-node/src/logging.rs +++ b/nym-node/src/logging.rs @@ -13,6 +13,7 @@ use tracing_subscriber::{EnvFilter, Layer}; pub(crate) struct OtelConfig { pub endpoint: String, pub service_name: String, + pub ingestion_key: Option, } /// Handle returned when OTel is active so the caller can trigger a graceful shutdown. @@ -55,6 +56,7 @@ pub(crate) fn setup_tracing_logger( let (otel_layer, provider) = nym_bin_common::logging::init_otel_layer( &otel_config.service_name, &otel_config.endpoint, + otel_config.ingestion_key.as_deref(), ).map_err(|e| anyhow::anyhow!("failed to initialise OpenTelemetry: {e}"))?; tracing_subscriber::registry() @@ -77,6 +79,7 @@ pub(crate) fn setup_tracing_logger( let (otel_layer, provider) = nym_bin_common::logging::init_otel_layer( &otel_config.service_name, &otel_config.endpoint, + otel_config.ingestion_key.as_deref(), ).map_err(|e| anyhow::anyhow!("failed to initialise OpenTelemetry: {e}"))?; tracing_subscriber::registry()