Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(stackable-telemetry): Add RollingFileAppender #933

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
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,
);
NickLarsenNZ marked this conversation as resolved.
Show resolved Hide resolved

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: 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,
Comment on lines +482 to +491
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: #940 changed the API interface of these functions and as such I suggest to also change it here to be in line.

Suggested change
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,
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
Loading