diff --git a/Cargo.lock b/Cargo.lock index 1e02337..c1113c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -29,7 +29,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.101", ] [[package]] @@ -38,6 +38,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + [[package]] name = "bumpalo" version = "3.14.0" @@ -86,9 +92,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.10" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", @@ -143,7 +149,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.101", ] [[package]] @@ -181,7 +187,19 @@ checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -234,9 +252,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "log" @@ -260,6 +278,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.16" @@ -286,10 +310,24 @@ dependencies = [ "js-sys", "once_cell", "pin-project-lite", - "thiserror", + "thiserror 1.0.48", "urlencoding", ] +[[package]] +name = "opentelemetry" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e87237e2775f74896f9ad219d26a2081751187eb7c9f5c58dde20a23b95d16c" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.12", + "tracing", +] + [[package]] name = "opentelemetry-stdout" version = "0.3.0" @@ -298,8 +336,8 @@ checksum = "4bdf28b381f23afcd150afc0b38a4183dd321fc96320c1554752b6b761648f78" dependencies = [ "chrono", "futures-util", - "opentelemetry", - "opentelemetry_sdk", + "opentelemetry 0.22.0", + "opentelemetry_sdk 0.22.1", "ordered-float", "serde", "serde_json", @@ -316,13 +354,30 @@ dependencies = [ "futures-channel", "futures-executor", "futures-util", - "glob", "once_cell", - "opentelemetry", + "opentelemetry 0.22.0", "ordered-float", "percent-encoding", - "rand", - "thiserror", + "rand 0.8.5", + "thiserror 1.0.48", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afdefb21d1d47394abc1ba6c57363ab141be19e27cc70d0e422b7f303e4d290b" +dependencies = [ + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "opentelemetry 0.29.1", + "percent-encoding", + "rand 0.9.1", + "serde_json", + "thiserror 2.0.12", + "tracing", ] [[package]] @@ -372,22 +427,28 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.8.5" @@ -395,8 +456,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -406,7 +477,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -415,7 +496,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.10", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", ] [[package]] @@ -470,7 +560,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.101", ] [[package]] @@ -521,9 +611,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -536,7 +626,16 @@ version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.48", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -547,7 +646,18 @@ checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.101", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -562,12 +672,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.30" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -576,16 +687,17 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ + "num-conv", "time-core", ] @@ -606,11 +718,10 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -618,20 +729,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.101", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -650,14 +761,14 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.23.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9be14ba1bbe4ab79e9229f7f89fab8d120b865859f10527f31c033e599d2284" +checksum = "fd8e764bd6f5813fd8bebc3117875190c5b0415be8f7f8059bffb6ecd979c444" dependencies = [ "js-sys", "once_cell", - "opentelemetry", - "opentelemetry_sdk", + "opentelemetry 0.29.1", + "opentelemetry_sdk 0.29.0", "smallvec", "tracing", "tracing-core", @@ -683,13 +794,13 @@ dependencies = [ "Inflector", "http", "lazy_static", - "opentelemetry", + "opentelemetry 0.29.1", "opentelemetry-stdout", - "opentelemetry_sdk", - "rand", + "opentelemetry_sdk 0.29.0", + "rand 0.8.5", "serde", "serde_json", - "thiserror", + "thiserror 1.0.48", "time", "tracing", "tracing-core", @@ -791,6 +902,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.87" @@ -812,7 +932,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -834,7 +954,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -876,3 +996,12 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] diff --git a/Cargo.toml b/Cargo.toml index 58c3468..6a0bd95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ version = "0.2.9" [dependencies.opentelemetry] default-features = false features = ["trace"] -version = "0.22.0" +version = "0.29.0" optional = true [dependencies.serde] @@ -45,7 +45,7 @@ features = ["formatting"] version = "0.3.30" [dependencies.tracing-opentelemetry] -version = "0.23.0" +version = "0.30.0" optional = true [dependencies.tracing-subscriber] @@ -69,7 +69,7 @@ version = "0.1.0" lazy_static = "1.4.0" tracing = "0.1.34" rand = "0.8.5" -opentelemetry_sdk = "0.22.1" +opentelemetry_sdk = "0.29.0" [dev-dependencies.time] features = ["serde", "serde-well-known", "formatting"] @@ -78,7 +78,7 @@ version = "0.3.30" [dev-dependencies.opentelemetry] default-features = false features = ["testing", "trace"] -version = "0.22.0" +version = "0.29.0" [dev-dependencies.opentelemetry-stdout] features = ["trace"] diff --git a/src/event_formatter.rs b/src/event_formatter.rs index 7555032..f9b2d8a 100644 --- a/src/event_formatter.rs +++ b/src/event_formatter.rs @@ -1,15 +1,13 @@ use crate::{ google::LogSeverity, serializers::{SerializableContext, SerializableSpan, SourceLocation}, - visitor::Visitor, writer::WriteAdaptor, }; use serde::ser::{SerializeMap, Serializer as _}; use std::fmt; use time::{format_description::well_known::Rfc3339, OffsetDateTime}; -use tracing_core::{Event, Subscriber}; +use tracing_core::{field::Visit, Event, Subscriber}; use tracing_subscriber::{ - field::VisitOutput, fmt::{ format::{self, JsonFields}, FmtContext, FormatEvent, @@ -42,6 +40,78 @@ pub struct EventFormatter { pub(crate) cloud_trace_configuration: Option, } +// Helper struct to capture event fields +struct EventFieldVisitor<'a>(serde_json::Map, &'a JsonFields); + +impl Visit for EventFieldVisitor<'_> { + fn record_f64(&mut self, field: &tracing_core::Field, value: f64) { + self.0.insert( + field.name().to_string(), + serde_json::Value::Number(serde_json::Number::from_f64(value).unwrap_or_else(|| { + // tracing::debug!(target: "tracing_stackdriver::event_formatter", "f64 is not finite, using 0.0 instead"); + serde_json::Number::from(0) + })), + ); + } + + fn record_i64(&mut self, field: &tracing_core::Field, value: i64) { + self.0.insert( + field.name().to_string(), + serde_json::Value::Number(value.into()), + ); + } + + fn record_u64(&mut self, field: &tracing_core::Field, value: u64) { + self.0.insert( + field.name().to_string(), + serde_json::Value::Number(value.into()), + ); + } + + fn record_bool(&mut self, field: &tracing_core::Field, value: bool) { + self.0 + .insert(field.name().to_string(), serde_json::Value::Bool(value)); + } + + fn record_str(&mut self, field: &tracing_core::Field, value: &str) { + self.0.insert( + field.name().to_string(), + serde_json::Value::String(value.to_string()), + ); + } + + fn record_error( + &mut self, + field: &tracing_core::Field, + value: &(dyn std::error::Error + 'static), + ) { + self.0.insert( + field.name().to_string(), + serde_json::Value::String(value.to_string()), + ); + } + + fn record_debug(&mut self, field: &tracing_core::Field, value: &dyn fmt::Debug) { + match field.name() { + // Skip fields that are actually log metadata that have already been handled + name if name.starts_with("log.") => (), + name if name.starts_with("event.") => (), + "message" => { + self.0.insert( + "message".to_string(), // Use "message" as the key for the message field + serde_json::Value::String(format!("{:?}", value)), + ); + } + _ => { + self.0.insert( + field.name().to_string(), + serde_json::Value::String(format!("{:?}", value)), + ); + } + } + } +} + impl EventFormatter { /// Internal event formatting for a given serializer fn format_event( @@ -62,12 +132,25 @@ impl EventFormatter { .and_then(|id| context.span(id)) .or_else(|| context.lookup_current()); - // FIXME: derive an accurate entry count ahead of time let mut map = serializer.serialize_map(None)?; - // serialize custom fields + map.serialize_entry("severity", &severity)?; map.serialize_entry("time", &time)?; - map.serialize_entry("target", &meta.target())?; + map.serialize_entry("target", meta.target())?; + + // Extract and serialize event fields + let mut visitor = EventFieldVisitor(serde_json::Map::new(), context.field_format()); + event.record(&mut visitor); + + for (key, value) in visitor.0 { + // The "message" field is often the primary human-readable content. + // Google Cloud Logging expects this at the top level of jsonPayload. + if key == "message" { + map.serialize_entry("message", &value)?; + } else { + map.serialize_entry(&key, &value)?; + } + } if self.include_source_location { if let Some(file) = meta.file() { @@ -81,50 +164,52 @@ impl EventFormatter { } } - // serialize the current span and its leaves - if let Some(span) = span { - map.serialize_entry("span", &SerializableSpan::new(&span))?; + if let Some(span_ref) = span { + map.serialize_entry("span", &SerializableSpan::new(&span_ref))?; map.serialize_entry("spans", &SerializableContext::new(context))?; #[cfg(feature = "opentelemetry")] - if let (Some(crate::CloudTraceConfiguration { project_id }), Some(otel_data)) = ( - self.cloud_trace_configuration.as_ref(), - span.extensions().get::(), - ) { - use opentelemetry::trace::TraceContextExt; - - let builder = &otel_data.builder; - - if let Some(span_id) = builder.span_id { - map.serialize_entry("logging.googleapis.com/spanId", &span_id.to_string())?; + if let Some(config) = self.cloud_trace_configuration.as_ref() { + // Attempt to get OpenTelemetry trace and span IDs + let mut otel_trace_id: Option = None; + let mut otel_span_id: Option = None; + let mut otel_is_sampled: Option = None; + + // Iterate through extensions to find OtelData + if let Some(extensions) = span_ref + .extensions() + .get::() + { + let otel_ctx = &extensions.parent_cx; // Use parent_cx as it reflects the context when span was created + use opentelemetry::trace::TraceContextExt; + if otel_ctx.has_active_span() { + let otel_span_ref = otel_ctx.span(); + let otel_span_context = otel_span_ref.span_context(); + + otel_trace_id = Some(format!( + "projects/{}/traces/{}", + config.project_id, + otel_span_context.trace_id() + )); + otel_span_id = Some(otel_span_context.span_id().to_string()); + otel_is_sampled = Some(otel_span_context.is_sampled()); + } } - let (trace_id, trace_sampled) = if otel_data.parent_cx.has_active_span() { - let span_ref = otel_data.parent_cx.span(); - let span_context = span_ref.span_context(); - - (Some(span_context.trace_id()), span_context.is_sampled()) - } else { - (builder.trace_id, false) - }; - - if let Some(trace_id) = trace_id { - map.serialize_entry( - "logging.googleapis.com/trace", - &format!("projects/{project_id}/traces/{trace_id}",), - )?; + if let Some(trace_id_val) = otel_trace_id { + map.serialize_entry("logging.googleapis.com/trace", &trace_id_val)?; } - - if trace_sampled { + if let Some(span_id_val) = otel_span_id { + map.serialize_entry("logging.googleapis.com/spanId", &span_id_val)?; + } + if let Some(true) = otel_is_sampled { + // Only add if true map.serialize_entry("logging.googleapis.com/trace_sampled", &true)?; } } } - // serialize the stackdriver-specific fields with a visitor - let mut visitor = Visitor::new(severity, map); - event.record(&mut visitor); - visitor.finish().map_err(Error::from)?; + map.end()?; Ok(()) } } @@ -157,3 +242,4 @@ impl Default for EventFormatter { } } } +