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

mod error;
mod key;

#[cfg(not(feature = "kv_unstable"))]
mod source;
#[cfg(not(feature = "kv_unstable"))]
mod value;

pub use self::error::Error;
pub use self::key::{Key, ToKey};
pub use self::source::{Source, VisitSource};
pub use self::value::{ToValue, Value, VisitValue};

#[cfg(feature = "kv_unstable")]
pub mod source;
#[cfg(feature = "kv_unstable")]
pub mod value;

#[cfg(feature = "kv_unstable")]
pub use self::source::Visitor;