Skip to content

Commit

Permalink
feat(stackable-telemetry): Add RollingFileAppender (#933)
Browse files Browse the repository at this point in the history
* feat: add RollingFileAppender to stackable-telemetry

* add changelog entry

* adjust docs

* adjust docs

* error handling, simplify filelogsettings creation

* Apply suggestions from code review

Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com>

* fix use statement

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

Co-authored-by: Techassi <git@techassi.dev>

---------

Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com>
Co-authored-by: Techassi <git@techassi.dev>
  • Loading branch information
3 people authored Jan 22, 2025
1 parent fc222e4 commit 4c2bfaa
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 7 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.

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
81 changes: 74 additions & 7 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,6 +16,7 @@ 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::*;
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 pre-configured [`Subscriber`][1] layers.
Expand Down Expand Up @@ -214,6 +218,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 @@ -246,6 +251,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 @@ -385,12 +413,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 @@ -414,6 +436,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 @@ -446,6 +469,26 @@ impl TracingBuilder<builder_state::Config> {
console_log_settings: console_log_settings.into(),
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: impl Into<FileLogSettings>,
) -> TracingBuilder<builder_state::Config> {
TracingBuilder {
service_name: self.service_name,
console_log_settings: self.console_log_settings,
file_log_settings: file_log_settings.into(),
otlp_log_settings: self.otlp_log_settings,
otlp_trace_settings: self.otlp_trace_settings,
_marker: self._marker,
}
}
Expand All @@ -466,6 +509,7 @@ impl TracingBuilder<builder_state::Config> {
console_log_settings: self.console_log_settings,
otlp_log_settings: otlp_log_settings.into(),
otlp_trace_settings: self.otlp_trace_settings,
file_log_settings: self.file_log_settings,
_marker: self._marker,
}
}
Expand All @@ -486,6 +530,7 @@ impl TracingBuilder<builder_state::Config> {
console_log_settings: self.console_log_settings,
otlp_log_settings: self.otlp_log_settings,
otlp_trace_settings: otlp_trace_settings.into(),
file_log_settings: self.file_log_settings,
_marker: self._marker,
}
}
Expand All @@ -502,6 +547,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 @@ -517,6 +563,8 @@ fn env_filter_builder(env_var: &str, default_directive: impl Into<Directive>) ->

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

use rstest::rstest;
use settings::Settings;
use tracing::level_filters::LevelFilter;
Expand Down Expand Up @@ -618,6 +666,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(PathBuf::from("/abc_file_dir"))
.build(),
)
.with_otlp_log_exporter(
Settings::builder()
.with_environment_variable("ABC_OTLP_LOG")
Expand Down Expand Up @@ -645,6 +701,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(PathBuf::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 @@ -89,6 +94,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

0 comments on commit 4c2bfaa

Please sign in to comment.