Skip to content

Commit 68acaaf

Browse files
authored
feat(stackable-telemetry): Add tuple to settings conversions (#940)
* feat: Add tuple to settings conversions * test: Fix doc test import * docs: Improve usage doc comments * test: Fix doc test return value * docs: Add section about advanced configuration
1 parent af0d1f1 commit 68acaaf

File tree

7 files changed

+224
-53
lines changed

7 files changed

+224
-53
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/stackable-telemetry/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] }
2525
[dev-dependencies]
2626
tokio.workspace = true
2727
tracing-opentelemetry.workspace = true
28+
rstest.workspace = true
2829
stackable-webhook = { path = "../stackable-webhook" }
2930

3031
[package.metadata.cargo-udeps.ignore]

crates/stackable-telemetry/src/tracing/mod.rs

Lines changed: 123 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use snafu::{ResultExt as _, Snafu};
1818
use tracing::subscriber::SetGlobalDefaultError;
1919
use tracing_subscriber::{filter::Directive, layer::SubscriberExt, EnvFilter, Layer, Registry};
2020

21-
use settings::{ConsoleLogSettings, OtlpLogSettings, OtlpTraceSettings};
21+
use settings::*;
2222

2323
pub mod settings;
2424

@@ -40,11 +40,46 @@ pub enum Error {
4040
SetGlobalDefaultSubscriber { source: SetGlobalDefaultError },
4141
}
4242

43-
/// Easily initialize a set of preconfigured [`Subscriber`][1] layers.
43+
/// Easily initialize a set of pre-configured [`Subscriber`][1] layers.
4444
///
45-
/// # Usage:
45+
/// # Usage
46+
///
47+
/// There are two different styles to configure individual subscribers: Using the sophisticated
48+
/// [`SettingsBuilder`] or the simplified tuple style for basic configuration. Currently, three
49+
/// different subscribers are supported: console output, OTLP log export, and OTLP trace export.
50+
///
51+
/// The subscribers are active as long as the tracing guard returned by [`Tracing::init`] is in
52+
/// scope and not dropped. Dropping it results in subscribers being shut down, which can lead to
53+
/// loss of telemetry data when done before exiting the application. This is why it is important
54+
/// to hold onto the guard as long as required.
55+
///
56+
/// <div class="warning">
57+
/// Name the guard variable appropriately, do not just use <code>let _ =</code>, as that will drop
58+
/// immediately.
59+
/// </div>
60+
///
61+
/// ```
62+
/// # use stackable_telemetry::tracing::{Tracing, Error};
63+
/// #[tokio::main]
64+
/// async fn main() -> Result<(), Error> {
65+
/// let _tracing_guard = Tracing::builder() // < Scope starts here
66+
/// .service_name("test") // |
67+
/// .build() // |
68+
/// .init()?; // |
69+
/// // |
70+
/// tracing::info!("log a message"); // |
71+
/// Ok(()) // < Scope ends here, guard is dropped
72+
/// }
4673
/// ```
47-
/// use stackable_telemetry::tracing::{Tracing, Error, settings::{Build as _, Settings}};
74+
///
75+
/// ## Basic configuration
76+
///
77+
/// A basic configuration of subscribers can be done by using 2-tuples or 3-tuples, also called
78+
/// doubles and triples. Using tuples, the subscriber can be enabled/disabled and it's environment
79+
/// variable and default level can be set.
80+
///
81+
/// ```
82+
/// use stackable_telemetry::tracing::{Tracing, Error, settings::Settings};
4883
/// use tracing_subscriber::filter::LevelFilter;
4984
///
5085
/// #[tokio::main]
@@ -54,8 +89,36 @@ pub enum Error {
5489
/// // runtime.
5590
/// let otlp_log_flag = false;
5691
///
57-
/// // IMPORTANT: Name the guard variable appropriately, do not just use
58-
/// // `let _ =`, as that will drop immediately.
92+
/// let _tracing_guard = Tracing::builder()
93+
/// .service_name("test")
94+
/// .with_console_output(("TEST_CONSOLE", LevelFilter::INFO))
95+
/// .with_otlp_log_exporter(("TEST_OTLP_LOG", LevelFilter::DEBUG, otlp_log_flag))
96+
/// .build()
97+
/// .init()?;
98+
///
99+
/// tracing::info!("log a message");
100+
///
101+
/// Ok(())
102+
/// }
103+
/// ```
104+
///
105+
/// ## Advanced configuration
106+
///
107+
/// More advanced configurations can be done via the [`Settings::builder`] function. Each
108+
/// subscriber provides specific settings based on a common set of options. These options can be
109+
/// customized via the following methods:
110+
///
111+
/// - [`SettingsBuilder::console_log_settings_builder`]
112+
/// - [`SettingsBuilder::otlp_log_settings_builder`]
113+
/// - [`SettingsBuilder::otlp_trace_settings_builder`]
114+
///
115+
/// ```
116+
/// # use stackable_telemetry::tracing::{Tracing, Error, settings::Settings};
117+
/// # use tracing_subscriber::filter::LevelFilter;
118+
/// #[tokio::main]
119+
/// async fn main() -> Result<(), Error> {
120+
/// let otlp_log_flag = false;
121+
///
59122
/// let _tracing_guard = Tracing::builder()
60123
/// .service_name("test")
61124
/// .with_console_output(
@@ -164,8 +227,10 @@ impl Tracing {
164227
/// Initialise the configured tracing subscribers, returning a guard that
165228
/// will shutdown the subscribers when dropped.
166229
///
167-
/// IMPORTANT: Name the guard variable appropriately, do not just use
168-
/// `let _ =`, as that will drop immediately.
230+
/// <div class="warning">
231+
/// Name the guard variable appropriately, do not just use <code>let _ =</code>, as that will drop
232+
/// immediately.
233+
/// </div>
169234
pub fn init(mut self) -> Result<Tracing> {
170235
let mut layers: Vec<Box<dyn Layer<Registry> + Sync + Send>> = Vec::new();
171236

@@ -374,11 +439,11 @@ impl TracingBuilder<builder_state::Config> {
374439
/// [1]: tracing_subscriber::filter::LevelFilter
375440
pub fn with_console_output(
376441
self,
377-
console_log_settings: ConsoleLogSettings,
442+
console_log_settings: impl Into<ConsoleLogSettings>,
378443
) -> TracingBuilder<builder_state::Config> {
379444
TracingBuilder {
380445
service_name: self.service_name,
381-
console_log_settings,
446+
console_log_settings: console_log_settings.into(),
382447
otlp_log_settings: self.otlp_log_settings,
383448
otlp_trace_settings: self.otlp_trace_settings,
384449
_marker: self._marker,
@@ -394,12 +459,12 @@ impl TracingBuilder<builder_state::Config> {
394459
/// [1]: tracing_subscriber::filter::LevelFilter
395460
pub fn with_otlp_log_exporter(
396461
self,
397-
otlp_log_settings: OtlpLogSettings,
462+
otlp_log_settings: impl Into<OtlpLogSettings>,
398463
) -> TracingBuilder<builder_state::Config> {
399464
TracingBuilder {
400465
service_name: self.service_name,
401466
console_log_settings: self.console_log_settings,
402-
otlp_log_settings,
467+
otlp_log_settings: otlp_log_settings.into(),
403468
otlp_trace_settings: self.otlp_trace_settings,
404469
_marker: self._marker,
405470
}
@@ -414,13 +479,13 @@ impl TracingBuilder<builder_state::Config> {
414479
/// [1]: tracing_subscriber::filter::LevelFilter
415480
pub fn with_otlp_trace_exporter(
416481
self,
417-
otlp_trace_settings: OtlpTraceSettings,
482+
otlp_trace_settings: impl Into<OtlpTraceSettings>,
418483
) -> TracingBuilder<builder_state::Config> {
419484
TracingBuilder {
420485
service_name: self.service_name,
421486
console_log_settings: self.console_log_settings,
422487
otlp_log_settings: self.otlp_log_settings,
423-
otlp_trace_settings,
488+
otlp_trace_settings: otlp_trace_settings.into(),
424489
_marker: self._marker,
425490
}
426491
}
@@ -452,7 +517,8 @@ fn env_filter_builder(env_var: &str, default_directive: impl Into<Directive>) ->
452517

453518
#[cfg(test)]
454519
mod test {
455-
use settings::{Build as _, Settings};
520+
use rstest::rstest;
521+
use settings::Settings;
456522
use tracing::level_filters::LevelFilter;
457523

458524
use super::*;
@@ -499,6 +565,48 @@ mod test {
499565
assert!(!trace_guard.otlp_trace_settings.enabled);
500566
}
501567

568+
#[test]
569+
fn builder_with_console_output_double() {
570+
let trace_guard = Tracing::builder()
571+
.service_name("test")
572+
.with_console_output(("ABC_A", LevelFilter::TRACE))
573+
.build();
574+
575+
assert_eq!(
576+
trace_guard.console_log_settings,
577+
ConsoleLogSettings {
578+
common_settings: Settings {
579+
environment_variable: "ABC_A",
580+
default_level: LevelFilter::TRACE,
581+
enabled: true
582+
},
583+
log_format: Default::default()
584+
}
585+
)
586+
}
587+
588+
#[rstest]
589+
#[case(false)]
590+
#[case(true)]
591+
fn builder_with_console_output_triple(#[case] enabled: bool) {
592+
let trace_guard = Tracing::builder()
593+
.service_name("test")
594+
.with_console_output(("ABC_A", LevelFilter::TRACE, enabled))
595+
.build();
596+
597+
assert_eq!(
598+
trace_guard.console_log_settings,
599+
ConsoleLogSettings {
600+
common_settings: Settings {
601+
environment_variable: "ABC_A",
602+
default_level: LevelFilter::TRACE,
603+
enabled
604+
},
605+
log_format: Default::default()
606+
}
607+
)
608+
}
609+
502610
#[test]
503611
fn builder_with_all() {
504612
let trace_guard = Tracing::builder()

crates/stackable-telemetry/src/tracing/settings/console_log.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
33
use std::ops::Deref;
44

5-
use super::{Build, Settings, SettingsBuilder};
5+
use tracing::level_filters::LevelFilter;
6+
7+
use super::{Settings, SettingsBuilder};
68

79
/// Configure specific settings for the Console Log subscriber.
810
#[derive(Debug, Default, PartialEq)]
@@ -74,12 +76,36 @@ impl From<SettingsBuilder> for ConsoleLogSettingsBuilder {
7476
}
7577
}
7678

77-
/// This implementation is used to build console log settings from common settings without
78-
/// specifying console log specific settings.
79-
impl Build<ConsoleLogSettings> for SettingsBuilder {
80-
fn build(self) -> ConsoleLogSettings {
79+
impl From<Settings> for ConsoleLogSettings {
80+
fn from(common_settings: Settings) -> Self {
8181
ConsoleLogSettings {
82-
common_settings: self.build(),
82+
common_settings,
83+
..Default::default()
84+
}
85+
}
86+
}
87+
88+
impl From<(&'static str, LevelFilter)> for ConsoleLogSettings {
89+
fn from(value: (&'static str, LevelFilter)) -> Self {
90+
Self {
91+
common_settings: Settings {
92+
environment_variable: value.0,
93+
default_level: value.1,
94+
enabled: true,
95+
},
96+
..Default::default()
97+
}
98+
}
99+
}
100+
101+
impl From<(&'static str, LevelFilter, bool)> for ConsoleLogSettings {
102+
fn from(value: (&'static str, LevelFilter, bool)) -> Self {
103+
Self {
104+
common_settings: Settings {
105+
environment_variable: value.0,
106+
default_level: value.1,
107+
enabled: value.2,
108+
},
83109
..Default::default()
84110
}
85111
}

crates/stackable-telemetry/src/tracing/settings/mod.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,6 @@ pub struct SettingsBuilder {
5151
default_level: LevelFilter,
5252
}
5353

54-
/// Finalizer to be implemented on builders.
55-
pub trait Build<T> {
56-
/// Finalize settings.
57-
fn build(self) -> T;
58-
}
59-
60-
impl Build<Settings> for SettingsBuilder {
61-
fn build(self) -> Settings {
62-
Settings {
63-
environment_variable: self.environment_variable,
64-
default_level: self.default_level,
65-
enabled: self.enabled,
66-
}
67-
}
68-
}
69-
7054
impl SettingsBuilder {
7155
/// Set the environment variable used for overriding the [`Settings::default_level`].
7256
///
@@ -114,6 +98,15 @@ impl SettingsBuilder {
11498
pub fn otlp_trace_settings_builder(self) -> OtlpTraceSettingsBuilder {
11599
self.into()
116100
}
101+
102+
/// Consumes self and constructs valid [`Settings`].
103+
pub fn build(self) -> Settings {
104+
Settings {
105+
environment_variable: self.environment_variable,
106+
default_level: self.default_level,
107+
enabled: self.enabled,
108+
}
109+
}
117110
}
118111

119112
impl Default for SettingsBuilder {

crates/stackable-telemetry/src/tracing/settings/otlp_log.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
33
use std::ops::Deref;
44

5-
use super::{Build, Settings, SettingsBuilder};
5+
use tracing::level_filters::LevelFilter;
6+
7+
use super::{Settings, SettingsBuilder};
68

79
#[derive(Debug, Default, PartialEq)]
810
pub struct OtlpLogSettings {
@@ -39,13 +41,32 @@ impl From<SettingsBuilder> for OtlpLogSettingsBuilder {
3941
}
4042
}
4143

42-
/// This implementation is used to build OTLP log settings from common settings without
43-
/// specifying OTLP log specific settings.
44-
impl Build<OtlpLogSettings> for SettingsBuilder {
45-
fn build(self) -> OtlpLogSettings {
46-
OtlpLogSettings {
47-
common_settings: self.build(),
48-
// ..Default::default()
44+
impl From<Settings> for OtlpLogSettings {
45+
fn from(common_settings: Settings) -> Self {
46+
Self { common_settings }
47+
}
48+
}
49+
50+
impl From<(&'static str, LevelFilter)> for OtlpLogSettings {
51+
fn from(value: (&'static str, LevelFilter)) -> Self {
52+
Self {
53+
common_settings: Settings {
54+
environment_variable: value.0,
55+
default_level: value.1,
56+
enabled: true,
57+
},
58+
}
59+
}
60+
}
61+
62+
impl From<(&'static str, LevelFilter, bool)> for OtlpLogSettings {
63+
fn from(value: (&'static str, LevelFilter, bool)) -> Self {
64+
Self {
65+
common_settings: Settings {
66+
environment_variable: value.0,
67+
default_level: value.1,
68+
enabled: value.2,
69+
},
4970
}
5071
}
5172
}

0 commit comments

Comments
 (0)