rustfoundry/telemetry/log/mod.rs
1//! Logging-related functionality.
2
3mod field_dedup;
4mod field_filtering;
5mod field_redact;
6mod rate_limit;
7
8pub(crate) mod init;
9
10#[cfg(any(test, feature = "testing"))]
11pub(crate) mod testing;
12
13#[doc(hidden)]
14pub mod internal;
15
16#[cfg(feature = "metrics")]
17pub mod log_volume;
18
19use self::init::LogHarness;
20use self::internal::current_log;
21use crate::telemetry::log::init::build_log_with_drain;
22use crate::telemetry::settings::LogVerbosity;
23use crate::Result;
24use slog::{Logger, OwnedKV};
25use std::ops::Deref;
26use std::sync::Arc;
27
28#[cfg(any(test, feature = "testing"))]
29pub use self::testing::TestLogRecord;
30
31/// Sets current log's verbosity, overriding the settings used in [`init`].
32///
33/// For reasons related to the current implementation of `set_verbosity()`, there is a danger of
34/// stack overflow if it is called an extremely large number of times on the same logger. To
35/// protect against the possibility of stack overflow, there is an internal counter which will
36/// trigger a panic if a limit of (currently) 1000 calls on a single logger is reached.
37///
38/// To avoid this panic, only call `set_verbosity()` when there is an actual change to the
39/// verbosity level.
40///
41/// [`init`]: crate::telemetry::init
42pub fn set_verbosity(verbosity: LogVerbosity) -> Result<()> {
43 let harness = LogHarness::get();
44
45 let mut settings = harness.settings.clone();
46 settings.verbosity = verbosity;
47
48 let current_log = current_log();
49 let current_log_lock = current_log.write();
50
51 let Some(mut current_log_lock) =
52 internal::LoggerWithKvNestingTracking::check_nesting_level(current_log_lock)
53 else {
54 return Ok(()); // avoid changes, nesting level was beyond threshold
55 };
56
57 let kv = OwnedKV(current_log_lock.list().clone());
58 current_log_lock.inner = build_log_with_drain(&settings, kv, Arc::clone(&harness.root_drain));
59
60 Ok(())
61}
62
63/// Gets the current log's verbosity.
64pub fn verbosity() -> LogVerbosity {
65 let harness = LogHarness::get();
66 harness.settings.verbosity
67}
68
69/// Returns current log as a raw [slog] crate's `Logger` used by Rustfoundry internally.
70///
71/// Can be used to propagate the logging context to libraries that don't use Rustfoundry'
72/// telemetry.
73///
74/// [slog]: https://crates.io/crates/slog
75pub fn slog_logger() -> Arc<parking_lot::RwLock<impl Deref<Target = Logger>>> {
76 current_log()
77}
78
79// NOTE: `#[doc(hidden)]` + `#[doc(inline)]` for `pub use` trick is used to prevent these macros
80// to show up in the crate's top level docs.
81
82/// Adds fields to all the log records, making them context fields.
83///
84/// Calling the method with same field name multiple times updates the key value. There is a small
85/// cost in performance if large numbers of the same field are added, which then must be
86/// deduplicated at runtime. For that reason, as well as the fact that there is a danger of stack
87/// overflow if `add_fields!` is called an extremely large number of times on the same logger,
88/// there is an internal counter which will trigger a panic if a limit of (currently) 1000 calls on
89/// a single logger is reached.
90///
91/// To avoid this panic, make sure to only use `add_fields!` for fields that will remain relatively
92/// static (under 1000 updates over the lifetime of any given logger).
93///
94/// Certain added fields may not be present in the resulting logs if
95/// [`LoggingSettings::redact_keys`] is used.
96///
97/// # Examples
98/// ```
99/// use rustfoundry::telemetry::TelemetryContext;
100/// use rustfoundry::telemetry::log::{self, TestLogRecord};
101/// use rustfoundry::telemetry::settings::Level;
102///
103/// // Test context is used for demonstration purposes to show the resulting log records.
104/// let ctx = TelemetryContext::test();
105/// let _scope = ctx.scope();
106///
107/// log::warn!("Hello with one field"; "foo" => "bar");
108///
109/// log::add_fields!("ctx_field1" => 42, "ctx_field2" => "baz");
110///
111/// log::warn!("With context fields"; "foo" => "bar");
112///
113/// // Update the context field value
114/// log::add_fields!("ctx_field1" => 43);
115///
116/// log::warn!("One more with context fields");
117///
118/// assert_eq!(*ctx.log_records(), &[
119/// TestLogRecord {
120/// level: Level::Warning,
121/// message: "Hello with one field".into(),
122/// fields: vec![("foo".into(), "bar".into())]
123/// },
124/// TestLogRecord {
125/// level: Level::Warning,
126/// message: "With context fields".into(),
127/// fields: vec![
128/// ("ctx_field2".into(), "baz".into()),
129/// ("ctx_field1".into(), "42".into()),
130/// ("foo".into(), "bar".into())
131/// ]
132/// },
133/// TestLogRecord {
134/// level: Level::Warning,
135/// message: "One more with context fields".into(),
136/// fields: vec![
137/// ("ctx_field1".into(), "43".into()),
138/// ("ctx_field2".into(), "baz".into()),
139/// ]
140/// }
141/// ]);
142/// ```
143///
144/// [`LoggingSettings::redact_keys`]: crate::telemetry::settings::LoggingSettings::redact_keys
145#[macro_export]
146#[doc(hidden)]
147macro_rules! __add_fields {
148 ( $($args:tt)* ) => {
149 $crate::telemetry::log::internal::add_log_fields(
150 $crate::reexports_for_macros::slog::o!($($args)*)
151 );
152 };
153}
154
155/// Log error level record.
156///
157/// If duplicate fields are specified for the record then the last one takes precedence and
158/// overwrites the value of the previous one.
159///
160/// Certain added fields may not be present in the resulting logs if
161/// [`LoggingSettings::redact_keys`] is used.
162///
163/// # Examples
164/// ```
165/// use rustfoundry::telemetry::TelemetryContext;
166/// use rustfoundry::telemetry::log::{self, TestLogRecord};
167/// use rustfoundry::telemetry::settings::Level;
168///
169/// // Test context is used for demonstration purposes to show the resulting log records.
170/// let ctx = TelemetryContext::test();
171/// let _scope = ctx.scope();
172///
173/// // Simple log message
174/// log::error!("Hello world!");
175///
176/// // Macro also accepts format arguments
177/// log::error!("The values are: {}, {}", 42, true);
178///
179/// // Fields key-value pairs can be added to log record, by separating the format message
180/// // and fields by `;`.
181/// log::error!("Answer: {}", 42; "foo" => "bar", "baz" => 1337);
182///
183/// assert_eq!(*ctx.log_records(), &[
184/// TestLogRecord {
185/// level: Level::Error,
186/// message: "Hello world!".into(),
187/// fields: vec![]
188/// },
189/// TestLogRecord {
190/// level: Level::Error,
191/// message: "The values are: 42, true".into(),
192/// fields: vec![]
193/// },
194/// TestLogRecord {
195/// level: Level::Error,
196/// message: "Answer: 42".into(),
197/// fields: vec![
198/// ("baz".into(), "1337".into()),
199/// ("foo".into(), "bar".into())
200/// ]
201/// }
202/// ]);
203/// ```
204///
205/// [`LoggingSettings::redact_keys`]: crate::telemetry::settings::LoggingSettings::redact_keys
206#[macro_export]
207#[doc(hidden)]
208macro_rules! __error {
209 ( $($args:tt)+ ) => {
210 $crate::reexports_for_macros::slog::error!(
211 $crate::telemetry::log::internal::current_log().read(),
212 $($args)+
213 );
214 };
215}
216
217/// Log warning level record.
218///
219/// If duplicate fields are specified for the record then the last one takes precedence and
220/// overwrites the value of the previous one.
221///
222/// Certain added fields may not be present in the resulting logs if
223/// [`LoggingSettings::redact_keys`] is used.
224///
225/// # Examples
226/// ```
227/// use rustfoundry::telemetry::TelemetryContext;
228/// use rustfoundry::telemetry::log::{self, TestLogRecord};
229/// use rustfoundry::telemetry::settings::Level;
230///
231/// // Test context is used for demonstration purposes to show the resulting log records.
232/// let ctx = TelemetryContext::test();
233/// let _scope = ctx.scope();
234///
235/// // Simple log message
236/// log::warn!("Hello world!");
237///
238/// // Macro also accepts format arguments
239/// log::warn!("The values are: {}, {}", 42, true);
240///
241/// // Fields key-value pairs can be added to log record, by separating the format message
242/// // and fields by `;`.
243/// log::warn!("Answer: {}", 42; "foo" => "bar", "baz" => 1337);
244///
245/// assert_eq!(*ctx.log_records(), &[
246/// TestLogRecord {
247/// level: Level::Warning,
248/// message: "Hello world!".into(),
249/// fields: vec![]
250/// },
251/// TestLogRecord {
252/// level: Level::Warning,
253/// message: "The values are: 42, true".into(),
254/// fields: vec![]
255/// },
256/// TestLogRecord {
257/// level: Level::Warning,
258/// message: "Answer: 42".into(),
259/// fields: vec![
260/// ("baz".into(), "1337".into()),
261/// ("foo".into(), "bar".into())
262/// ]
263/// }
264/// ]);
265/// ```
266///
267/// [`LoggingSettings::redact_keys`]: crate::telemetry::settings::LoggingSettings::redact_keys
268#[doc(hidden)]
269#[macro_export]
270macro_rules! __warn {
271 ( $($args:tt)+ ) => {
272 $crate::reexports_for_macros::slog::warn!(
273 $crate::telemetry::log::internal::current_log().read(),
274 $($args)+
275 );
276 };
277}
278
279/// Log debug level record.
280///
281/// If duplicate fields are specified for the record then the last one takes precedence and
282/// overwrites the value of the previous one.
283///
284/// Certain added fields may not be present in the resulting logs if
285/// [`LoggingSettings::redact_keys`] is used.
286///
287/// # Examples
288/// ```
289/// use rustfoundry::telemetry::TelemetryContext;
290/// use rustfoundry::telemetry::log::{self, TestLogRecord};
291/// use rustfoundry::telemetry::settings::Level;
292///
293/// // Test context is used for demonstration purposes to show the resulting log records.
294/// let ctx = TelemetryContext::test();
295/// let _scope = ctx.scope();
296///
297/// // Simple log message
298/// log::debug!("Hello world!");
299///
300/// // Macro also accepts format arguments
301/// log::debug!("The values are: {}, {}", 42, true);
302///
303/// // Fields key-value pairs can be added to log record, by separating the format message
304/// // and fields by `;`.
305/// log::debug!("Answer: {}", 42; "foo" => "bar", "baz" => 1337);
306///
307/// assert_eq!(*ctx.log_records(), &[
308/// TestLogRecord {
309/// level: Level::Debug,
310/// message: "Hello world!".into(),
311/// fields: vec![]
312/// },
313/// TestLogRecord {
314/// level: Level::Debug,
315/// message: "The values are: 42, true".into(),
316/// fields: vec![]
317/// },
318/// TestLogRecord {
319/// level: Level::Debug,
320/// message: "Answer: 42".into(),
321/// fields: vec![
322/// ("baz".into(), "1337".into()),
323/// ("foo".into(), "bar".into())
324/// ]
325/// }
326/// ]);
327/// ```
328///
329/// [`LoggingSettings::redact_keys`]: crate::telemetry::settings::LoggingSettings::redact_keys
330#[macro_export]
331#[doc(hidden)]
332macro_rules! __debug {
333 ( $($args:tt)+ ) => {
334 $crate::reexports_for_macros::slog::debug!(
335 $crate::telemetry::log::internal::current_log().read(),
336 $($args)+
337 );
338 };
339}
340
341/// Log info level record.
342///
343/// If duplicate fields are specified for the record then the last one takes precedence and
344/// overwrites the value of the previous one.
345///
346/// Certain added fields may not be present in the resulting logs if
347/// [`LoggingSettings::redact_keys`] is used.
348///
349/// # Examples
350/// ```
351/// use rustfoundry::telemetry::TelemetryContext;
352/// use rustfoundry::telemetry::log::{self, TestLogRecord};
353/// use rustfoundry::telemetry::settings::Level;
354///
355/// // Test context is used for demonstration purposes to show the resulting log records.
356/// let ctx = TelemetryContext::test();
357/// let _scope = ctx.scope();
358///
359/// // Simple log message
360/// log::info!("Hello world!");
361///
362/// // Macro also accepts format arguments
363/// log::info!("The values are: {}, {}", 42, true);
364///
365/// // Fields key-value pairs can be added to log record, by separating the format message
366/// // and fields by `;`.
367/// log::info!("Answer: {}", 42; "foo" => "bar", "baz" => 1337);
368///
369/// assert_eq!(*ctx.log_records(), &[
370/// TestLogRecord {
371/// level: Level::Info,
372/// message: "Hello world!".into(),
373/// fields: vec![]
374/// },
375/// TestLogRecord {
376/// level: Level::Info,
377/// message: "The values are: 42, true".into(),
378/// fields: vec![]
379/// },
380/// TestLogRecord {
381/// level: Level::Info,
382/// message: "Answer: 42".into(),
383/// fields: vec![
384/// ("baz".into(), "1337".into()),
385/// ("foo".into(), "bar".into())
386/// ]
387/// }
388/// ]);
389/// ```
390///
391/// [`LoggingSettings::redact_keys`]: crate::telemetry::settings::LoggingSettings::redact_keys
392#[macro_export]
393#[doc(hidden)]
394macro_rules! __info {
395 ( $($args:tt)+ ) => {
396 $crate::reexports_for_macros::slog::info!(
397 $crate::telemetry::log::internal::current_log().read(),
398 $($args)+
399 );
400 };
401}
402
403/// Log trace level record.
404///
405/// If duplicate fields are specified for the record then the last one takes precedence and
406/// overwrites the value of the previous one.
407///
408/// Certain added fields may not be present in the resulting logs if
409/// [`LoggingSettings::redact_keys`] is used.
410///
411/// # Examples
412/// ```
413/// use rustfoundry::telemetry::TelemetryContext;
414/// use rustfoundry::telemetry::log::{self, TestLogRecord};
415/// use rustfoundry::telemetry::settings::Level;
416///
417/// // Test context is used for demonstration purposes to show the resulting log records.
418/// let ctx = TelemetryContext::test();
419/// let _scope = ctx.scope();
420///
421/// // Simple log message
422/// log::trace!("Hello world!");
423///
424/// // Macro also accepts format arguments
425/// log::trace!("The values are: {}, {}", 42, true);
426///
427/// // Fields key-value pairs can be added to log record, by separating the format message
428/// // and fields by `;`.
429/// log::trace!("Answer: {}", 42; "foo" => "bar", "baz" => 1337);
430///
431/// assert_eq!(*ctx.log_records(), &[
432/// TestLogRecord {
433/// level: Level::Trace,
434/// message: "Hello world!".into(),
435/// fields: vec![]
436/// },
437/// TestLogRecord {
438/// level: Level::Trace,
439/// message: "The values are: 42, true".into(),
440/// fields: vec![]
441/// },
442/// TestLogRecord {
443/// level: Level::Trace,
444/// message: "Answer: 42".into(),
445/// fields: vec![
446/// ("baz".into(), "1337".into()),
447/// ("foo".into(), "bar".into())
448/// ]
449/// }
450/// ]);
451/// ```
452///
453/// [`LoggingSettings::redact_keys`]: crate::telemetry::settings::LoggingSettings::redact_keys
454#[macro_export]
455#[doc(hidden)]
456macro_rules! __trace {
457 ( $($args:tt)+ ) => {
458 $crate::reexports_for_macros::slog::trace!(
459 $crate::telemetry::log::internal::current_log().read(),
460 $($args)+
461 );
462 };
463}
464
465#[doc(inline)]
466pub use {
467 __add_fields as add_fields, __debug as debug, __error as error, __info as info,
468 __trace as trace, __warn as warn,
469};