Skip to content

Commit

Permalink
feat(stackable-telemetry): Add tuple to settings conversions (#940)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Techassi authored Jan 17, 2025
1 parent af0d1f1 commit 68acaaf
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 53 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/stackable-telemetry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] }
[dev-dependencies]
tokio.workspace = true
tracing-opentelemetry.workspace = true
rstest.workspace = true
stackable-webhook = { path = "../stackable-webhook" }

[package.metadata.cargo-udeps.ignore]
Expand Down
138 changes: 123 additions & 15 deletions crates/stackable-telemetry/src/tracing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use snafu::{ResultExt as _, Snafu};
use tracing::subscriber::SetGlobalDefaultError;
use tracing_subscriber::{filter::Directive, layer::SubscriberExt, EnvFilter, Layer, Registry};

use settings::{ConsoleLogSettings, OtlpLogSettings, OtlpTraceSettings};
use settings::*;

pub mod settings;

Expand All @@ -40,11 +40,46 @@ pub enum Error {
SetGlobalDefaultSubscriber { source: SetGlobalDefaultError },
}

/// Easily initialize a set of preconfigured [`Subscriber`][1] layers.
/// Easily initialize a set of pre-configured [`Subscriber`][1] layers.
///
/// # Usage:
/// # Usage
///
/// There are two different styles to configure individual subscribers: Using the sophisticated
/// [`SettingsBuilder`] or the simplified tuple style for basic configuration. Currently, three
/// different subscribers are supported: console output, OTLP log export, and OTLP trace export.
///
/// The subscribers are active as long as the tracing guard returned by [`Tracing::init`] is in
/// scope and not dropped. Dropping it results in subscribers being shut down, which can lead to
/// loss of telemetry data when done before exiting the application. This is why it is important
/// to hold onto the guard as long as required.
///
/// <div class="warning">
/// Name the guard variable appropriately, do not just use <code>let _ =</code>, as that will drop
/// immediately.
/// </div>
///
/// ```
/// # use stackable_telemetry::tracing::{Tracing, Error};
/// #[tokio::main]
/// async fn main() -> Result<(), Error> {
/// let _tracing_guard = Tracing::builder() // < Scope starts here
/// .service_name("test") // |
/// .build() // |
/// .init()?; // |
/// // |
/// tracing::info!("log a message"); // |
/// Ok(()) // < Scope ends here, guard is dropped
/// }
/// ```
/// use stackable_telemetry::tracing::{Tracing, Error, settings::{Build as _, Settings}};
///
/// ## Basic configuration
///
/// A basic configuration of subscribers can be done by using 2-tuples or 3-tuples, also called
/// doubles and triples. Using tuples, the subscriber can be enabled/disabled and it's environment
/// variable and default level can be set.
///
/// ```
/// use stackable_telemetry::tracing::{Tracing, Error, settings::Settings};
/// use tracing_subscriber::filter::LevelFilter;
///
/// #[tokio::main]
Expand All @@ -54,8 +89,36 @@ pub enum Error {
/// // runtime.
/// let otlp_log_flag = false;
///
/// // IMPORTANT: Name the guard variable appropriately, do not just use
/// // `let _ =`, as that will drop immediately.
/// let _tracing_guard = Tracing::builder()
/// .service_name("test")
/// .with_console_output(("TEST_CONSOLE", LevelFilter::INFO))
/// .with_otlp_log_exporter(("TEST_OTLP_LOG", LevelFilter::DEBUG, otlp_log_flag))
/// .build()
/// .init()?;
///
/// tracing::info!("log a message");
///
/// Ok(())
/// }
/// ```
///
/// ## Advanced configuration
///
/// More advanced configurations can be done via the [`Settings::builder`] function. Each
/// subscriber provides specific settings based on a common set of options. These options can be
/// customized via the following methods:
///
/// - [`SettingsBuilder::console_log_settings_builder`]
/// - [`SettingsBuilder::otlp_log_settings_builder`]
/// - [`SettingsBuilder::otlp_trace_settings_builder`]
///
/// ```
/// # use stackable_telemetry::tracing::{Tracing, Error, settings::Settings};
/// # use tracing_subscriber::filter::LevelFilter;
/// #[tokio::main]
/// async fn main() -> Result<(), Error> {
/// let otlp_log_flag = false;
///
/// let _tracing_guard = Tracing::builder()
/// .service_name("test")
/// .with_console_output(
Expand Down Expand Up @@ -164,8 +227,10 @@ impl Tracing {
/// Initialise the configured tracing subscribers, returning a guard that
/// will shutdown the subscribers when dropped.
///
/// IMPORTANT: Name the guard variable appropriately, do not just use
/// `let _ =`, as that will drop immediately.
/// <div class="warning">
/// Name the guard variable appropriately, do not just use <code>let _ =</code>, as that will drop
/// immediately.
/// </div>
pub fn init(mut self) -> Result<Tracing> {
let mut layers: Vec<Box<dyn Layer<Registry> + Sync + Send>> = Vec::new();

Expand Down Expand Up @@ -374,11 +439,11 @@ impl TracingBuilder<builder_state::Config> {
/// [1]: tracing_subscriber::filter::LevelFilter
pub fn with_console_output(
self,
console_log_settings: ConsoleLogSettings,
console_log_settings: impl Into<ConsoleLogSettings>,
) -> TracingBuilder<builder_state::Config> {
TracingBuilder {
service_name: self.service_name,
console_log_settings,
console_log_settings: console_log_settings.into(),
otlp_log_settings: self.otlp_log_settings,
otlp_trace_settings: self.otlp_trace_settings,
_marker: self._marker,
Expand All @@ -394,12 +459,12 @@ impl TracingBuilder<builder_state::Config> {
/// [1]: tracing_subscriber::filter::LevelFilter
pub fn with_otlp_log_exporter(
self,
otlp_log_settings: OtlpLogSettings,
otlp_log_settings: impl Into<OtlpLogSettings>,
) -> TracingBuilder<builder_state::Config> {
TracingBuilder {
service_name: self.service_name,
console_log_settings: self.console_log_settings,
otlp_log_settings,
otlp_log_settings: otlp_log_settings.into(),
otlp_trace_settings: self.otlp_trace_settings,
_marker: self._marker,
}
Expand All @@ -414,13 +479,13 @@ impl TracingBuilder<builder_state::Config> {
/// [1]: tracing_subscriber::filter::LevelFilter
pub fn with_otlp_trace_exporter(
self,
otlp_trace_settings: OtlpTraceSettings,
otlp_trace_settings: impl Into<OtlpTraceSettings>,
) -> TracingBuilder<builder_state::Config> {
TracingBuilder {
service_name: self.service_name,
console_log_settings: self.console_log_settings,
otlp_log_settings: self.otlp_log_settings,
otlp_trace_settings,
otlp_trace_settings: otlp_trace_settings.into(),
_marker: self._marker,
}
}
Expand Down Expand Up @@ -452,7 +517,8 @@ fn env_filter_builder(env_var: &str, default_directive: impl Into<Directive>) ->

#[cfg(test)]
mod test {
use settings::{Build as _, Settings};
use rstest::rstest;
use settings::Settings;
use tracing::level_filters::LevelFilter;

use super::*;
Expand Down Expand Up @@ -499,6 +565,48 @@ mod test {
assert!(!trace_guard.otlp_trace_settings.enabled);
}

#[test]
fn builder_with_console_output_double() {
let trace_guard = Tracing::builder()
.service_name("test")
.with_console_output(("ABC_A", LevelFilter::TRACE))
.build();

assert_eq!(
trace_guard.console_log_settings,
ConsoleLogSettings {
common_settings: Settings {
environment_variable: "ABC_A",
default_level: LevelFilter::TRACE,
enabled: true
},
log_format: Default::default()
}
)
}

#[rstest]
#[case(false)]
#[case(true)]
fn builder_with_console_output_triple(#[case] enabled: bool) {
let trace_guard = Tracing::builder()
.service_name("test")
.with_console_output(("ABC_A", LevelFilter::TRACE, enabled))
.build();

assert_eq!(
trace_guard.console_log_settings,
ConsoleLogSettings {
common_settings: Settings {
environment_variable: "ABC_A",
default_level: LevelFilter::TRACE,
enabled
},
log_format: Default::default()
}
)
}

#[test]
fn builder_with_all() {
let trace_guard = Tracing::builder()
Expand Down
38 changes: 32 additions & 6 deletions crates/stackable-telemetry/src/tracing/settings/console_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
use std::ops::Deref;

use super::{Build, Settings, SettingsBuilder};
use tracing::level_filters::LevelFilter;

use super::{Settings, SettingsBuilder};

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

/// This implementation is used to build console log settings from common settings without
/// specifying console log specific settings.
impl Build<ConsoleLogSettings> for SettingsBuilder {
fn build(self) -> ConsoleLogSettings {
impl From<Settings> for ConsoleLogSettings {
fn from(common_settings: Settings) -> Self {
ConsoleLogSettings {
common_settings: self.build(),
common_settings,
..Default::default()
}
}
}

impl From<(&'static str, LevelFilter)> for ConsoleLogSettings {
fn from(value: (&'static str, LevelFilter)) -> Self {
Self {
common_settings: Settings {
environment_variable: value.0,
default_level: value.1,
enabled: true,
},
..Default::default()
}
}
}

impl From<(&'static str, LevelFilter, bool)> for ConsoleLogSettings {
fn from(value: (&'static str, LevelFilter, bool)) -> Self {
Self {
common_settings: Settings {
environment_variable: value.0,
default_level: value.1,
enabled: value.2,
},
..Default::default()
}
}
Expand Down
25 changes: 9 additions & 16 deletions crates/stackable-telemetry/src/tracing/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,6 @@ pub struct SettingsBuilder {
default_level: LevelFilter,
}

/// Finalizer to be implemented on builders.
pub trait Build<T> {
/// Finalize settings.
fn build(self) -> T;
}

impl Build<Settings> for SettingsBuilder {
fn build(self) -> Settings {
Settings {
environment_variable: self.environment_variable,
default_level: self.default_level,
enabled: self.enabled,
}
}
}

impl SettingsBuilder {
/// Set the environment variable used for overriding the [`Settings::default_level`].
///
Expand Down Expand Up @@ -114,6 +98,15 @@ impl SettingsBuilder {
pub fn otlp_trace_settings_builder(self) -> OtlpTraceSettingsBuilder {
self.into()
}

/// Consumes self and constructs valid [`Settings`].
pub fn build(self) -> Settings {
Settings {
environment_variable: self.environment_variable,
default_level: self.default_level,
enabled: self.enabled,
}
}
}

impl Default for SettingsBuilder {
Expand Down
37 changes: 29 additions & 8 deletions crates/stackable-telemetry/src/tracing/settings/otlp_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
use std::ops::Deref;

use super::{Build, Settings, SettingsBuilder};
use tracing::level_filters::LevelFilter;

use super::{Settings, SettingsBuilder};

#[derive(Debug, Default, PartialEq)]
pub struct OtlpLogSettings {
Expand Down Expand Up @@ -39,13 +41,32 @@ impl From<SettingsBuilder> for OtlpLogSettingsBuilder {
}
}

/// This implementation is used to build OTLP log settings from common settings without
/// specifying OTLP log specific settings.
impl Build<OtlpLogSettings> for SettingsBuilder {
fn build(self) -> OtlpLogSettings {
OtlpLogSettings {
common_settings: self.build(),
// ..Default::default()
impl From<Settings> for OtlpLogSettings {
fn from(common_settings: Settings) -> Self {
Self { common_settings }
}
}

impl From<(&'static str, LevelFilter)> for OtlpLogSettings {
fn from(value: (&'static str, LevelFilter)) -> Self {
Self {
common_settings: Settings {
environment_variable: value.0,
default_level: value.1,
enabled: true,
},
}
}
}

impl From<(&'static str, LevelFilter, bool)> for OtlpLogSettings {
fn from(value: (&'static str, LevelFilter, bool)) -> Self {
Self {
common_settings: Settings {
environment_variable: value.0,
default_level: value.1,
enabled: value.2,
},
}
}
}
Expand Down
Loading

0 comments on commit 68acaaf

Please sign in to comment.