@@ -40,6 +40,16 @@ use crate::QW_ENABLE_OPENTELEMETRY_OTLP_EXPORTER_ENV_KEY;
4040#[ cfg( feature = "tokio-console" ) ]
4141use crate :: QW_ENABLE_TOKIO_CONSOLE_ENV_KEY ;
4242
43+ /// Load the default logging filter from the environment. The filter can later
44+ /// be updated using the result callback of [setup_logging_and_tracing].
45+ fn startup_env_filter ( level : Level ) -> anyhow:: Result < EnvFilter > {
46+ let env_filter = env:: var ( "RUST_LOG" )
47+ . map ( |_| EnvFilter :: from_default_env ( ) )
48+ . or_else ( |_| EnvFilter :: try_new ( format ! ( "quickwit={level},tantivy=WARN" ) ) )
49+ . context ( "failed to set up tracing env filter" ) ?;
50+ Ok ( env_filter)
51+ }
52+
4353pub fn setup_logging_and_tracing (
4454 level : Level ,
4555 ansi_colors : bool ,
@@ -52,13 +62,10 @@ pub fn setup_logging_and_tracing(
5262 return Ok ( quickwit_serve:: do_nothing_env_filter_reload_fn ( ) ) ;
5363 }
5464 }
55- let env_filter = env:: var ( "RUST_LOG" )
56- . map ( |_| EnvFilter :: from_default_env ( ) )
57- . or_else ( |_| EnvFilter :: try_new ( format ! ( "quickwit={level},tantivy=WARN" ) ) )
58- . context ( "failed to set up tracing env filter" ) ?;
5965 global:: set_text_map_propagator ( TraceContextPropagator :: new ( ) ) ;
60- let ( reloadable_env_filter, reload_handle) = tracing_subscriber:: reload:: Layer :: new ( env_filter) ;
61- let registry = tracing_subscriber:: registry ( ) . with ( reloadable_env_filter) ;
66+ let ( reloadable_env_filter, reload_handle) =
67+ tracing_subscriber:: reload:: Layer :: new ( startup_env_filter ( level) ?) ;
68+ let registry = tracing_subscriber:: registry ( ) ;
6269 // Note on disabling ANSI characters: setting the ansi boolean on event format is insufficient.
6370 // It is thus set on layers, see https://github.com/tokio-rs/tracing/issues/1817
6471 if get_bool_from_env ( QW_ENABLE_OPENTELEMETRY_OTLP_EXPORTER_ENV_KEY , false ) {
@@ -90,6 +97,7 @@ pub fn setup_logging_and_tracing(
9097 let fmt_fields = event_format. format_fields ( ) ;
9198
9299 registry
100+ . with ( reloadable_env_filter)
93101 . with ( telemetry_layer)
94102 . with (
95103 tracing_subscriber:: fmt:: layer ( )
@@ -102,24 +110,44 @@ pub fn setup_logging_and_tracing(
102110 } else {
103111 let event_format = EventFormat :: get_from_env ( ) ;
104112 let fmt_fields = event_format. format_fields ( ) ;
113+ #[ cfg( not( feature = "jemalloc-profiled" ) ) ]
114+ let registry = registry. with ( reloadable_env_filter) . with (
115+ tracing_subscriber:: fmt:: layer ( )
116+ . event_format ( event_format)
117+ . fmt_fields ( fmt_fields)
118+ . with_ansi ( ansi_colors) ,
119+ ) ;
120+ #[ cfg( feature = "jemalloc-profiled" ) ]
121+ let registry = jemalloc_profiled:: configure_registry (
122+ registry,
123+ event_format,
124+ fmt_fields,
125+ ansi_colors,
126+ level,
127+ ) ?;
105128
106129 registry
107- . with (
108- tracing_subscriber:: fmt:: layer ( )
109- . event_format ( event_format)
110- . fmt_fields ( fmt_fields)
111- . with_ansi ( ansi_colors) ,
112- )
113130 . try_init ( )
114131 . context ( "failed to register tracing subscriber" ) ?;
115132 }
133+
116134 Ok ( Arc :: new ( move |env_filter_def : & str | {
117135 let new_env_filter = EnvFilter :: try_new ( env_filter_def) ?;
118136 reload_handle. reload ( new_env_filter) ?;
119137 Ok ( ( ) )
120138 } ) )
121139}
122140
141+ /// We do not rely on the RFC3339 implementation, because it has a nanosecond precision.
142+ /// See discussion here: https://github.com/time-rs/time/discussions/418
143+ fn time_formatter ( ) -> UtcTime < Vec < BorrowedFormatItem < ' static > > > {
144+ let time_format = time:: format_description:: parse (
145+ "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3]Z" ,
146+ )
147+ . expect ( "time format description should be valid" ) ;
148+ UtcTime :: new ( time_format)
149+ }
150+
123151enum EventFormat < ' a > {
124152 Full ( Format < Full , UtcTime < Vec < BorrowedFormatItem < ' a > > > > ) ,
125153 Json ( Format < Json > ) ,
@@ -136,17 +164,9 @@ impl EventFormat<'_> {
136164 let json_format = tracing_subscriber:: fmt:: format ( ) . json ( ) ;
137165 EventFormat :: Json ( json_format)
138166 } else {
139- // We do not rely on the RFC3339 implementation, because it has a nanosecond precision.
140- // See discussion here: https://github.com/time-rs/time/discussions/418
141- let timer_format = time:: format_description:: parse (
142- "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3]Z" ,
143- )
144- . expect ( "time format description should be valid" ) ;
145- let timer = UtcTime :: new ( timer_format) ;
146-
147167 let full_format = tracing_subscriber:: fmt:: format ( )
148168 . with_target ( true )
149- . with_timer ( timer ) ;
169+ . with_timer ( time_formatter ( ) ) ;
150170
151171 EventFormat :: Full ( full_format)
152172 }
@@ -191,3 +211,129 @@ impl FormatFields<'_> for FieldFormat {
191211 }
192212 }
193213}
214+
215+ /// Logger configurations specific to the jemalloc profiler.
216+ ///
217+ /// A custom event formatter is used to print the backtrace of the
218+ /// profiling events.
219+ #[ cfg( feature = "jemalloc-profiled" ) ]
220+ pub ( super ) mod jemalloc_profiled {
221+ use std:: fmt;
222+
223+ use quickwit_common:: jemalloc_profiled:: JEMALLOC_PROFILER_TARGET ;
224+ use time:: format_description:: BorrowedFormatItem ;
225+ use tracing:: { Event , Level , Metadata , Subscriber } ;
226+ use tracing_subscriber:: Layer ;
227+ use tracing_subscriber:: filter:: filter_fn;
228+ use tracing_subscriber:: fmt:: format:: { DefaultFields , Writer } ;
229+ use tracing_subscriber:: fmt:: time:: { FormatTime , UtcTime } ;
230+ use tracing_subscriber:: fmt:: { FmtContext , FormatEvent , FormatFields , FormattedFields } ;
231+ use tracing_subscriber:: layer:: SubscriberExt ;
232+ use tracing_subscriber:: registry:: LookupSpan ;
233+
234+ use super :: { EventFormat , FieldFormat , startup_env_filter, time_formatter} ;
235+
236+ /// An event formatter specific to the memory profiler output.
237+ ///
238+ /// Also displays a backtrace after spans and the fields of the tracing
239+ /// event (into separate lines).
240+ struct ProfilingFormat {
241+ time_formatter : UtcTime < Vec < BorrowedFormatItem < ' static > > > ,
242+ }
243+
244+ impl Default for ProfilingFormat {
245+ fn default ( ) -> Self {
246+ Self {
247+ time_formatter : time_formatter ( ) ,
248+ }
249+ }
250+ }
251+
252+ impl < S , N > FormatEvent < S , N > for ProfilingFormat
253+ where
254+ S : Subscriber + for < ' a > LookupSpan < ' a > ,
255+ N : for < ' a > FormatFields < ' a > + ' static ,
256+ {
257+ fn format_event (
258+ & self ,
259+ ctx : & FmtContext < ' _ , S , N > ,
260+ mut writer : Writer < ' _ > ,
261+ event : & Event < ' _ > ,
262+ ) -> fmt:: Result {
263+ self . time_formatter . format_time ( & mut writer) ?;
264+ write ! ( writer, " {JEMALLOC_PROFILER_TARGET} " ) ?;
265+ if let Some ( scope) = ctx. event_scope ( ) {
266+ let mut seen = false ;
267+
268+ for span in scope. from_root ( ) {
269+ write ! ( writer, "{}" , span. metadata( ) . name( ) ) ?;
270+ seen = true ;
271+
272+ let ext = span. extensions ( ) ;
273+ if let Some ( fields) = & ext. get :: < FormattedFields < N > > ( ) {
274+ if !fields. is_empty ( ) {
275+ write ! ( writer, "{{{}}}:" , fields) ?;
276+ }
277+ }
278+ }
279+
280+ if seen {
281+ writer. write_char ( ' ' ) ?;
282+ }
283+ } ;
284+
285+ ctx. format_fields ( writer. by_ref ( ) , event) ?;
286+ writeln ! ( writer) ?;
287+
288+ // Print a backtrace to help idenify the callsite
289+ backtrace:: trace ( |frame| {
290+ backtrace:: resolve_frame ( frame, |symbol| {
291+ if let Some ( symbole_name) = symbol. name ( ) {
292+ let _ = writeln ! ( writer, "{}" , symbole_name) ;
293+ } else {
294+ let _ = writeln ! ( writer, "symb failed" ) ;
295+ }
296+ } ) ;
297+ true
298+ } ) ;
299+ Ok ( ( ) )
300+ }
301+ }
302+
303+ fn profiler_tracing_filter ( metadata : & Metadata ) -> bool {
304+ metadata. is_span ( ) || ( metadata. is_event ( ) && metadata. target ( ) == JEMALLOC_PROFILER_TARGET )
305+ }
306+
307+ /// Configures the regular logging layer and a specific layer that gathers
308+ /// extra debug information for the jemalloc profiler.
309+ ///
310+ /// The the jemalloc profiler formatter disables the env filter reloading
311+ /// because the [tracing_subscriber::reload::Layer] seems to overwrite the
312+ /// TRACE level span filter even though it's applied to a separate layer.
313+ pub ( super ) fn configure_registry < S > (
314+ registry : S ,
315+ event_format : EventFormat < ' static > ,
316+ fmt_fields : FieldFormat ,
317+ ansi_colors : bool ,
318+ level : Level ,
319+ ) -> anyhow:: Result < impl Subscriber + for <' span > LookupSpan < ' span > >
320+ where
321+ S : Subscriber + for < ' span > LookupSpan < ' span > ,
322+ {
323+ Ok ( registry
324+ . with (
325+ tracing_subscriber:: fmt:: layer ( )
326+ . event_format ( ProfilingFormat :: default ( ) )
327+ . fmt_fields ( DefaultFields :: new ( ) )
328+ . with_ansi ( ansi_colors)
329+ . with_filter ( filter_fn ( profiler_tracing_filter) ) ,
330+ )
331+ . with (
332+ tracing_subscriber:: fmt:: layer ( )
333+ . event_format ( event_format)
334+ . fmt_fields ( fmt_fields)
335+ . with_ansi ( ansi_colors)
336+ . with_filter ( startup_env_filter ( level) ?) ,
337+ ) )
338+ }
339+ }
0 commit comments