Skip to content

Commit 8af111f

Browse files
committed
Add min_log_level config
1 parent ac5c165 commit 8af111f

File tree

4 files changed

+99
-13
lines changed

4 files changed

+99
-13
lines changed

src/bridges/tracing.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1378,7 +1378,7 @@ mod tests {
13781378

13791379
let console_options = ConsoleOptions {
13801380
target: Target::Pipe(output.clone()),
1381-
..ConsoleOptions::default()
1381+
..ConsoleOptions::default().with_min_log_level(Level::TRACE)
13821382
};
13831383

13841384
let handler = crate::configure()

src/config.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use opentelemetry_sdk::{
1515
use regex::Regex;
1616

1717
use crate::ConfigureError;
18+
use tracing::Level;
1819

1920
/// Whether to send logs to Logfire.
2021
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
@@ -69,6 +70,8 @@ impl From<bool> for SendToLogfire {
6970
pub struct ConsoleOptions {
7071
/// Where to send output
7172
pub(crate) target: Target,
73+
/// The minimum log level to show in the console.
74+
pub min_log_level: Level,
7275
// TODO: support the below configuration options (inherited from Python SDK)
7376

7477
// /// Whether to show colors in the console.
@@ -83,13 +86,10 @@ pub struct ConsoleOptions {
8386
// ///
8487
// /// It includes the filename, log level, and line number.
8588
// verbose: bool,
86-
// /// The minimum log level to show in the console.
87-
// min_log_level: Level,
8889
// /// Whether to print the URL of the Logfire project after initialization.
8990
// show_project_link: bool,
9091
}
9192

92-
#[expect(clippy::derivable_impls)] // When the other options are implemented, we will need this.
9393
impl Default for ConsoleOptions {
9494
fn default() -> Self {
9595
ConsoleOptions {
@@ -98,7 +98,7 @@ impl Default for ConsoleOptions {
9898
// include_timestamps: true,
9999
// include_tags: true,
100100
// verbose: false,
101-
// min_log_level: Level::INFO,
101+
min_log_level: Level::INFO,
102102
// show_project_link: true,
103103
target: Target::default(),
104104
}
@@ -114,6 +114,15 @@ impl ConsoleOptions {
114114
}
115115
}
116116

117+
impl ConsoleOptions {
118+
/// Set the minimum log level to show in the console.
119+
#[must_use]
120+
pub fn with_min_log_level(mut self, min_log_level: Level) -> Self {
121+
self.min_log_level = min_log_level;
122+
self
123+
}
124+
}
125+
117126
/// Whether to show colors in the console.
118127
#[derive(Default, Debug, Clone, Copy)]
119128
pub enum ConsoleColors {
@@ -367,4 +376,13 @@ mod tests {
367376
assert_eq!(SendToLogfire::from(true), SendToLogfire::Yes);
368377
assert_eq!(SendToLogfire::from(false), SendToLogfire::No);
369378
}
379+
380+
#[test]
381+
fn test_console_option_with_min_log_level() {
382+
let console_options = super::ConsoleOptions::default();
383+
assert_eq!(console_options.min_log_level, tracing::Level::INFO);
384+
let console_options =
385+
super::ConsoleOptions::default().with_min_log_level(tracing::Level::DEBUG);
386+
assert_eq!(console_options.min_log_level, tracing::Level::DEBUG);
387+
}
370388
}

src/internal/exporters/console.rs

+74-6
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,22 @@ impl ConsoleWriter {
8080
self.with_writer(|w| {
8181
let mut buffer = BufWriter::new(w);
8282
for span in batch {
83-
let _ = Self::span_to_writer(span, &mut buffer);
83+
let _ = Self::span_to_writer(self, span, &mut buffer);
8484
}
8585
});
8686
}
8787

8888
pub fn write_tracing_event(&self, event: &tracing::Event<'_>) {
8989
self.with_writer(|w| {
9090
let mut buffer = BufWriter::new(w);
91-
let _ = Self::event_to_writer(event, &mut buffer);
91+
let _ = Self::event_to_writer(self, event, &mut buffer);
9292
});
9393
}
9494

9595
pub fn write_tracing_opentelemetry_data(&self, data: &OtelData) {
9696
self.with_writer(|w| {
9797
let mut buffer = BufWriter::new(w);
98-
let _ = Self::otel_data_to_writer(data, &mut buffer);
98+
let _ = Self::otel_data_to_writer(self, data, &mut buffer);
9999
});
100100
}
101101

@@ -107,7 +107,7 @@ impl ConsoleWriter {
107107
}
108108
}
109109

110-
fn span_to_writer<W: io::Write>(span: &SpanData, w: &mut W) -> io::Result<()> {
110+
fn span_to_writer<W: io::Write>(&self, span: &SpanData, w: &mut W) -> io::Result<()> {
111111
// only print for pending span and logs
112112
if span.get_span_type().is_none_or(|ty| ty == "span") {
113113
return Ok(());
@@ -128,6 +128,12 @@ impl ConsoleWriter {
128128
"logfire.level_num" => {
129129
if let Value::I64(val) = kv.value {
130130
level = Some(val);
131+
if let Some(level) = level {
132+
// Filter out span below the minimum log level
133+
if level < level_to_level_number(self.options.min_log_level) {
134+
return Ok(());
135+
}
136+
}
131137
}
132138
}
133139
"code.namespace" => target = Some(kv.value.as_str()),
@@ -183,9 +189,17 @@ impl ConsoleWriter {
183189
writeln!(w)
184190
}
185191

186-
fn event_to_writer<W: io::Write>(event: &tracing::Event<'_>, w: &mut W) -> io::Result<()> {
192+
fn event_to_writer<W: io::Write>(
193+
&self,
194+
event: &tracing::Event<'_>,
195+
w: &mut W,
196+
) -> io::Result<()> {
187197
let timestamp: DateTime<Utc> = Utc::now();
188198
let level = level_to_level_number(*event.metadata().level());
199+
// Filter out event below the minimum log level
200+
if level < level_to_level_number(self.options.min_log_level) {
201+
return Ok(());
202+
}
189203
let target = event.metadata().module_path();
190204

191205
let mut visitor = FieldsVisitor {
@@ -226,6 +240,7 @@ impl ConsoleWriter {
226240
}
227241

228242
fn otel_data_to_writer<W: io::Write>(
243+
&self,
229244
data: &tracing_opentelemetry::OtelData,
230245
w: &mut W,
231246
) -> io::Result<()> {
@@ -249,6 +264,12 @@ impl ConsoleWriter {
249264
"logfire.level_num" => {
250265
if let Value::I64(val) = kv.value {
251266
level = Some(val);
267+
if let Some(level) = level {
268+
// Filter out data below the minimum log level
269+
if level < level_to_level_number(self.options.min_log_level) {
270+
return Ok(());
271+
}
272+
}
252273
}
253274
}
254275
"code.namespace" => target = Some(kv.value.as_str()),
@@ -346,7 +367,9 @@ mod tests {
346367
fn test_print_to_console() {
347368
let output = Arc::new(Mutex::new(Vec::new()));
348369

349-
let console_options = ConsoleOptions::default().with_target(Target::Pipe(output.clone()));
370+
let console_options = ConsoleOptions::default()
371+
.with_target(Target::Pipe(output.clone()))
372+
.with_min_log_level(Level::TRACE);
350373

351374
let handler = crate::configure()
352375
.local()
@@ -386,4 +409,49 @@ mod tests {
386409
1970-01-01T00:00:00.000005Z ERROR logfire panic: oh no! location=src/internal/exporters/console.rs:369:17, backtrace=disabled backtrace
387410
"#);
388411
}
412+
413+
#[test]
414+
fn test_print_to_console_with_min_log_level() {
415+
let output = Arc::new(Mutex::new(Vec::new()));
416+
417+
let console_options = ConsoleOptions::default()
418+
.with_target(Target::Pipe(output.clone()))
419+
.with_min_log_level(Level::INFO);
420+
421+
let handler = crate::configure()
422+
.local()
423+
.send_to_logfire(false)
424+
.with_console(Some(console_options))
425+
.install_panic_handler()
426+
.with_default_level_filter(LevelFilter::TRACE)
427+
.finish()
428+
.unwrap();
429+
430+
let guard = set_local_logfire(handler);
431+
432+
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
433+
tracing::subscriber::with_default(guard.subscriber().clone(), || {
434+
let root = crate::span!("root span").entered();
435+
let _ = crate::span!("hello world span").entered();
436+
let _ = crate::span!(level: Level::DEBUG, "debug span");
437+
let _ = crate::span!(parent: &root, level: Level::DEBUG, "debug span with explicit parent");
438+
crate::info!("hello world log");
439+
panic!("oh no!");
440+
});
441+
}))
442+
.unwrap_err();
443+
444+
guard.shutdown_handler.shutdown().unwrap();
445+
446+
let output = output.lock().unwrap();
447+
let output = std::str::from_utf8(&output).unwrap();
448+
let output = remap_timestamps_in_console_output(output);
449+
450+
assert_snapshot!(output, @r#"
451+
1970-01-01T00:00:00.000000Z INFO logfire::internal::exporters::console::tests root span
452+
1970-01-01T00:00:00.000001Z INFO logfire::internal::exporters::console::tests hello world span
453+
1970-01-01T00:00:00.000004Z INFO logfire::internal::exporters::console::tests hello world log
454+
1970-01-01T00:00:00.000005Z ERROR logfire panic: oh no! location=src/internal/exporters/console.rs:369:17, backtrace=disabled backtrace
455+
"#);
456+
}
389457
}

src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ impl LogfireConfigBuilder {
498498
} else {
499499
tracer_provider_builder =
500500
tracer_provider_builder.with_id_generator(UlidIdGenerator::new());
501-
};
501+
}
502502

503503
if let Some(resource) = advanced_options.resource.clone() {
504504
tracer_provider_builder = tracer_provider_builder.with_resource(resource);
@@ -597,7 +597,7 @@ impl LogfireConfigBuilder {
597597

598598
meter_provider_builder = meter_provider_builder.with_reader(metric_reader);
599599
}
600-
};
600+
}
601601

602602
if let Some(metrics) = self.metrics.filter(|_| self.enable_metrics) {
603603
for reader in metrics.additional_readers {

0 commit comments

Comments
 (0)