diff --git a/Cargo.lock b/Cargo.lock index bf1f68c6..6d8cd186 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -454,6 +454,22 @@ dependencies = [ "version_check 0.9.3", ] +[[package]] +name = "core-foundation" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "cpufeatures" version = "0.2.1" @@ -734,6 +750,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encoding_rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -1167,6 +1192,21 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" +dependencies = [ + "futures-util", + "hyper", + "log", + "rustls", + "tokio", + "tokio-rustls", + "webpki", +] + [[package]] name = "hyper-timeout" version = "0.4.1" @@ -1257,6 +1297,18 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "integer-encoding" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48dc51180a9b377fd75814d0cc02199c20f8e99433d6762f650d39cdbbd3b56f" + +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + [[package]] name = "iri-string" version = "0.4.0" @@ -1398,8 +1450,11 @@ dependencies = [ "mas-core", "opentelemetry", "opentelemetry-http", + "opentelemetry-jaeger", "opentelemetry-otlp", "opentelemetry-semantic-conventions", + "opentelemetry-zipkin", + "reqwest", "schemars", "serde_yaml", "tokio", @@ -1746,6 +1801,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl-probe" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" + [[package]] name = "opentelemetry" version = "0.16.0" @@ -1778,6 +1839,26 @@ dependencies = [ "futures-util", "http", "opentelemetry", + "reqwest", +] + +[[package]] +name = "opentelemetry-jaeger" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db22f492873ea037bc267b35a0e8e4fb846340058cb7c864efe3d0bf23684593" +dependencies = [ + "async-trait", + "headers", + "http", + "lazy_static", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-semantic-conventions", + "reqwest", + "thiserror", + "thrift", + "tokio", ] [[package]] @@ -1806,6 +1887,34 @@ dependencies = [ "opentelemetry", ] +[[package]] +name = "opentelemetry-zipkin" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d12766fd02acef686cf2f97b2fb6cbd9ba78313e051d3c4cca9591d5c947ed" +dependencies = [ + "async-trait", + "http", + "lazy_static", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-semantic-conventions", + "reqwest", + "serde", + "serde_json", + "thiserror", + "typed-builder", +] + +[[package]] +name = "ordered-float" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +dependencies = [ + "num-traits", +] + [[package]] name = "os_str_bytes" version = "3.1.0" @@ -2312,6 +2421,43 @@ dependencies = [ "winapi", ] +[[package]] +name = "reqwest" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51c732d463dd300362ffb44b7b125f299c23d2990411a4253824630ebc7467fb" +dependencies = [ + "base64 0.13.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-native-certs", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + [[package]] name = "ring" version = "0.16.20" @@ -2369,6 +2515,18 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustls-native-certs" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" +dependencies = [ + "openssl-probe", + "rustls", + "schannel", + "security-framework", +] + [[package]] name = "rustversion" version = "1.0.5" @@ -2396,6 +2554,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + [[package]] name = "schemars" version = "0.8.6" @@ -2444,6 +2612,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "0.9.0" @@ -3001,6 +3192,28 @@ dependencies = [ "once_cell", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "thrift" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6d965454947cc7266d22716ebfd07b18d84ebaf35eec558586bbb2a8cb6b5b" +dependencies = [ + "byteorder", + "integer-encoding", + "log", + "ordered-float", + "threadpool", +] + [[package]] name = "time" version = "0.1.44" @@ -3389,6 +3602,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "typed-builder" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a46ee5bd706ff79131be9c94e7edcb82b703c487766a114434e5790361cf08c5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "typenum" version = "1.14.0" @@ -3654,6 +3878,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.78" @@ -3764,6 +4000,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] + [[package]] name = "wyz" version = "0.2.0" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 4a86c832..aa7d2ea3 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -10,8 +10,6 @@ tokio = { version = "1.12.0", features = ["full"] } futures = "0.3.17" anyhow = "1.0.44" clap = "3.0.0-beta.4" -tracing = "0.1.29" -tracing-subscriber = "0.2.25" dotenv = "0.15.0" schemars = { version = "0.8.6", features = ["url", "chrono"] } tower = { version = "0.4.8", features = ["full"] } @@ -19,21 +17,31 @@ tower-http = { version = "0.1.1", features = ["full"] } hyper = { version = "0.14.13", features = ["full"] } serde_yaml = "0.8.21" warp = "0.3.1" -argon2 = { version = "0.3.1", features = ["password-hash"] } -opentelemetry = { version = "0.16.0", features = ["trace", "metrics", "rt-tokio"] } -opentelemetry-otlp = { version = "0.9.0", features = ["trace", "metrics"], optional = true } -opentelemetry-semantic-conventions = "0.8.0" -tracing-opentelemetry = "0.15.0" url = "2.2.2" +argon2 = { version = "0.3.1", features = ["password-hash"] } +reqwest = { version = "0.11.5", features = ["rustls-tls"], default-features = false, optional = true } + +tracing = "0.1.29" +tracing-subscriber = "0.2.25" +tracing-opentelemetry = "0.15.0" +opentelemetry = { version = "0.16.0", features = ["trace", "metrics", "rt-tokio"] } +opentelemetry-http = "0.5.0" +opentelemetry-semantic-conventions = "0.8.0" +opentelemetry-jaeger = { version = "0.15.0", features = ["rt-tokio", "reqwest_collector_client"], optional = true } +opentelemetry-otlp = { version = "0.9.0", features = ["trace", "metrics"], optional = true } +opentelemetry-zipkin = { version = "0.14.0", features = ["reqwest-client", "reqwest-rustls"], default-features = false, optional = true } mas-config = { path = "../config" } mas-core = { path = "../core" } -opentelemetry-http = "0.5.0" [dev-dependencies] indoc = "1.0.3" [features] -default = ["otlp"] -# Enable Opentelemetry OTLP exporter. Requires "protoc" +default = ["otlp", "jaeger", "zipkin"] +# Enable OpenTelemetry OTLP exporter. Requires "protoc" otlp = ["opentelemetry-otlp"] +# Enable OpenTelemetry Jaeger exporter and propagator. +jaeger = ["opentelemetry-jaeger", "reqwest"] +# Enable OpenTelemetry Zipkin exporter and B3 propagator. +zipkin = ["opentelemetry-zipkin", "reqwest"] diff --git a/crates/cli/src/telemetry.rs b/crates/cli/src/telemetry.rs index 25ffa230..40f67e88 100644 --- a/crates/cli/src/telemetry.rs +++ b/crates/cli/src/telemetry.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::time::Duration; +use std::{net::SocketAddr, time::Duration}; use anyhow::bail; use futures::stream::{Stream, StreamExt}; @@ -27,7 +27,12 @@ use opentelemetry::{ Resource, }, }; +#[cfg(feature = "jaeger")] +use opentelemetry_jaeger::Propagator as JaegerPropagator; use opentelemetry_semantic_conventions as semcov; +#[cfg(feature = "zipkin")] +use opentelemetry_zipkin::{B3Encoding, Propagator as ZipkinPropagator}; +use url::Url; pub fn setup(config: &TelemetryConfig) -> anyhow::Result> { global::set_error_handler(|e| tracing::error!("{}", e))?; @@ -53,6 +58,20 @@ fn match_propagator( match propagator { Propagator::TraceContext => Ok(Box::new(TraceContextPropagator::new())), Propagator::Baggage => Ok(Box::new(BaggagePropagator::new())), + + #[cfg(feature = "jaeger")] + Propagator::Jaeger => Ok(Box::new(JaegerPropagator::new())), + + #[cfg(feature = "zipkin")] + Propagator::B3 => Ok(Box::new(ZipkinPropagator::with_encoding( + B3Encoding::SingleHeader, + ))), + + #[cfg(feature = "zipkin")] + Propagator::B3Multi => Ok(Box::new(ZipkinPropagator::with_encoding( + B3Encoding::MultipleHeader, + ))), + p => bail!( "The service was compiled without support for the {:?} propagator, but config uses it.", p @@ -67,8 +86,15 @@ fn propagator(propagators: &[Propagator]) -> anyhow::Result Tracer { + sdk::export::trace::stdout::new_pipeline() + .with_pretty_print(true) + .with_trace_config(trace_config()) + .install_simple() +} + #[cfg(feature = "otlp")] -fn otlp_tracer(endpoint: &Option) -> anyhow::Result { +fn otlp_tracer(endpoint: &Option) -> anyhow::Result { use opentelemetry_otlp::WithExportConfig; let mut exporter = opentelemetry_otlp::new_exporter().tonic(); @@ -86,15 +112,52 @@ fn otlp_tracer(endpoint: &Option) -> anyhow::Result { } #[cfg(not(feature = "otlp"))] -fn otlp_tracer(_endpoint: &Option) -> anyhow::Result { +fn otlp_tracer(_endpoint: &Option) -> anyhow::Result { anyhow::bail!("The service was compiled without OTLP exporter support, but config exports traces via OTLP.") } -fn stdout_tracer() -> Tracer { - sdk::export::trace::stdout::new_pipeline() - .with_pretty_print(true) - .with_trace_config(trace_config()) - .install_simple() +#[cfg(not(feature = "jaeger"))] +fn jaeger_tracer(_agent_endpoint: &Option) -> anyhow::Result { + anyhow::bail!("The service was compiled without Jaeger exporter support, but config exports traces via Jaeger.") +} + +#[cfg(feature = "jaeger")] +fn jaeger_tracer(agent_endpoint: &Option) -> anyhow::Result { + // TODO: also support exporting to a Jaeger collector & skip the agent + let mut pipeline = opentelemetry_jaeger::new_pipeline() + .with_service_name(env!("CARGO_PKG_NAME")) + .with_trace_config(trace_config()); + + if let Some(agent_endpoint) = agent_endpoint { + pipeline = pipeline.with_agent_endpoint(agent_endpoint); + } + + let tracer = pipeline.install_batch(opentelemetry::runtime::Tokio)?; + + Ok(tracer) +} + +#[cfg(not(feature = "zipkin"))] +fn zipkin_tracer(_collector_endpoint: &Option) -> anyhow::Result { + anyhow::bail!("The service was compiled without Jaeger exporter support, but config exports traces via Jaeger.") +} + +#[cfg(feature = "zipkin")] +fn zipkin_tracer(collector_endpoint: &Option) -> anyhow::Result { + let http_client = reqwest::Client::new(); + + let mut pipeline = opentelemetry_zipkin::new_pipeline() + .with_http_client(http_client) + .with_service_name(env!("CARGO_PKG_NAME")) + .with_trace_config(trace_config()); + + if let Some(collector_endpoint) = collector_endpoint { + pipeline = pipeline.with_collector_endpoint(collector_endpoint.to_string()); + } + + let tracer = pipeline.install_batch(opentelemetry::runtime::Tokio)?; + + Ok(tracer) } fn tracer(config: &TracingExporterConfig) -> anyhow::Result> { @@ -102,6 +165,8 @@ fn tracer(config: &TracingExporterConfig) -> anyhow::Result> { TracingExporterConfig::None => return Ok(None), TracingExporterConfig::Stdout => stdout_tracer(), TracingExporterConfig::Otlp { endpoint } => otlp_tracer(endpoint)?, + TracingExporterConfig::Jaeger { agent_endpoint } => jaeger_tracer(agent_endpoint)?, + TracingExporterConfig::Zipkin { collector_endpoint } => zipkin_tracer(collector_endpoint)?, }; Ok(Some(tracer)) diff --git a/crates/config/src/telemetry.rs b/crates/config/src/telemetry.rs index 00596775..2b1a41b1 100644 --- a/crates/config/src/telemetry.rs +++ b/crates/config/src/telemetry.rs @@ -12,10 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::net::SocketAddr; + use async_trait::async_trait; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; +use url::Url; use super::ConfigurationSection; @@ -38,7 +41,15 @@ pub enum TracingExporterConfig { Stdout, Otlp { #[serde(default)] - endpoint: Option, + endpoint: Option, + }, + Jaeger { + #[serde(default)] + agent_endpoint: Option, + }, + Zipkin { + #[serde(default)] + collector_endpoint: Option, }, }