Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading