Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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.

2 changes: 2 additions & 0 deletions crates/stackable-telemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ All notable changes to this project will be documented in this file.
### Added

- Introduce common `Settings` and subscriber specific settings ([#901]).
- Add support for logging to files ([#933]).

### Changed

- BREAKING: Renamed `TracingBuilder` methods with long names, and prefix with `with_` ([#901]).
- BREAKING: Use the new subscriber settings in the `TracingBuilder` ([#901]).

[#901]: https://github.com/stackabletech/operator-rs/pull/901
[#933]: https://github.com/stackabletech/operator-rs/pull/933

## [0.2.0] - 2024-07-10

Expand Down
1 change: 1 addition & 0 deletions crates/stackable-telemetry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ snafu.workspace = true
tokio.workspace = true
tower.workspace = true
tracing.workspace = true
tracing-appender.workspace = true
tracing-opentelemetry.workspace = true
tracing-subscriber = { workspace = true, features = ["env-filter"] }

Expand Down
83 changes: 75 additions & 8 deletions crates/stackable-telemetry/src/tracing/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! This module contains functionality to initialise tracing Subscribers for
//! console output, and OpenTelemetry OTLP export for traces and logs.
//! console output, file output, and OpenTelemetry OTLP export for traces and logs.
//!
//! It is intended to be used by the Stackable Data Platform operators and
//! webhooks, but it should be generic enough to be used in any application.
Expand All @@ -16,9 +16,10 @@ use opentelemetry_sdk::{
use opentelemetry_semantic_conventions::resource;
use snafu::{ResultExt as _, Snafu};
use tracing::subscriber::SetGlobalDefaultError;
use tracing_appender::rolling::{InitError, RollingFileAppender, Rotation};
use tracing_subscriber::{filter::Directive, layer::SubscriberExt, EnvFilter, Layer, Registry};

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

pub mod settings;

Expand All @@ -38,6 +39,9 @@ pub enum Error {

#[snafu(display("unable to set the global default subscriber"))]
SetGlobalDefaultSubscriber { source: SetGlobalDefaultError },

#[snafu(display("failed to initialize rolling file appender"))]
InitRollingFileAppender { source: InitError },
}

/// Easily initialize a set of preconfigured [`Subscriber`][1] layers.
Expand Down Expand Up @@ -151,6 +155,7 @@ pub enum Error {
pub struct Tracing {
service_name: &'static str,
console_log_settings: ConsoleLogSettings,
file_log_settings: FileLogSettings,
otlp_log_settings: OtlpLogSettings,
otlp_trace_settings: OtlpTraceSettings,
logger_provider: Option<LoggerProvider>,
Expand Down Expand Up @@ -181,6 +186,29 @@ impl Tracing {
layers.push(console_output_layer.boxed());
}

if self.file_log_settings.enabled {
let env_filter_layer = env_filter_builder(
self.file_log_settings.common_settings.environment_variable,
self.file_log_settings.default_level,
);

let file_appender = RollingFileAppender::builder()
.rotation(Rotation::HOURLY)
.filename_prefix(self.service_name.to_string())
.filename_suffix("tracing-rs.json")
.max_log_files(6)
.build(&self.file_log_settings.file_log_dir)
.context(InitRollingFileAppenderSnafu)?;

layers.push(
tracing_subscriber::fmt::layer()
.json()
.with_writer(file_appender)
.with_filter(env_filter_layer)
.boxed(),
);
}

if self.otlp_log_settings.enabled {
let env_filter_layer = env_filter_builder(
self.otlp_log_settings.environment_variable,
Expand Down Expand Up @@ -320,12 +348,6 @@ mod builder_state {
#[derive(Default)]
pub struct PreServiceName;

/// The state before the [`EnvFilter`][1] environment variable name is set.
///
/// [1]: tracing_subscriber::filter::EnvFilter
#[derive(Default)]
pub struct PreEnvVar;

/// The state that allows you to configure the supported [`Subscriber`][1]
/// [`Layer`][2].
///
Expand All @@ -349,6 +371,7 @@ pub struct TracingBuilder<S: BuilderState> {
console_log_settings: ConsoleLogSettings,
otlp_log_settings: OtlpLogSettings,
otlp_trace_settings: OtlpTraceSettings,
file_log_settings: FileLogSettings,

/// Allow the generic to be used (needed for impls).
_marker: std::marker::PhantomData<S>,
Expand Down Expand Up @@ -381,6 +404,26 @@ impl TracingBuilder<builder_state::Config> {
console_log_settings,
otlp_log_settings: self.otlp_log_settings,
otlp_trace_settings: self.otlp_trace_settings,
file_log_settings: self.file_log_settings,
_marker: self._marker,
}
}

/// Enable the file output tracing subscriber and set the default
/// [`LevelFilter`][1] which is overridable through the given environment
/// variable.
///
/// [1]: tracing_subscriber::filter::LevelFilter
pub fn with_file_output(
self,
file_log_settings: FileLogSettings,
) -> TracingBuilder<builder_state::Config> {
TracingBuilder {
service_name: self.service_name,
console_log_settings: self.console_log_settings,
file_log_settings,
otlp_log_settings: self.otlp_log_settings,
otlp_trace_settings: self.otlp_trace_settings,
_marker: self._marker,
}
}
Expand All @@ -401,6 +444,7 @@ impl TracingBuilder<builder_state::Config> {
console_log_settings: self.console_log_settings,
otlp_log_settings,
otlp_trace_settings: self.otlp_trace_settings,
file_log_settings: self.file_log_settings,
_marker: self._marker,
}
}
Expand All @@ -421,6 +465,7 @@ impl TracingBuilder<builder_state::Config> {
console_log_settings: self.console_log_settings,
otlp_log_settings: self.otlp_log_settings,
otlp_trace_settings,
file_log_settings: self.file_log_settings,
_marker: self._marker,
}
}
Expand All @@ -437,6 +482,7 @@ impl TracingBuilder<builder_state::Config> {
console_log_settings: self.console_log_settings,
otlp_log_settings: self.otlp_log_settings,
otlp_trace_settings: self.otlp_trace_settings,
file_log_settings: self.file_log_settings,
logger_provider: None,
}
}
Expand All @@ -452,6 +498,8 @@ fn env_filter_builder(env_var: &str, default_directive: impl Into<Directive>) ->

#[cfg(test)]
mod test {
use std::path::PathBuf;

use settings::{Build as _, Settings};
use tracing::level_filters::LevelFilter;

Expand Down Expand Up @@ -510,6 +558,14 @@ mod test {
.enabled(true)
.build(),
)
.with_file_output(
Settings::builder()
.with_environment_variable("ABC_FILE")
.with_default_level(LevelFilter::INFO)
.enabled(true)
.file_log_settings_builder(String::from("/abc_file_dir"))
.build(),
)
.with_otlp_log_exporter(
Settings::builder()
.with_environment_variable("ABC_OTLP_LOG")
Expand Down Expand Up @@ -537,6 +593,17 @@ mod test {
log_format: Default::default()
}
);
assert_eq!(
trace_guard.file_log_settings,
FileLogSettings {
common_settings: Settings {
enabled: true,
environment_variable: "ABC_FILE",
default_level: LevelFilter::INFO
},
file_log_dir: PathBuf::from("/abc_file_dir")
}
);
assert_eq!(
trace_guard.otlp_log_settings,
OtlpLogSettings {
Expand Down
70 changes: 70 additions & 0 deletions crates/stackable-telemetry/src/tracing/settings/file_log.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//! File Log Subscriber Settings.

use std::{ops::Deref, path::PathBuf};

use super::Settings;

/// Configure specific settings for the File Log subscriber.
#[derive(Debug, Default, PartialEq)]
pub struct FileLogSettings {
/// Common subscriber settings that apply to the File Log Subscriber.
pub common_settings: Settings,

/// Path to directory for log files.
pub file_log_dir: PathBuf,
}

impl Deref for FileLogSettings {
type Target = Settings;

fn deref(&self) -> &Self::Target {
&self.common_settings
}
}

/// For building [`FileLogSettings`].
///
/// <div class="warning">
/// Do not use directly, instead use the [`Settings::builder`] associated function.
/// </div>
pub struct FileLogSettingsBuilder {
pub(crate) common_settings: Settings,
pub(crate) file_log_dir: PathBuf,
}

impl FileLogSettingsBuilder {
/// Consumes self and returns a valid [`FileLogSettings`] instance.
pub fn build(self) -> FileLogSettings {
FileLogSettings {
common_settings: self.common_settings,
file_log_dir: self.file_log_dir,
}
}
}

#[cfg(test)]
mod test {
use tracing::level_filters::LevelFilter;

use super::*;

#[test]
fn builds_settings() {
let expected = FileLogSettings {
common_settings: Settings {
environment_variable: "hello",
default_level: LevelFilter::DEBUG,
enabled: true,
},
file_log_dir: PathBuf::from("/logs"),
};
let result = Settings::builder()
.with_environment_variable("hello")
.with_default_level(LevelFilter::DEBUG)
.enabled(true)
.file_log_settings_builder(String::from("/logs"))
.build();

assert_eq!(expected, result);
}
}
16 changes: 16 additions & 0 deletions crates/stackable-telemetry/src/tracing/settings/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
//! Subscriber settings.

use std::path::Path;

use tracing::level_filters::LevelFilter;

pub mod console_log;
pub use console_log::*;

pub mod file_log;
pub use file_log::*;

pub mod otlp_log;
pub use otlp_log::*;

Expand Down Expand Up @@ -105,6 +110,17 @@ impl SettingsBuilder {
self.into()
}

/// Set specific [`FileLogSettings`].
pub fn file_log_settings_builder<P>(self, path: P) -> FileLogSettingsBuilder
where
P: AsRef<Path>,
{
FileLogSettingsBuilder {
common_settings: self.build(),
file_log_dir: path.as_ref().to_path_buf(),
}
}

/// Set specific [`OtlpLogSettings`].
pub fn otlp_log_settings_builder(self) -> OtlpLogSettingsBuilder {
self.into()
Expand Down
Loading