Skip to content

Commit 7bacc17

Browse files
committed
Refactor telemetry registry setup
1 parent de835e9 commit 7bacc17

7 files changed

Lines changed: 211 additions & 194 deletions

File tree

quickwit/Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

quickwit/quickwit-cli/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ path = "src/generate_markdown.rs"
2222

2323
[dependencies]
2424
anyhow = { workspace = true }
25+
backtrace = { workspace = true, optional = true }
2526
bytesize = { workspace = true }
2627
chrono = { workspace = true }
2728
clap = { workspace = true }
@@ -47,6 +48,7 @@ time = { workspace = true }
4748
tokio = { workspace = true }
4849
toml = { workspace = true }
4950
tracing = { workspace = true }
51+
tracing-subscriber = { workspace = true, optional = true }
5052

5153
quickwit-actors = { workspace = true }
5254
quickwit-cluster = { workspace = true }
@@ -80,8 +82,10 @@ quickwit-storage = { workspace = true, features = ["testsuite"] }
8082
datafusion = ["quickwit-serve/datafusion"]
8183
jemalloc = ["dep:tikv-jemalloc-ctl", "dep:tikv-jemallocator"]
8284
jemalloc-profiled = [
85+
"jemalloc",
86+
"dep:backtrace",
87+
"dep:tracing-subscriber",
8388
"quickwit-common/jemalloc-profiled",
84-
"quickwit-telemetry-exporters/jemalloc-profiled",
8589
"quickwit-serve/jemalloc-profiled"
8690
]
8791
ci-test = []

quickwit/quickwit-cli/src/jemalloc.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,142 @@ pub fn start_jemalloc_metrics_loop() {
6262
}
6363
});
6464
}
65+
66+
#[cfg(feature = "jemalloc-profiled")]
67+
pub use profiled_tracing::tracing_registry;
68+
69+
#[cfg(feature = "jemalloc-profiled")]
70+
mod profiled_tracing {
71+
use std::fmt;
72+
73+
use anyhow::Context;
74+
use quickwit_common::jemalloc_profiled::JEMALLOC_PROFILER_TARGET;
75+
use quickwit_telemetry_exporters::EnvFilterReloadFn;
76+
use time::format_description::BorrowedFormatItem;
77+
use tracing::{Event, Level, Metadata, Subscriber};
78+
use tracing_subscriber::filter::filter_fn;
79+
use tracing_subscriber::fmt::format::{DefaultFields, Writer};
80+
use tracing_subscriber::fmt::time::{FormatTime, UtcTime};
81+
use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields, FormattedFields};
82+
use tracing_subscriber::layer::SubscriberExt;
83+
use tracing_subscriber::registry::LookupSpan;
84+
use tracing_subscriber::{EnvFilter, Layer};
85+
86+
fn time_formatter() -> UtcTime<Vec<BorrowedFormatItem<'static>>> {
87+
let time_format = time::format_description::parse(
88+
"[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3]Z",
89+
)
90+
.expect("time format description should be valid");
91+
UtcTime::new(time_format)
92+
}
93+
94+
/// An event formatter specific to the memory profiler output.
95+
///
96+
/// Also displays a backtrace after the spans and fields of the tracing
97+
/// event (into separate lines).
98+
struct ProfilingFormat {
99+
time_formatter: UtcTime<Vec<BorrowedFormatItem<'static>>>,
100+
}
101+
102+
impl Default for ProfilingFormat {
103+
fn default() -> Self {
104+
Self {
105+
time_formatter: time_formatter(),
106+
}
107+
}
108+
}
109+
110+
impl<S, N> FormatEvent<S, N> for ProfilingFormat
111+
where
112+
S: Subscriber + for<'a> LookupSpan<'a>,
113+
N: for<'a> FormatFields<'a> + 'static,
114+
{
115+
fn format_event(
116+
&self,
117+
ctx: &FmtContext<'_, S, N>,
118+
mut writer: Writer<'_>,
119+
event: &Event<'_>,
120+
) -> fmt::Result {
121+
self.time_formatter.format_time(&mut writer)?;
122+
write!(writer, " {JEMALLOC_PROFILER_TARGET} ")?;
123+
if let Some(scope) = ctx.event_scope() {
124+
let mut seen = false;
125+
126+
for span in scope.from_root() {
127+
write!(writer, "{}", span.metadata().name())?;
128+
seen = true;
129+
130+
let ext = span.extensions();
131+
if let Some(fields) = &ext.get::<FormattedFields<N>>()
132+
&& !fields.is_empty()
133+
{
134+
write!(writer, "{{{fields}}}:")?;
135+
}
136+
}
137+
138+
if seen {
139+
writer.write_char(' ')?;
140+
}
141+
};
142+
143+
ctx.format_fields(writer.by_ref(), event)?;
144+
writeln!(writer)?;
145+
146+
// Print a backtrace to help identify the callsite.
147+
backtrace::trace(|frame| {
148+
backtrace::resolve_frame(frame, |symbol| {
149+
if let Some(symbol_name) = symbol.name() {
150+
let _ = writeln!(writer, "{symbol_name}");
151+
} else {
152+
let _ = writeln!(writer, "symb failed");
153+
}
154+
});
155+
true
156+
});
157+
Ok(())
158+
}
159+
}
160+
161+
fn profiler_tracing_filter(metadata: &Metadata) -> bool {
162+
metadata.is_span() || (metadata.is_event() && metadata.target() == JEMALLOC_PROFILER_TARGET)
163+
}
164+
165+
fn startup_env_filter(level: Level) -> anyhow::Result<EnvFilter> {
166+
let env_filter = std::env::var("RUST_LOG")
167+
.map(|_| EnvFilter::from_default_env())
168+
.or_else(|_| EnvFilter::try_new(format!("quickwit={level},tantivy=WARN")))
169+
.context("failed to set up tracing env filter")?;
170+
Ok(env_filter)
171+
}
172+
173+
/// Configures the regular logging layer and a specific layer that gathers
174+
/// extra debug information for the jemalloc profiler.
175+
///
176+
/// The jemalloc profiler formatter disables the env filter reloading
177+
/// because the [tracing_subscriber::reload::Layer] seems to overwrite the
178+
/// filter configured by [profiler_tracing_filter()] even though it is
179+
/// applied to a separate layer.
180+
pub fn tracing_registry(
181+
level: Level,
182+
ansi_colors: bool,
183+
) -> anyhow::Result<(
184+
impl Subscriber + for<'span> LookupSpan<'span> + Send + Sync + 'static,
185+
EnvFilterReloadFn,
186+
)> {
187+
let registry = tracing_subscriber::registry().with(
188+
tracing_subscriber::fmt::layer()
189+
.event_format(ProfilingFormat::default())
190+
.fmt_fields(DefaultFields::new())
191+
.with_ansi(ansi_colors)
192+
.with_filter(filter_fn(profiler_tracing_filter)),
193+
);
194+
let registry = registry.with(
195+
quickwit_telemetry_exporters::logging_layer(ansi_colors)
196+
.with_filter(startup_env_filter(level)?),
197+
);
198+
Ok((
199+
registry,
200+
quickwit_telemetry_exporters::do_nothing_env_filter_reload_fn(),
201+
))
202+
}
203+
}

quickwit/quickwit-cli/src/main.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use quickwit_cli::jemalloc::start_jemalloc_metrics_loop;
2323
use quickwit_cli::metrics::register_build_info_metric;
2424
use quickwit_cli::{busy_detector, install_default_crypto_ring_provider};
2525
use quickwit_common::runtimes::scrape_tokio_runtime_metrics;
26-
use quickwit_serve::BuildInfo;
26+
use quickwit_serve::{BuildInfo, EnvFilterReloadFn};
2727
use tracing::error;
2828

2929
#[cfg(feature = "tokio-console")]
@@ -83,15 +83,30 @@ fn init_telemetry(
8383
service_version: &str,
8484
level: tracing::Level,
8585
ansi_colors: bool,
86-
) -> anyhow::Result<quickwit_telemetry_exporters::TelemetryHandle> {
86+
) -> anyhow::Result<(
87+
quickwit_telemetry_exporters::TelemetryHandle,
88+
EnvFilterReloadFn,
89+
)> {
8790
#[cfg(feature = "tokio-console")]
8891
{
8992
if quickwit_common::get_bool_from_env(QW_ENABLE_TOKIO_CONSOLE_ENV_KEY, false) {
9093
console_subscriber::init();
91-
return Ok(quickwit_telemetry_exporters::TelemetryHandle::default());
94+
return Ok((
95+
quickwit_telemetry_exporters::TelemetryHandle::default(),
96+
quickwit_telemetry_exporters::do_nothing_env_filter_reload_fn(),
97+
));
9298
}
9399
}
94-
quickwit_telemetry_exporters::init_telemetry(service_version, level, ansi_colors)
100+
#[cfg(feature = "jemalloc-profiled")]
101+
let (registry, env_filter_reload_fn) =
102+
quickwit_cli::jemalloc::tracing_registry(level, ansi_colors)?;
103+
104+
#[cfg(not(feature = "jemalloc-profiled"))]
105+
let (registry, env_filter_reload_fn) =
106+
quickwit_telemetry_exporters::default_tracing_registry(level, ansi_colors)?;
107+
108+
let telemetry_handle = quickwit_telemetry_exporters::init_telemetry(service_version, registry)?;
109+
Ok((telemetry_handle, env_filter_reload_fn))
95110
}
96111

97112
async fn main_impl() -> anyhow::Result<()> {
@@ -100,7 +115,7 @@ async fn main_impl() -> anyhow::Result<()> {
100115
install_default_crypto_ring_provider();
101116

102117
let build_info = BuildInfo::get();
103-
let telemetry_handle = init_telemetry(
118+
let (telemetry_handle, env_filter_reload_fn) = init_telemetry(
104119
&build_info.version,
105120
command.default_log_level(),
106121
ansi_colors,
@@ -113,10 +128,7 @@ async fn main_impl() -> anyhow::Result<()> {
113128
#[cfg(feature = "jemalloc")]
114129
start_jemalloc_metrics_loop();
115130

116-
let return_code: i32 = if let Err(command_error) = command
117-
.execute(telemetry_handle.env_filter_reload_fn())
118-
.await
119-
{
131+
let return_code: i32 = if let Err(command_error) = command.execute(env_filter_reload_fn).await {
120132
error!(error=%command_error, "command failed");
121133
eprintln!(
122134
"{} command failed: {:?}\n",

quickwit/quickwit-telemetry-exporters/Cargo.toml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ license.workspace = true
1212

1313
[dependencies]
1414
anyhow = { workspace = true }
15-
backtrace = { workspace = true, optional = true }
1615
metrics = { workspace = true }
1716
metrics-exporter-otel = { workspace = true }
1817
metrics-exporter-prometheus = { workspace = true }
@@ -29,9 +28,3 @@ tracing-subscriber = { workspace = true }
2928

3029
quickwit-common = { workspace = true }
3130
quickwit-metrics = { workspace = true }
32-
33-
[features]
34-
jemalloc-profiled = [
35-
"dep:backtrace",
36-
"quickwit-common/jemalloc-profiled",
37-
]

0 commit comments

Comments
 (0)