Skip to content

Commit 4c2bfaa

Browse files
xeniapeNickLarsenNZTechassi
authored
feat(stackable-telemetry): Add RollingFileAppender (#933)
* 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 <[email protected]> * fix use statement * Update crates/stackable-telemetry/src/tracing/mod.rs Co-authored-by: Techassi <[email protected]> --------- Co-authored-by: Nick <[email protected]> Co-authored-by: Techassi <[email protected]>
1 parent fc222e4 commit 4c2bfaa

File tree

6 files changed

+164
-7
lines changed

6 files changed

+164
-7
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/stackable-telemetry/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ All notable changes to this project will be documented in this file.
77
### Added
88

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

1112
### Changed
1213

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

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

1820
## [0.2.0] - 2024-07-10
1921

crates/stackable-telemetry/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ snafu.workspace = true
1919
tokio.workspace = true
2020
tower.workspace = true
2121
tracing.workspace = true
22+
tracing-appender.workspace = true
2223
tracing-opentelemetry.workspace = true
2324
tracing-subscriber = { workspace = true, features = ["env-filter"] }
2425

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

+74-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! This module contains functionality to initialise tracing Subscribers for
2-
//! console output, and OpenTelemetry OTLP export for traces and logs.
2+
//! console output, file output, and OpenTelemetry OTLP export for traces and logs.
33
//!
44
//! It is intended to be used by the Stackable Data Platform operators and
55
//! webhooks, but it should be generic enough to be used in any application.
@@ -16,6 +16,7 @@ use opentelemetry_sdk::{
1616
use opentelemetry_semantic_conventions::resource;
1717
use snafu::{ResultExt as _, Snafu};
1818
use tracing::subscriber::SetGlobalDefaultError;
19+
use tracing_appender::rolling::{InitError, RollingFileAppender, Rotation};
1920
use tracing_subscriber::{filter::Directive, layer::SubscriberExt, EnvFilter, Layer, Registry};
2021

2122
use settings::*;
@@ -38,6 +39,9 @@ pub enum Error {
3839

3940
#[snafu(display("unable to set the global default subscriber"))]
4041
SetGlobalDefaultSubscriber { source: SetGlobalDefaultError },
42+
43+
#[snafu(display("failed to initialize rolling file appender"))]
44+
InitRollingFileAppender { source: InitError },
4145
}
4246

4347
/// Easily initialize a set of pre-configured [`Subscriber`][1] layers.
@@ -214,6 +218,7 @@ pub enum Error {
214218
pub struct Tracing {
215219
service_name: &'static str,
216220
console_log_settings: ConsoleLogSettings,
221+
file_log_settings: FileLogSettings,
217222
otlp_log_settings: OtlpLogSettings,
218223
otlp_trace_settings: OtlpTraceSettings,
219224
logger_provider: Option<LoggerProvider>,
@@ -246,6 +251,29 @@ impl Tracing {
246251
layers.push(console_output_layer.boxed());
247252
}
248253

254+
if self.file_log_settings.enabled {
255+
let env_filter_layer = env_filter_builder(
256+
self.file_log_settings.common_settings.environment_variable,
257+
self.file_log_settings.default_level,
258+
);
259+
260+
let file_appender = RollingFileAppender::builder()
261+
.rotation(Rotation::HOURLY)
262+
.filename_prefix(self.service_name.to_string())
263+
.filename_suffix("tracing-rs.json")
264+
.max_log_files(6)
265+
.build(&self.file_log_settings.file_log_dir)
266+
.context(InitRollingFileAppenderSnafu)?;
267+
268+
layers.push(
269+
tracing_subscriber::fmt::layer()
270+
.json()
271+
.with_writer(file_appender)
272+
.with_filter(env_filter_layer)
273+
.boxed(),
274+
);
275+
}
276+
249277
if self.otlp_log_settings.enabled {
250278
let env_filter_layer = env_filter_builder(
251279
self.otlp_log_settings.environment_variable,
@@ -385,12 +413,6 @@ mod builder_state {
385413
#[derive(Default)]
386414
pub struct PreServiceName;
387415

388-
/// The state before the [`EnvFilter`][1] environment variable name is set.
389-
///
390-
/// [1]: tracing_subscriber::filter::EnvFilter
391-
#[derive(Default)]
392-
pub struct PreEnvVar;
393-
394416
/// The state that allows you to configure the supported [`Subscriber`][1]
395417
/// [`Layer`][2].
396418
///
@@ -414,6 +436,7 @@ pub struct TracingBuilder<S: BuilderState> {
414436
console_log_settings: ConsoleLogSettings,
415437
otlp_log_settings: OtlpLogSettings,
416438
otlp_trace_settings: OtlpTraceSettings,
439+
file_log_settings: FileLogSettings,
417440

418441
/// Allow the generic to be used (needed for impls).
419442
_marker: std::marker::PhantomData<S>,
@@ -446,6 +469,26 @@ impl TracingBuilder<builder_state::Config> {
446469
console_log_settings: console_log_settings.into(),
447470
otlp_log_settings: self.otlp_log_settings,
448471
otlp_trace_settings: self.otlp_trace_settings,
472+
file_log_settings: self.file_log_settings,
473+
_marker: self._marker,
474+
}
475+
}
476+
477+
/// Enable the file output tracing subscriber and set the default
478+
/// [`LevelFilter`][1] which is overridable through the given environment
479+
/// variable.
480+
///
481+
/// [1]: tracing_subscriber::filter::LevelFilter
482+
pub fn with_file_output(
483+
self,
484+
file_log_settings: impl Into<FileLogSettings>,
485+
) -> TracingBuilder<builder_state::Config> {
486+
TracingBuilder {
487+
service_name: self.service_name,
488+
console_log_settings: self.console_log_settings,
489+
file_log_settings: file_log_settings.into(),
490+
otlp_log_settings: self.otlp_log_settings,
491+
otlp_trace_settings: self.otlp_trace_settings,
449492
_marker: self._marker,
450493
}
451494
}
@@ -466,6 +509,7 @@ impl TracingBuilder<builder_state::Config> {
466509
console_log_settings: self.console_log_settings,
467510
otlp_log_settings: otlp_log_settings.into(),
468511
otlp_trace_settings: self.otlp_trace_settings,
512+
file_log_settings: self.file_log_settings,
469513
_marker: self._marker,
470514
}
471515
}
@@ -486,6 +530,7 @@ impl TracingBuilder<builder_state::Config> {
486530
console_log_settings: self.console_log_settings,
487531
otlp_log_settings: self.otlp_log_settings,
488532
otlp_trace_settings: otlp_trace_settings.into(),
533+
file_log_settings: self.file_log_settings,
489534
_marker: self._marker,
490535
}
491536
}
@@ -502,6 +547,7 @@ impl TracingBuilder<builder_state::Config> {
502547
console_log_settings: self.console_log_settings,
503548
otlp_log_settings: self.otlp_log_settings,
504549
otlp_trace_settings: self.otlp_trace_settings,
550+
file_log_settings: self.file_log_settings,
505551
logger_provider: None,
506552
}
507553
}
@@ -517,6 +563,8 @@ fn env_filter_builder(env_var: &str, default_directive: impl Into<Directive>) ->
517563

518564
#[cfg(test)]
519565
mod test {
566+
use std::path::PathBuf;
567+
520568
use rstest::rstest;
521569
use settings::Settings;
522570
use tracing::level_filters::LevelFilter;
@@ -618,6 +666,14 @@ mod test {
618666
.enabled(true)
619667
.build(),
620668
)
669+
.with_file_output(
670+
Settings::builder()
671+
.with_environment_variable("ABC_FILE")
672+
.with_default_level(LevelFilter::INFO)
673+
.enabled(true)
674+
.file_log_settings_builder(PathBuf::from("/abc_file_dir"))
675+
.build(),
676+
)
621677
.with_otlp_log_exporter(
622678
Settings::builder()
623679
.with_environment_variable("ABC_OTLP_LOG")
@@ -645,6 +701,17 @@ mod test {
645701
log_format: Default::default()
646702
}
647703
);
704+
assert_eq!(
705+
trace_guard.file_log_settings,
706+
FileLogSettings {
707+
common_settings: Settings {
708+
enabled: true,
709+
environment_variable: "ABC_FILE",
710+
default_level: LevelFilter::INFO
711+
},
712+
file_log_dir: PathBuf::from("/abc_file_dir")
713+
}
714+
);
648715
assert_eq!(
649716
trace_guard.otlp_log_settings,
650717
OtlpLogSettings {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//! File Log Subscriber Settings.
2+
3+
use std::{ops::Deref, path::PathBuf};
4+
5+
use super::Settings;
6+
7+
/// Configure specific settings for the File Log subscriber.
8+
#[derive(Debug, Default, PartialEq)]
9+
pub struct FileLogSettings {
10+
/// Common subscriber settings that apply to the File Log Subscriber.
11+
pub common_settings: Settings,
12+
13+
/// Path to directory for log files.
14+
pub file_log_dir: PathBuf,
15+
}
16+
17+
impl Deref for FileLogSettings {
18+
type Target = Settings;
19+
20+
fn deref(&self) -> &Self::Target {
21+
&self.common_settings
22+
}
23+
}
24+
25+
/// For building [`FileLogSettings`].
26+
///
27+
/// <div class="warning">
28+
/// Do not use directly, instead use the [`Settings::builder`] associated function.
29+
/// </div>
30+
pub struct FileLogSettingsBuilder {
31+
pub(crate) common_settings: Settings,
32+
pub(crate) file_log_dir: PathBuf,
33+
}
34+
35+
impl FileLogSettingsBuilder {
36+
/// Consumes self and returns a valid [`FileLogSettings`] instance.
37+
pub fn build(self) -> FileLogSettings {
38+
FileLogSettings {
39+
common_settings: self.common_settings,
40+
file_log_dir: self.file_log_dir,
41+
}
42+
}
43+
}
44+
45+
#[cfg(test)]
46+
mod test {
47+
use tracing::level_filters::LevelFilter;
48+
49+
use super::*;
50+
51+
#[test]
52+
fn builds_settings() {
53+
let expected = FileLogSettings {
54+
common_settings: Settings {
55+
environment_variable: "hello",
56+
default_level: LevelFilter::DEBUG,
57+
enabled: true,
58+
},
59+
file_log_dir: PathBuf::from("/logs"),
60+
};
61+
let result = Settings::builder()
62+
.with_environment_variable("hello")
63+
.with_default_level(LevelFilter::DEBUG)
64+
.enabled(true)
65+
.file_log_settings_builder(PathBuf::from("/logs"))
66+
.build();
67+
68+
assert_eq!(expected, result);
69+
}
70+
}

crates/stackable-telemetry/src/tracing/settings/mod.rs

+16
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
//! Subscriber settings.
22
3+
use std::path::Path;
4+
35
use tracing::level_filters::LevelFilter;
46

57
pub mod console_log;
68
pub use console_log::*;
79

10+
pub mod file_log;
11+
pub use file_log::*;
12+
813
pub mod otlp_log;
914
pub use otlp_log::*;
1015

@@ -89,6 +94,17 @@ impl SettingsBuilder {
8994
self.into()
9095
}
9196

97+
/// Set specific [`FileLogSettings`].
98+
pub fn file_log_settings_builder<P>(self, path: P) -> FileLogSettingsBuilder
99+
where
100+
P: AsRef<Path>,
101+
{
102+
FileLogSettingsBuilder {
103+
common_settings: self.build(),
104+
file_log_dir: path.as_ref().to_path_buf(),
105+
}
106+
}
107+
92108
/// Set specific [`OtlpLogSettings`].
93109
pub fn otlp_log_settings_builder(self) -> OtlpLogSettingsBuilder {
94110
self.into()

0 commit comments

Comments
 (0)