@@ -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+ }
0 commit comments