Skip to content

Commit 17e15f1

Browse files
authored
Merge branch 'master' into feat/tracing-appender-local-time
2 parents c10ced4 + d81a69e commit 17e15f1

56 files changed

Lines changed: 675 additions & 178 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/CI.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ jobs:
121121
shell: bash
122122

123123
check-msrv:
124-
# Run `cargo check` on our minimum supported Rust version (1.63.0). This
124+
# Run `cargo check` on our minimum supported Rust version (1.65.0). This
125125
# checks with minimal versions; maximal versions are checked above.
126126
name: "cargo check (+MSRV -Zminimal-versions)"
127127
needs: check
@@ -143,7 +143,7 @@ jobs:
143143
- tracing-tower
144144
- tracing
145145
toolchain:
146-
- 1.63.0
146+
- 1.65.0
147147
- stable
148148
steps:
149149
- uses: actions/checkout@v4

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ members = [
2222
# This will be ignored with Rust older than 1.74, but for now that's okay;
2323
# we're only using it to fix check-cfg issues that first appeared in Rust 1.80.
2424
[workspace.lints.rust]
25-
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(flaky_tests)", "cfg(tracing_unstable)"] }
25+
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(flaky_tests)", "cfg(tracing_unstable)", "cfg(unsound_local_offset)"] }

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ attachment that `Future::instrument` does.
254254
## Supported Rust Versions
255255

256256
Tracing is built against the latest stable release. The minimum supported
257-
version is 1.63. The current Tracing version is not guaranteed to build on Rust
257+
version is 1.65. The current Tracing version is not guaranteed to build on Rust
258258
versions earlier than the minimum supported version.
259259

260260
Tracing follows the same compiler support policies as the rest of the Tokio
@@ -372,7 +372,7 @@ are not maintained by the `tokio` project. These include:
372372
- [`tracing-actix`] provides `tracing` integration for the `actix` actor
373373
framework.
374374
- [`axum-insights`] provides `tracing` integration and Application insights export for the `axum` web framework.
375-
- [`tracing-gelf`] implements a subscriber for exporting traces in Greylog
375+
- [`tracing-gelf`] implements a subscriber for exporting traces in Graylog
376376
GELF format.
377377
- [`tracing-coz`] provides integration with the [coz] causal profiler
378378
(Linux-only).
@@ -398,7 +398,7 @@ are not maintained by the `tokio` project. These include:
398398
- [`tracing-elastic-apm`] provides a layer for reporting traces to [Elastic APM].
399399
- [`tracing-etw`] provides a layer for emitting Windows [ETW] events.
400400
- [`sentry-tracing`] provides a layer for reporting events and traces to [Sentry].
401-
- [`tracing-forest`] provides a subscriber that preserves contextual coherence by
401+
- [`tracing-forest`] provides a subscriber that preserves contextual coherence by
402402
grouping together logs from the same spans during writing.
403403
- [`tracing-loki`] provides a layer for shipping logs to [Grafana Loki].
404404
- [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format.

examples/Cargo.toml

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ default = []
1111
[dev-dependencies]
1212

1313
# tracing crates
14-
tracing = { path = "../tracing", version = "0.2"}
15-
tracing-core = { path = "../tracing-core", version = "0.2"}
14+
tracing = { path = "../tracing", version = "0.2" }
15+
tracing-core = { path = "../tracing-core", version = "0.2" }
1616
tracing-error = { path = "../tracing-error" }
1717
tracing-flame = { path = "../tracing-flame" }
1818
tracing-tower = { version = "0.1.0", path = "../tracing-tower" }
1919
tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = ["json", "env-filter"] }
2020
tracing-futures = { version = "0.3", path = "../tracing-futures", features = ["futures-01"] }
21-
tracing-attributes = { path = "../tracing-attributes", version = "0.2"}
21+
tracing-attributes = { path = "../tracing-attributes", version = "0.2" }
2222
tracing-log = { path = "../tracing-log", version = "0.2", features = ["env_logger"] }
2323
tracing-serde = { path = "../tracing-serde" }
2424
tracing-appender = { path = "../tracing-appender" }
@@ -39,7 +39,7 @@ bytes = "1.2.0"
3939
argh = "0.1.8"
4040

4141
# sloggish example
42-
nu-ansi-term = "0.46.0"
42+
nu-ansi-term = "0.50.0"
4343
humantime = "2.1.0"
4444
log = "0.4.17"
4545

@@ -49,7 +49,13 @@ tempfile = "3.3.0"
4949

5050
# fmt examples
5151
snafu = "0.6.10"
52-
thiserror = "1.0.31"
52+
thiserror = "2"
53+
54+
# env-filter-explorer example
55+
ansi-to-tui = "7.0.0"
56+
ratatui = "0.29.0"
57+
crossterm = "0.28.1"
58+
tui-textarea = "0.7.0"
5359

5460
[lints]
5561
workspace = true
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
use std::{
2+
io::{self},
3+
sync::{Arc, Mutex},
4+
};
5+
6+
use ansi_to_tui::IntoText;
7+
use crossterm::event;
8+
use ratatui::{
9+
buffer::Buffer,
10+
layout::{Constraint, Layout, Rect},
11+
style::Stylize,
12+
widgets::{Block, Widget},
13+
DefaultTerminal, Frame,
14+
};
15+
use tracing_subscriber::{filter::ParseError, fmt::MakeWriter, EnvFilter};
16+
use tui_textarea::{Input, Key, TextArea};
17+
18+
/// A list of preset filters to make it easier to explore the filter syntax.
19+
///
20+
/// The UI allows you to select a preset filter with the up/down arrow keys.
21+
const PRESET_FILTERS: &[&str] = &[
22+
"trace",
23+
"debug",
24+
"info",
25+
"warn",
26+
"error",
27+
"[with_fields]",
28+
"[with_fields{foo}]",
29+
"[with_fields{bar}]",
30+
"[with_fields{foo=42}]",
31+
"[with_fields{bar=bar}]",
32+
"[with_fields{foo=99}]",
33+
"[with_fields{bar=nope}]",
34+
"[with_fields{nonexistent}]",
35+
"other_crate=info",
36+
"other_crate=debug",
37+
"trace,other_crate=warn",
38+
"warn,other_crate=info",
39+
];
40+
41+
fn main() -> io::Result<()> {
42+
let terminal = ratatui::init();
43+
let result = App::new().run(terminal);
44+
ratatui::restore();
45+
result
46+
}
47+
48+
struct App {
49+
filter: TextArea<'static>,
50+
preset_index: usize,
51+
exit: bool,
52+
log_widget: Result<LogWidget, ParseError>,
53+
}
54+
55+
impl App {
56+
/// Creates a new instance of the application, ready to run
57+
fn new() -> Self {
58+
let mut filter = TextArea::new(vec![PRESET_FILTERS[0].to_string()]);
59+
let title = "Env Filter Explorer. <Esc> to quit, <Up>/<Down> to select preset";
60+
filter.set_block(Block::bordered().title(title));
61+
Self {
62+
filter,
63+
preset_index: 0,
64+
exit: false,
65+
log_widget: Ok(LogWidget::default()),
66+
}
67+
}
68+
69+
/// The application's main loop until the user exits.
70+
fn run(mut self, mut terminal: DefaultTerminal) -> io::Result<()> {
71+
while !self.exit {
72+
self.log_widget = self.evaluate_filter();
73+
terminal.draw(|frame| self.render(frame))?;
74+
self.handle_event()?;
75+
}
76+
Ok(())
77+
}
78+
79+
/// Render the application with a filter input area and a log output area.
80+
fn render(&self, frame: &mut Frame) {
81+
let layout = Layout::vertical([Constraint::Length(3), Constraint::Fill(1)]);
82+
let [filter_area, main_area] = layout.areas(frame.area());
83+
frame.render_widget(&self.filter, filter_area);
84+
match &self.log_widget {
85+
Ok(log_widget) => frame.render_widget(log_widget, main_area),
86+
Err(error) => frame.render_widget(error.to_string().red(), main_area),
87+
}
88+
}
89+
90+
/// Handles a single terminal event (e.g. mouse, keyboard, resize).
91+
fn handle_event(&mut self) -> io::Result<()> {
92+
let event = event::read()?;
93+
let input = Input::from(event);
94+
match input.key {
95+
Key::Enter => return Ok(()), // ignore new lines
96+
Key::Esc => self.exit = true,
97+
Key::Up => self.select_previous_preset(),
98+
Key::Down => self.select_next_preset(),
99+
_ => self.add_input(input),
100+
}
101+
Ok(())
102+
}
103+
104+
/// Selects the previous preset filter in the list.
105+
fn select_previous_preset(&mut self) {
106+
self.select_preset(self.preset_index.saturating_sub(1));
107+
}
108+
109+
/// Selects the next preset filter in the list.
110+
fn select_next_preset(&mut self) {
111+
self.select_preset((self.preset_index + 1).min(PRESET_FILTERS.len() - 1));
112+
}
113+
114+
/// Selects a preset filter by index and updates the filter text area.
115+
fn select_preset(&mut self, index: usize) {
116+
self.preset_index = index;
117+
self.filter.select_all();
118+
self.filter.delete_line_by_head();
119+
self.filter.insert_str(PRESET_FILTERS[self.preset_index]);
120+
}
121+
122+
/// Handles normal keyboard input by adding it to the filter text area.
123+
fn add_input(&mut self, input: Input) {
124+
self.filter.input(input);
125+
}
126+
127+
/// Evaluates the current filter and returns a log widget with the filtered logs or an error.
128+
fn evaluate_filter(&mut self) -> Result<LogWidget, ParseError> {
129+
let filter = self.filter.lines()[0].to_string();
130+
let env_filter = EnvFilter::builder().parse(filter)?;
131+
let log_widget = LogWidget::default();
132+
let collector = tracing_subscriber::fmt()
133+
.with_env_filter(env_filter)
134+
.with_writer(log_widget.clone())
135+
.finish();
136+
tracing::collect::with_default(collector, || {
137+
simulate_logging();
138+
other_crate_span();
139+
});
140+
Ok(log_widget)
141+
}
142+
}
143+
144+
/// A writer that collects logs into a buffer and can be displayed as a widget.
145+
#[derive(Clone, Default, Debug)]
146+
struct LogWidget {
147+
buffer: Arc<Mutex<Vec<u8>>>,
148+
}
149+
150+
impl io::Write for LogWidget {
151+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
152+
self.buffer.lock().unwrap().write(buf)
153+
}
154+
155+
fn flush(&mut self) -> io::Result<()> {
156+
self.buffer.lock().unwrap().flush()
157+
}
158+
}
159+
160+
impl<'a> MakeWriter<'a> for LogWidget {
161+
type Writer = Self;
162+
163+
fn make_writer(&'a self) -> Self::Writer {
164+
self.clone()
165+
}
166+
}
167+
168+
impl Widget for &LogWidget {
169+
/// Displays the logs that have been collected in the buffer.
170+
///
171+
/// If the buffer is empty, it displays "No matching logs".
172+
fn render(self, area: Rect, buf: &mut Buffer) {
173+
let buffer = self.buffer.lock().unwrap();
174+
let string = String::from_utf8_lossy(&buffer).to_string();
175+
if string.is_empty() {
176+
"No matching logs".render(area, buf);
177+
return;
178+
}
179+
string
180+
.into_text() // convert a string with ANSI escape codes into ratatui Text
181+
.unwrap_or_else(|err| format!("Error parsing output: {err}").into())
182+
.render(area, buf);
183+
}
184+
}
185+
186+
#[tracing::instrument]
187+
fn simulate_logging() {
188+
tracing::info!("This is an info message");
189+
tracing::error!("This is an error message");
190+
tracing::warn!("This is a warning message");
191+
tracing::debug!("This is a debug message");
192+
tracing::trace!("This is a trace message");
193+
194+
with_fields(42, "bar");
195+
with_fields(99, "nope");
196+
197+
trace_span();
198+
debug_span();
199+
info_span();
200+
warn_span();
201+
error_span();
202+
}
203+
204+
#[tracing::instrument]
205+
fn with_fields(foo: u32, bar: &'static str) {
206+
tracing::info!(foo, bar, "This is an info message with fields");
207+
}
208+
209+
#[tracing::instrument(level = "trace")]
210+
fn trace_span() {
211+
tracing::error!("Error message inside a span with trace level");
212+
tracing::info!("Info message inside a span with trace level");
213+
tracing::trace!("Trace message inside a span with trace level");
214+
}
215+
216+
#[tracing::instrument]
217+
fn debug_span() {
218+
tracing::error!("Error message inside a span with debug level");
219+
tracing::info!("Info message inside a span with debug level");
220+
tracing::debug!("Debug message inside a span with debug level");
221+
}
222+
223+
#[tracing::instrument]
224+
fn info_span() {
225+
tracing::error!("Error message inside a span with info level");
226+
tracing::info!("Info message inside a span with info level");
227+
tracing::debug!("Debug message inside a span with info level");
228+
}
229+
230+
#[tracing::instrument]
231+
fn warn_span() {
232+
tracing::error!("Error message inside a span with warn level");
233+
tracing::info!("Info message inside a span with warn level");
234+
tracing::debug!("Debug message inside a span with warn level");
235+
}
236+
237+
#[tracing::instrument]
238+
fn error_span() {
239+
tracing::error!("Error message inside a span with error level");
240+
tracing::info!("Info message inside a span with error level");
241+
tracing::debug!("Debug message inside a span with error level");
242+
}
243+
244+
#[tracing::instrument(target = "other_crate")]
245+
fn other_crate_span() {
246+
tracing::error!(target: "other_crate", "An error message from another crate");
247+
tracing::warn!(target: "other_crate", "A warning message from another crate");
248+
tracing::info!(target: "other_crate", "An info message from another crate");
249+
tracing::debug!(target: "other_crate", "A debug message from another crate");
250+
tracing::trace!(target: "other_crate", "A trace message from another crate");
251+
}

tracing-appender/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ local-time = ["time/local-offset"]
2828
crossbeam-channel = "0.5.5"
2929
time = { version = "0.3.2", default-features = false, features = ["formatting", "parsing"] }
3030
parking_lot = { optional = true, version = "0.12.1" }
31-
thiserror = "1.0.31"
31+
thiserror = "2"
3232

3333
[dependencies.tracing-subscriber]
3434
path = "../tracing-subscriber"

tracing-appender/src/lib.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
//!
2323
//! This crate can be used in a few ways to record spans/events:
2424
//! - Using a [`RollingFileAppender`] to perform writes to a log file. This will block on writes.
25-
//! - Using *any* type implementing [`std::io::Write`][write] in a non-blocking fashion.
25+
//! - Using *any* type implementing [`std::io::Write`] in a non-blocking fashion.
2626
//! - Using a combination of [`NonBlocking`] and [`RollingFileAppender`] to allow writes to a log file
2727
//! without blocking.
2828
//!
@@ -48,7 +48,7 @@
4848
//! # }
4949
//! ```
5050
//!
51-
//! The file appender implements [`std::io::Write`][write]. To be used with
51+
//! The file appender implements [`std::io::Write`]. To be used with
5252
//! [`tracing_subscriber::FmtSubscriber`][fmt_subscriber], it must be combined with a
5353
//! [`MakeWriter`][make_writer] implementation to be able to record tracing spans/event.
5454
//!
@@ -68,12 +68,12 @@
6868
//! .init();
6969
//! # }
7070
//! ```
71-
//! **Note:** `_guard` is a [`WorkerGuard`][guard] which is returned by [`tracing_appender::non_blocking`][non_blocking]
71+
//! **Note:** `_guard` is a [`WorkerGuard`] which is returned by [`tracing_appender::non_blocking`][non_blocking]
7272
//! to ensure buffered logs are flushed to their output in the case of abrupt terminations of a process.
7373
//! See [`WorkerGuard` module][guard] for more details.
7474
//!
7575
//! The example below demonstrates the construction of a [`tracing_appender::non_blocking`][non_blocking]
76-
//! writer constructed with a [`std::io::Write`][write]:
76+
//! writer constructed with a [`std::io::Write`]:
7777
//!
7878
//! ```rust
7979
//! use std::io::Error;
@@ -102,7 +102,6 @@
102102
//!
103103
//! The [`non_blocking` module][non_blocking]'s documentation provides more detail on how to use `non_blocking`.
104104
//!
105-
//! [write]: std::io::Write
106105
//! [non_blocking]: mod@non_blocking
107106
//! [guard]: non_blocking::WorkerGuard
108107
//! [make_writer]: tracing_subscriber::fmt::MakeWriter

0 commit comments

Comments
 (0)