diff --git a/Cargo.lock b/Cargo.lock index 22d8213..b201b60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -917,7 +917,7 @@ dependencies = [ [[package]] name = "libdd-capabilities" version = "2.1.0" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ "anyhow", "bytes", @@ -941,8 +941,9 @@ dependencies = [ [[package]] name = "libdd-capabilities-impl" version = "3.0.0" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ + "anyhow", "bytes", "http", "http-body-util", @@ -989,7 +990,7 @@ dependencies = [ [[package]] name = "libdd-common" version = "5.1.0" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ "anyhow", "bytes", @@ -1030,7 +1031,7 @@ dependencies = [ "libc", "libdd-common 5.0.0", "libdd-libunwind-sys", - "libdd-telemetry", + "libdd-telemetry 5.0.1", "nix 0.29.0", "num-derive", "num-traits", @@ -1052,13 +1053,14 @@ dependencies = [ [[package]] name = "libdd-data-pipeline" version = "7.0.0" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ "anyhow", "arc-swap", "async-trait", "bytes", "either", + "futures", "getrandom 0.2.17", "http", "http-body-util", @@ -1068,6 +1070,7 @@ dependencies = [ "libdd-ddsketch 1.1.0", "libdd-dogstatsd-client", "libdd-shared-runtime 2.0.0", + "libdd-telemetry 6.0.0", "libdd-tinybytes", "libdd-trace-normalization", "libdd-trace-protobuf 4.0.0", @@ -1081,6 +1084,7 @@ dependencies = [ "tokio-util", "tracing", "uuid", + "web-time", ] [[package]] @@ -1094,7 +1098,7 @@ dependencies = [ [[package]] name = "libdd-ddsketch" version = "1.1.0" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ "prost", ] @@ -1102,7 +1106,7 @@ dependencies = [ [[package]] name = "libdd-dogstatsd-client" version = "4.0.0" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ "anyhow", "cadence", @@ -1174,7 +1178,7 @@ dependencies = [ [[package]] name = "libdd-shared-runtime" version = "2.0.0" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ "async-trait", "futures", @@ -1215,10 +1219,39 @@ dependencies = [ "winver", ] +[[package]] +name = "libdd-telemetry" +version = "6.0.0" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" +dependencies = [ + "anyhow", + "async-trait", + "base64", + "bytes", + "futures", + "getrandom 0.2.17", + "hashbrown 0.15.5", + "http", + "libc", + "libdd-capabilities 2.1.0", + "libdd-common 5.1.0", + "libdd-ddsketch 1.1.0", + "libdd-shared-runtime 2.0.0", + "serde", + "serde_json", + "sys-info", + "tokio", + "tokio-util", + "tracing", + "uuid", + "web-time", + "winver", +] + [[package]] name = "libdd-tinybytes" version = "1.1.1" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ "serde", ] @@ -1226,7 +1259,7 @@ dependencies = [ [[package]] name = "libdd-trace-normalization" version = "3.0.0" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ "anyhow", "libdd-trace-protobuf 4.0.0", @@ -1235,7 +1268,7 @@ dependencies = [ [[package]] name = "libdd-trace-obfuscation" version = "5.0.0" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ "anyhow", "fluent-uri", @@ -1261,7 +1294,7 @@ dependencies = [ [[package]] name = "libdd-trace-protobuf" version = "4.0.0" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ "prost", "serde", @@ -1271,7 +1304,7 @@ dependencies = [ [[package]] name = "libdd-trace-stats" version = "6.0.0" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ "anyhow", "arc-swap", @@ -1285,6 +1318,7 @@ dependencies = [ "libdd-ddsketch 1.1.0", "libdd-dogstatsd-client", "libdd-shared-runtime 2.0.0", + "libdd-telemetry 6.0.0", "libdd-trace-obfuscation", "libdd-trace-protobuf 4.0.0", "libdd-trace-utils", @@ -1293,12 +1327,13 @@ dependencies = [ "tokio", "tokio-util", "tracing", + "web-time", ] [[package]] name = "libdd-trace-utils" version = "9.0.0" -source = "git+https://github.com/DataDog/libdatadog.git?rev=8cd68ab922fb7aade0f089ccfc91291d874673af#8cd68ab922fb7aade0f089ccfc91291d874673af" +source = "git+https://github.com/DataDog/libdatadog.git?rev=73f23a2c03be39971c966e444785d63b6fd52e81#73f23a2c03be39971c966e444785d63b6fd52e81" dependencies = [ "anyhow", "base64", @@ -1310,6 +1345,7 @@ dependencies = [ "http-body", "http-body-util", "indexmap", + "itoa", "libdd-capabilities 2.1.0", "libdd-capabilities-impl 3.0.0", "libdd-common 5.1.0", @@ -1816,6 +1852,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test", + "web-time", ] [[package]] @@ -2757,6 +2794,16 @@ dependencies = [ "semver", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-root-certs" version = "1.0.8" diff --git a/crates/capabilities/Cargo.toml b/crates/capabilities/Cargo.toml index cbb376e..6fb0ace 100644 --- a/crates/capabilities/Cargo.toml +++ b/crates/capabilities/Cargo.toml @@ -16,8 +16,8 @@ bytes = "1.4" futures-core = "0.3" anyhow = "1" # TODO: Replace this temporary libdatadog PR rev with the official release/tag -# that contains DataDog/libdatadog#2235, then regenerate Cargo.lock. -libdd-capabilities = { git = "https://github.com/DataDog/libdatadog.git", rev = "8cd68ab922fb7aade0f089ccfc91291d874673af" } +# that contains DataDog/libdatadog#2172, then regenerate Cargo.lock. +libdd-capabilities = { git = "https://github.com/DataDog/libdatadog.git", rev = "73f23a2c03be39971c966e444785d63b6fd52e81" } [dev-dependencies] wasm-bindgen-test = "0.3" diff --git a/crates/pipeline/Cargo.toml b/crates/pipeline/Cargo.toml index f8e318a..fdc3b06 100644 --- a/crates/pipeline/Cargo.toml +++ b/crates/pipeline/Cargo.toml @@ -15,17 +15,18 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1" libdatadog-nodejs-capabilities = { path = "../capabilities" } # TODO: Replace these temporary libdatadog PR revs with the official release/tag -# that contains DataDog/libdatadog#2235, then regenerate Cargo.lock. -libdd-capabilities = { git = "https://github.com/DataDog/libdatadog.git", rev = "8cd68ab922fb7aade0f089ccfc91291d874673af" } -libdd-common = { git = "https://github.com/DataDog/libdatadog.git", rev = "8cd68ab922fb7aade0f089ccfc91291d874673af", default-features = false } -libdd-data-pipeline = { git = "https://github.com/DataDog/libdatadog.git", rev = "8cd68ab922fb7aade0f089ccfc91291d874673af", default-features = false } -libdd-trace-utils = { git = "https://github.com/DataDog/libdatadog.git", rev = "8cd68ab922fb7aade0f089ccfc91291d874673af", default-features = false, features = ["change-buffer"] } -libdd-trace-stats = { git = "https://github.com/DataDog/libdatadog.git", rev = "8cd68ab922fb7aade0f089ccfc91291d874673af", default-features = false } -libdd-trace-protobuf = { git = "https://github.com/DataDog/libdatadog.git", rev = "8cd68ab922fb7aade0f089ccfc91291d874673af", default-features = false } -libdd-shared-runtime = { git = "https://github.com/DataDog/libdatadog.git", rev = "8cd68ab922fb7aade0f089ccfc91291d874673af", default-features = false } +# that contains DataDog/libdatadog#2172, then regenerate Cargo.lock. +libdd-capabilities = { git = "https://github.com/DataDog/libdatadog.git", rev = "73f23a2c03be39971c966e444785d63b6fd52e81" } +libdd-common = { git = "https://github.com/DataDog/libdatadog.git", rev = "73f23a2c03be39971c966e444785d63b6fd52e81", default-features = false } +libdd-data-pipeline = { git = "https://github.com/DataDog/libdatadog.git", rev = "73f23a2c03be39971c966e444785d63b6fd52e81", default-features = false, features = ["telemetry"] } +libdd-trace-utils = { git = "https://github.com/DataDog/libdatadog.git", rev = "73f23a2c03be39971c966e444785d63b6fd52e81", default-features = false, features = ["change-buffer"] } +libdd-trace-stats = { git = "https://github.com/DataDog/libdatadog.git", rev = "73f23a2c03be39971c966e444785d63b6fd52e81", default-features = false } +libdd-trace-protobuf = { git = "https://github.com/DataDog/libdatadog.git", rev = "73f23a2c03be39971c966e444785d63b6fd52e81", default-features = false } +libdd-shared-runtime = { git = "https://github.com/DataDog/libdatadog.git", rev = "73f23a2c03be39971c966e444785d63b6fd52e81", default-features = false } rmp-serde = "1" bytes = "1" http = "1" +web-time = "1" console_error_panic_hook = "0.1" [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/crates/pipeline/src/lib.rs b/crates/pipeline/src/lib.rs index 31188af..4ff36bb 100644 --- a/crates/pipeline/src/lib.rs +++ b/crates/pipeline/src/lib.rs @@ -1,7 +1,7 @@ use libdatadog_nodejs_capabilities::WasmCapabilities; use libdd_data_pipeline::trace_exporter::agent_response::AgentResponse; use libdd_data_pipeline::trace_exporter::{ - TraceExporter, TraceExporterBuilder, TraceExporterOutputFormat, + TelemetryConfig, TraceExporter, TraceExporterBuilder, TraceExporterOutputFormat, }; use libdd_data_pipeline::OtlpProtocol; use libdd_shared_runtime::LocalRuntime; @@ -197,13 +197,17 @@ pub struct WasmSpanState { /// Datadog agent. libdatadog maps its internal traces to OTLP, so no /// JS-formatted spans are involved. Like `use_v05`, only takes effect if /// set before the first send (when the exporter is built). - otlp_endpoint: RefCell>, + otlp_endpoint: Cell>, /// OTLP wire protocol (`http/json` default, or `http/protobuf`). Only /// applied when `otlp_endpoint` is set. otlp_protocol: Cell>, /// Extra HTTP headers for OTLP export (e.g. collector auth), as key/value /// pairs. Only applied when `otlp_endpoint` is set. - otlp_headers: RefCell>, + otlp_headers: Cell>, + /// When set, the lazily-built exporter has telemetry enabled with this + /// config. Only takes effect if set before the first send + /// (when the exporter is built). + telemetry_config: Cell>, /// Latched message from a failed lazy `build_async`. Building is one-shot and /// a failure is fatal (bad config), so once set every send returns it (as a /// distinguishable error) instead of a misleading "builder already consumed", @@ -335,9 +339,10 @@ impl WasmSpanState { prepared_spans: RefCell::new(Vec::new()), sending: Cell::new(false), use_v05: Cell::new(false), - otlp_endpoint: RefCell::new(None), + otlp_endpoint: Cell::new(None), otlp_protocol: Cell::new(None), - otlp_headers: RefCell::new(Vec::new()), + otlp_headers: Cell::new(Vec::new()), + telemetry_config: Cell::new(None), build_error: RefCell::new(None), }) } @@ -359,7 +364,7 @@ impl WasmSpanState { /// Takes precedence over `setUseV05` (OTLP bypasses the agent entirely). #[wasm_bindgen(js_name = "setOtlpEndpoint")] pub fn set_otlp_endpoint(&self, url: String) { - *self.otlp_endpoint.borrow_mut() = Some(url); + self.otlp_endpoint.set(Some(url)); } /// Select the OTLP wire protocol: `http/json` (default) or `http/protobuf`. @@ -374,6 +379,31 @@ impl WasmSpanState { Ok(()) } + /// Enable telemetry on the lazily-built trace exporter. + /// + /// Must be called before the first `sendPreparedChunk`. Later calls have + /// no effect. + /// + /// # Arguments + /// + /// - `heartbeat_ms`: sets the metric-flush cadence. Set to 0 to defer to + /// libdatadog's default interval. + /// - `runtime_id`: tags telemetry payloads with the tracer's runtime id when provided + /// - `debug_enabled`: toggles libdd-telemetry's verbose logging + #[wasm_bindgen(js_name = "enableTelemetry")] + pub fn enable_telemetry( + &self, + heartbeat_ms: u32, + runtime_id: Option, + debug_enabled: bool, + ) { + self.telemetry_config.set(Some(TelemetryConfig { + heartbeat: heartbeat_ms as u64, + runtime_id, + debug_enabled, + })); + } + /// Set extra HTTP headers for OTLP export as a flat `[key, value, ...]` /// array (the host flattens its key/value map). Only takes effect with an /// OTLP endpoint set, before the first send. A trailing unpaired element on @@ -386,7 +416,7 @@ impl WasmSpanState { .chunks_exact(2) .map(|pair| (pair[0].clone(), pair[1].clone())) .collect(); - *self.otlp_headers.borrow_mut() = headers; + self.otlp_headers.set(headers); } #[wasm_bindgen] @@ -520,16 +550,19 @@ impl WasmSpanState { // When an OTLP endpoint is configured, libdatadog exports traces via // OTLP HTTP to that endpoint instead of the Datadog agent (mutually // exclusive with the agent v0.4/v0.5 path). - if let Some(url) = self.otlp_endpoint.borrow().as_deref() { - builder.set_otlp_endpoint(url); - if let Some(protocol) = self.otlp_protocol.get() { + if let Some(url) = self.otlp_endpoint.take() { + builder.set_otlp_endpoint(&url); + if let Some(protocol) = self.otlp_protocol.take() { builder.set_otlp_protocol(protocol); } - let headers = self.otlp_headers.borrow(); + let headers = self.otlp_headers.take(); if !headers.is_empty() { - builder.set_otlp_headers(headers.clone()); + builder.set_otlp_headers(headers); } } + if let Some(cfg) = self.telemetry_config.take() { + builder.enable_telemetry(cfg); + } match builder.build_async::().await { Ok(built) => *exporter_slot = Some(built), Err(e) => { diff --git a/crates/pipeline/src/stats.rs b/crates/pipeline/src/stats.rs index 127a21e..0a20f22 100644 --- a/crates/pipeline/src/stats.rs +++ b/crates/pipeline/src/stats.rs @@ -7,13 +7,14 @@ //! HTTP transport for flushing stats to the Datadog agent's `/v0.6/stats` //! endpoint. -use std::time::{Duration, SystemTime}; +use std::time::Duration; +use web_time::SystemTime; -/// Wall-clock now() for wasm. `std::time::SystemTime::now()` is unimplemented on -/// `wasm32-unknown-unknown` (it panics/traps), so derive the time from JS -/// `Date.now()` (milliseconds since the Unix epoch). +/// Wall-clock now() for wasm. Delegates to `web_time::SystemTime::now()`, +/// which routes to JS `Date.now()` on `wasm32-unknown-unknown` (native +/// `std::time::SystemTime::now()` is unimplemented on that target and traps). fn now() -> SystemTime { - SystemTime::UNIX_EPOCH + Duration::from_millis(js_sys::Date::now() as u64) + SystemTime::now() } use bytes::Bytes;