@@ -40,6 +40,16 @@ use crate::QW_ENABLE_OPENTELEMETRY_OTLP_EXPORTER_ENV_KEY;
40
40
#[ cfg( feature = "tokio-console" ) ]
41
41
use crate :: QW_ENABLE_TOKIO_CONSOLE_ENV_KEY ;
42
42
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
+
43
53
pub fn setup_logging_and_tracing (
44
54
level : Level ,
45
55
ansi_colors : bool ,
@@ -52,13 +62,10 @@ pub fn setup_logging_and_tracing(
52
62
return Ok ( quickwit_serve:: do_nothing_env_filter_reload_fn ( ) ) ;
53
63
}
54
64
}
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" ) ?;
59
65
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 ( ) ;
62
69
// Note on disabling ANSI characters: setting the ansi boolean on event format is insufficient.
63
70
// It is thus set on layers, see https://github.com/tokio-rs/tracing/issues/1817
64
71
if get_bool_from_env ( QW_ENABLE_OPENTELEMETRY_OTLP_EXPORTER_ENV_KEY , false ) {
@@ -90,6 +97,7 @@ pub fn setup_logging_and_tracing(
90
97
let fmt_fields = event_format. format_fields ( ) ;
91
98
92
99
registry
100
+ . with ( reloadable_env_filter)
93
101
. with ( telemetry_layer)
94
102
. with (
95
103
tracing_subscriber:: fmt:: layer ( )
@@ -102,24 +110,44 @@ pub fn setup_logging_and_tracing(
102
110
} else {
103
111
let event_format = EventFormat :: get_from_env ( ) ;
104
112
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
+ ) ?;
105
128
106
129
registry
107
- . with (
108
- tracing_subscriber:: fmt:: layer ( )
109
- . event_format ( event_format)
110
- . fmt_fields ( fmt_fields)
111
- . with_ansi ( ansi_colors) ,
112
- )
113
130
. try_init ( )
114
131
. context ( "failed to register tracing subscriber" ) ?;
115
132
}
133
+
116
134
Ok ( Arc :: new ( move |env_filter_def : & str | {
117
135
let new_env_filter = EnvFilter :: try_new ( env_filter_def) ?;
118
136
reload_handle. reload ( new_env_filter) ?;
119
137
Ok ( ( ) )
120
138
} ) )
121
139
}
122
140
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
+
123
151
enum EventFormat < ' a > {
124
152
Full ( Format < Full , UtcTime < Vec < BorrowedFormatItem < ' a > > > > ) ,
125
153
Json ( Format < Json > ) ,
@@ -136,17 +164,9 @@ impl EventFormat<'_> {
136
164
let json_format = tracing_subscriber:: fmt:: format ( ) . json ( ) ;
137
165
EventFormat :: Json ( json_format)
138
166
} 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
-
147
167
let full_format = tracing_subscriber:: fmt:: format ( )
148
168
. with_target ( true )
149
- . with_timer ( timer ) ;
169
+ . with_timer ( time_formatter ( ) ) ;
150
170
151
171
EventFormat :: Full ( full_format)
152
172
}
@@ -191,3 +211,129 @@ impl FormatFields<'_> for FieldFormat {
191
211
}
192
212
}
193
213
}
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