| 1 | //! Structured logging. |
| 2 | //! |
| 3 | //! Add the `kv` feature to your `Cargo.toml` to enable |
| 4 | //! this module: |
| 5 | //! |
| 6 | //! ```toml |
| 7 | //! [dependencies.log] |
| 8 | //! features = ["kv"] |
| 9 | //! ``` |
| 10 | //! |
| 11 | //! # Structured logging in `log` |
| 12 | //! |
| 13 | //! Structured logging enhances traditional text-based log records with user-defined |
| 14 | //! attributes. Structured logs can be analyzed using a variety of data processing |
| 15 | //! techniques, without needing to find and parse attributes from unstructured text first. |
| 16 | //! |
| 17 | //! In `log`, user-defined attributes are part of a [`Source`] on the log record. |
| 18 | //! Each attribute is a key-value; a pair of [`Key`] and [`Value`]. Keys are strings |
| 19 | //! and values are a datum of any type that can be formatted or serialized. Simple types |
| 20 | //! like strings, booleans, and numbers are supported, as well as arbitrarily complex |
| 21 | //! structures involving nested objects and sequences. |
| 22 | //! |
| 23 | //! ## Adding key-values to log records |
| 24 | //! |
| 25 | //! Key-values appear before the message format in the `log!` macros: |
| 26 | //! |
| 27 | //! ``` |
| 28 | //! # use log::info; |
| 29 | //! info!(a = 1; "Something of interest" ); |
| 30 | //! ``` |
| 31 | //! |
| 32 | //! Key-values support the same shorthand identifier syntax as `format_args`: |
| 33 | //! |
| 34 | //! ``` |
| 35 | //! # use log::info; |
| 36 | //! let a = 1; |
| 37 | //! |
| 38 | //! info!(a; "Something of interest" ); |
| 39 | //! ``` |
| 40 | //! |
| 41 | //! Values are capturing using the [`ToValue`] trait by default. To capture a value |
| 42 | //! using a different trait implementation, use a modifier after its key. Here's how |
| 43 | //! the same example can capture `a` using its `Debug` implementation instead: |
| 44 | //! |
| 45 | //! ``` |
| 46 | //! # use log::info; |
| 47 | //! info!(a:? = 1; "Something of interest" ); |
| 48 | //! ``` |
| 49 | //! |
| 50 | //! The following capturing modifiers are supported: |
| 51 | //! |
| 52 | //! - `:?` will capture the value using `Debug`. |
| 53 | //! - `:debug` will capture the value using `Debug`. |
| 54 | //! - `:%` will capture the value using `Display`. |
| 55 | //! - `:display` will capture the value using `Display`. |
| 56 | //! - `:err` will capture the value using `std::error::Error` (requires the `kv_std` feature). |
| 57 | //! - `:sval` will capture the value using `sval::Value` (requires the `kv_sval` feature). |
| 58 | //! - `:serde` will capture the value using `serde::Serialize` (requires the `kv_serde` feature). |
| 59 | //! |
| 60 | //! ## Working with key-values on log records |
| 61 | //! |
| 62 | //! Use the [`Record::key_values`](../struct.Record.html#method.key_values) method to access key-values. |
| 63 | //! |
| 64 | //! Individual values can be pulled from the source by their key: |
| 65 | //! |
| 66 | //! ``` |
| 67 | //! # fn main() -> Result<(), log::kv::Error> { |
| 68 | //! use log::kv::{Source, Key, Value}; |
| 69 | //! # let record = log::Record::builder().key_values(&[("a" , 1)]).build(); |
| 70 | //! |
| 71 | //! // info!(a = 1; "Something of interest"); |
| 72 | //! |
| 73 | //! let a: Value = record.key_values().get(Key::from("a" )).unwrap(); |
| 74 | //! assert_eq!(1, a.to_i64().unwrap()); |
| 75 | //! # Ok(()) |
| 76 | //! # } |
| 77 | //! ``` |
| 78 | //! |
| 79 | //! All key-values can also be enumerated using a [`VisitSource`]: |
| 80 | //! |
| 81 | //! ``` |
| 82 | //! # fn main() -> Result<(), log::kv::Error> { |
| 83 | //! use std::collections::BTreeMap; |
| 84 | //! |
| 85 | //! use log::kv::{self, Source, Key, Value, VisitSource}; |
| 86 | //! |
| 87 | //! struct Collect<'kvs>(BTreeMap<Key<'kvs>, Value<'kvs>>); |
| 88 | //! |
| 89 | //! impl<'kvs> VisitSource<'kvs> for Collect<'kvs> { |
| 90 | //! fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { |
| 91 | //! self.0.insert(key, value); |
| 92 | //! |
| 93 | //! Ok(()) |
| 94 | //! } |
| 95 | //! } |
| 96 | //! |
| 97 | //! let mut visitor = Collect(BTreeMap::new()); |
| 98 | //! |
| 99 | //! # let record = log::Record::builder().key_values(&[("a" , 1), ("b" , 2), ("c" , 3)]).build(); |
| 100 | //! // info!(a = 1, b = 2, c = 3; "Something of interest"); |
| 101 | //! |
| 102 | //! record.key_values().visit(&mut visitor)?; |
| 103 | //! |
| 104 | //! let collected = visitor.0; |
| 105 | //! |
| 106 | //! assert_eq!( |
| 107 | //! vec!["a" , "b" , "c" ], |
| 108 | //! collected |
| 109 | //! .keys() |
| 110 | //! .map(|k| k.as_str()) |
| 111 | //! .collect::<Vec<_>>(), |
| 112 | //! ); |
| 113 | //! # Ok(()) |
| 114 | //! # } |
| 115 | //! ``` |
| 116 | //! |
| 117 | //! [`Value`]s have methods for conversions to common types: |
| 118 | //! |
| 119 | //! ``` |
| 120 | //! # fn main() -> Result<(), log::kv::Error> { |
| 121 | //! use log::kv::{Source, Key}; |
| 122 | //! # let record = log::Record::builder().key_values(&[("a" , 1)]).build(); |
| 123 | //! |
| 124 | //! // info!(a = 1; "Something of interest"); |
| 125 | //! |
| 126 | //! let a = record.key_values().get(Key::from("a" )).unwrap(); |
| 127 | //! |
| 128 | //! assert_eq!(1, a.to_i64().unwrap()); |
| 129 | //! # Ok(()) |
| 130 | //! # } |
| 131 | //! ``` |
| 132 | //! |
| 133 | //! Values also have their own [`VisitValue`] type. Value visitors are a lightweight |
| 134 | //! API for working with primitives types: |
| 135 | //! |
| 136 | //! ``` |
| 137 | //! # fn main() -> Result<(), log::kv::Error> { |
| 138 | //! use log::kv::{self, Source, Key, VisitValue}; |
| 139 | //! # let record = log::Record::builder().key_values(&[("a" , 1)]).build(); |
| 140 | //! |
| 141 | //! struct IsNumeric(bool); |
| 142 | //! |
| 143 | //! impl<'kvs> VisitValue<'kvs> for IsNumeric { |
| 144 | //! fn visit_any(&mut self, _value: kv::Value) -> Result<(), kv::Error> { |
| 145 | //! self.0 = false; |
| 146 | //! Ok(()) |
| 147 | //! } |
| 148 | //! |
| 149 | //! fn visit_u64(&mut self, _value: u64) -> Result<(), kv::Error> { |
| 150 | //! self.0 = true; |
| 151 | //! Ok(()) |
| 152 | //! } |
| 153 | //! |
| 154 | //! fn visit_i64(&mut self, _value: i64) -> Result<(), kv::Error> { |
| 155 | //! self.0 = true; |
| 156 | //! Ok(()) |
| 157 | //! } |
| 158 | //! |
| 159 | //! fn visit_u128(&mut self, _value: u128) -> Result<(), kv::Error> { |
| 160 | //! self.0 = true; |
| 161 | //! Ok(()) |
| 162 | //! } |
| 163 | //! |
| 164 | //! fn visit_i128(&mut self, _value: i128) -> Result<(), kv::Error> { |
| 165 | //! self.0 = true; |
| 166 | //! Ok(()) |
| 167 | //! } |
| 168 | //! |
| 169 | //! fn visit_f64(&mut self, _value: f64) -> Result<(), kv::Error> { |
| 170 | //! self.0 = true; |
| 171 | //! Ok(()) |
| 172 | //! } |
| 173 | //! } |
| 174 | //! |
| 175 | //! // info!(a = 1; "Something of interest"); |
| 176 | //! |
| 177 | //! let a = record.key_values().get(Key::from("a" )).unwrap(); |
| 178 | //! |
| 179 | //! let mut visitor = IsNumeric(false); |
| 180 | //! |
| 181 | //! a.visit(&mut visitor)?; |
| 182 | //! |
| 183 | //! let is_numeric = visitor.0; |
| 184 | //! |
| 185 | //! assert!(is_numeric); |
| 186 | //! # Ok(()) |
| 187 | //! # } |
| 188 | //! ``` |
| 189 | //! |
| 190 | //! To serialize a value to a format like JSON, you can also use either `serde` or `sval`: |
| 191 | //! |
| 192 | //! ``` |
| 193 | //! # fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 194 | //! # #[cfg (feature = "serde" )] |
| 195 | //! # { |
| 196 | //! # use log::kv::Key; |
| 197 | //! #[derive(serde::Serialize)] |
| 198 | //! struct Data { |
| 199 | //! a: i32, b: bool, |
| 200 | //! c: &'static str, |
| 201 | //! } |
| 202 | //! |
| 203 | //! let data = Data { a: 1, b: true, c: "Some data" }; |
| 204 | //! |
| 205 | //! # let source = [("a" , log::kv::Value::from_serde(&data))]; |
| 206 | //! # let record = log::Record::builder().key_values(&source).build(); |
| 207 | //! // info!(a = data; "Something of interest"); |
| 208 | //! |
| 209 | //! let a = record.key_values().get(Key::from("a" )).unwrap(); |
| 210 | //! |
| 211 | //! assert_eq!("{ \"a \":1, \"b \":true, \"c \": \"Some data \"}" , serde_json::to_string(&a)?); |
| 212 | //! # } |
| 213 | //! # Ok(()) |
| 214 | //! # } |
| 215 | //! ``` |
| 216 | //! |
| 217 | //! The choice of serialization framework depends on the needs of the consumer. |
| 218 | //! If you're in a no-std environment, you can use `sval`. In other cases, you can use `serde`. |
| 219 | //! Log producers and log consumers don't need to agree on the serialization framework. |
| 220 | //! A value can be captured using its `serde::Serialize` implementation and still be serialized |
| 221 | //! through `sval` without losing any structure or data. |
| 222 | //! |
| 223 | //! Values can also always be formatted using the standard `Debug` and `Display` |
| 224 | //! traits: |
| 225 | //! |
| 226 | //! ``` |
| 227 | //! # use log::kv::Key; |
| 228 | //! # #[derive(Debug)] |
| 229 | //! struct Data { |
| 230 | //! a: i32, |
| 231 | //! b: bool, |
| 232 | //! c: &'static str, |
| 233 | //! } |
| 234 | //! |
| 235 | //! let data = Data { a: 1, b: true, c: "Some data" }; |
| 236 | //! |
| 237 | //! # let source = [("a" , log::kv::Value::from_debug(&data))]; |
| 238 | //! # let record = log::Record::builder().key_values(&source).build(); |
| 239 | //! // info!(a = data; "Something of interest"); |
| 240 | //! |
| 241 | //! let a = record.key_values().get(Key::from("a" )).unwrap(); |
| 242 | //! |
| 243 | //! assert_eq!("Data { a: 1, b: true, c: \"Some data \" }" , format!("{a:?}" )); |
| 244 | //! ``` |
| 245 | |
| 246 | mod error; |
| 247 | mod key; |
| 248 | |
| 249 | #[cfg (not(feature = "kv_unstable" ))] |
| 250 | mod source; |
| 251 | #[cfg (not(feature = "kv_unstable" ))] |
| 252 | mod value; |
| 253 | |
| 254 | pub use self::error::Error; |
| 255 | pub use self::key::{Key, ToKey}; |
| 256 | pub use self::source::{Source, VisitSource}; |
| 257 | pub use self::value::{ToValue, Value, VisitValue}; |
| 258 | |
| 259 | #[cfg (feature = "kv_unstable" )] |
| 260 | pub mod source; |
| 261 | #[cfg (feature = "kv_unstable" )] |
| 262 | pub mod value; |
| 263 | |
| 264 | #[cfg (feature = "kv_unstable" )] |
| 265 | pub use self::source::Visitor; |
| 266 | |