1use std::sync::OnceLock;
2#[cfg(feature = "sentry")]
3use std::sync::{atomic::AtomicBool, Arc};
4
5#[cfg(feature = "sentry")]
6use tracing::warn;
7use tracing_appender::rolling::{RollingFileAppender, Rotation};
8#[cfg(feature = "sentry")]
9use tracing_core::Level;
10use tracing_core::Subscriber;
11use tracing_subscriber::{
12 field::RecordFields,
13 fmt::{
14 self,
15 format::{DefaultFields, Writer},
16 time::FormatTime,
17 FormatEvent, FormatFields, FormattedFields,
18 },
19 layer::{Layered, SubscriberExt as _},
20 registry::LookupSpan,
21 reload::{self, Handle},
22 util::SubscriberInitExt as _,
23 EnvFilter, Layer, Registry,
24};
25
26#[cfg(feature = "sentry")]
27use crate::tracing::BRIDGE_SPAN_NAME;
28use crate::{error::ClientError, tracing::LogLevel};
29
30struct EventFormatter {
32 display_timestamp: bool,
33 display_level: bool,
34}
35
36impl EventFormatter {
37 fn new() -> Self {
38 Self { display_timestamp: true, display_level: true }
39 }
40
41 #[cfg(target_os = "android")]
42 fn for_logcat() -> Self {
43 Self { display_timestamp: false, display_level: false }
45 }
46
47 fn format_timestamp(&self, writer: &mut fmt::format::Writer<'_>) -> std::fmt::Result {
48 if fmt::time::SystemTime.format_time(writer).is_err() {
49 writer.write_str("<unknown time>")?;
50 }
51 Ok(())
52 }
53
54 fn write_filename(
55 &self,
56 writer: &mut fmt::format::Writer<'_>,
57 filename: &str,
58 ) -> std::fmt::Result {
59 const CRATES_IO_PATH_MATCHER: &str = ".cargo/registry/src/index.crates.io";
60 let crates_io_filename = filename
61 .split_once(CRATES_IO_PATH_MATCHER)
62 .and_then(|(_, rest)| rest.split_once('/').map(|(_, rest)| rest));
63
64 if let Some(filename) = crates_io_filename {
65 writer.write_str("<crates.io>/")?;
66 writer.write_str(filename)
67 } else {
68 writer.write_str(filename)
69 }
70 }
71}
72
73impl<S, N> FormatEvent<S, N> for EventFormatter
74where
75 S: Subscriber + for<'a> LookupSpan<'a>,
76 N: for<'a> FormatFields<'a> + 'static,
77{
78 fn format_event(
79 &self,
80 ctx: &fmt::FmtContext<'_, S, N>,
81 mut writer: fmt::format::Writer<'_>,
82 event: &tracing_core::Event<'_>,
83 ) -> std::fmt::Result {
84 let meta = event.metadata();
85
86 if self.display_timestamp {
87 self.format_timestamp(&mut writer)?;
88 writer.write_char(' ')?;
89 }
90
91 if self.display_level {
92 write!(writer, "{:>5} ", meta.level())?;
94 }
95
96 write!(writer, "{}: ", meta.target())?;
97
98 ctx.format_fields(writer.by_ref(), event)?;
99
100 if let Some(filename) = meta.file() {
101 writer.write_str(" | ")?;
102 self.write_filename(&mut writer, filename)?;
103 if let Some(line_number) = meta.line() {
104 write!(writer, ":{line_number}")?;
105 }
106 }
107
108 if let Some(scope) = ctx.event_scope() {
109 writer.write_str(" | spans: ")?;
110
111 let mut first = true;
112
113 for span in scope.from_root() {
114 if !first {
115 writer.write_str(" > ")?;
116 }
117
118 first = false;
119
120 write!(writer, "{}", span.name())?;
121
122 if let Some(fields) = &span.extensions().get::<FormattedFields<N>>() {
123 if !fields.is_empty() {
124 write!(writer, "{{{fields}}}")?;
125 }
126 }
127 }
128 }
129
130 writeln!(writer)
131 }
132}
133
134#[derive(Default)]
139struct FieldsFormatterForFiles(DefaultFields);
140
141impl<'writer> FormatFields<'writer> for FieldsFormatterForFiles {
142 fn format_fields<R: RecordFields>(
143 &self,
144 writer: Writer<'writer>,
145 fields: R,
146 ) -> std::fmt::Result {
147 self.0.format_fields(writer, fields)
148 }
149}
150
151type ReloadHandle = Handle<
152 tracing_subscriber::fmt::Layer<
153 Layered<EnvFilter, Registry>,
154 FieldsFormatterForFiles,
155 EventFormatter,
156 RollingFileAppender,
157 >,
158 Layered<EnvFilter, Registry>,
159>;
160
161fn text_layers(
162 config: TracingConfiguration,
163) -> (impl Layer<Layered<EnvFilter, Registry>>, Option<ReloadHandle>) {
164 let (file_layer, reload_handle) = config
165 .write_to_files
166 .map(|c| {
167 let layer = make_file_layer(c);
168 reload::Layer::new(layer)
169 })
170 .unzip();
171
172 let layers = Layer::and_then(
173 file_layer,
174 config.write_to_stdout_or_system.then(|| {
175 #[derive(Default)]
180 struct FieldsFormatterFormStdoutOrSystem(DefaultFields);
181
182 impl<'writer> FormatFields<'writer> for FieldsFormatterFormStdoutOrSystem {
183 fn format_fields<R: RecordFields>(
184 &self,
185 writer: Writer<'writer>,
186 fields: R,
187 ) -> std::fmt::Result {
188 self.0.format_fields(writer, fields)
189 }
190 }
191
192 #[cfg(not(target_os = "android"))]
193 return fmt::layer()
194 .fmt_fields(FieldsFormatterFormStdoutOrSystem::default())
195 .event_format(EventFormatter::new())
196 .with_ansi(false)
198 .with_writer(std::io::stderr);
199
200 #[cfg(target_os = "android")]
201 return fmt::layer()
202 .fmt_fields(FieldsFormatterFormStdoutOrSystem::default())
203 .event_format(EventFormatter::for_logcat())
204 .with_ansi(false)
206 .with_writer(paranoid_android::AndroidLogMakeWriter::new(
207 "org.matrix.rust.sdk".to_owned(),
208 ));
209 }),
210 );
211
212 (layers, reload_handle)
213}
214
215fn make_file_layer(
216 file_configuration: TracingFileConfiguration,
217) -> tracing_subscriber::fmt::Layer<
218 Layered<EnvFilter, Registry, Registry>,
219 FieldsFormatterForFiles,
220 EventFormatter,
221 RollingFileAppender,
222> {
223 let mut builder = RollingFileAppender::builder()
224 .rotation(Rotation::HOURLY)
225 .filename_prefix(&file_configuration.file_prefix);
226
227 if let Some(max_files) = file_configuration.max_files {
228 builder = builder.max_log_files(max_files as usize)
229 }
230 if let Some(file_suffix) = file_configuration.file_suffix {
231 builder = builder.filename_suffix(file_suffix)
232 }
233
234 let writer =
235 builder.build(&file_configuration.path).expect("Failed to create a rolling file appender.");
236
237 fmt::layer()
238 .fmt_fields(FieldsFormatterForFiles::default())
239 .event_format(EventFormatter::new())
240 .with_ansi(false)
244 .with_writer(writer)
245}
246
247#[derive(uniffi::Record)]
249pub struct TracingFileConfiguration {
250 path: String,
252
253 file_prefix: String,
255
256 file_suffix: Option<String>,
258
259 max_files: Option<u64>,
264}
265
266#[derive(PartialEq, PartialOrd)]
267enum LogTarget {
268 Hyper,
270
271 MatrixSdkFfi,
273
274 MatrixSdkBaseEventCache,
276 MatrixSdkBaseSlidingSync,
277 MatrixSdkBaseStoreAmbiguityMap,
278 MatrixSdkBaseResponseProcessors,
279
280 MatrixSdkCommonCrossProcessLock,
282 MatrixSdkCommonDeserializedResponses,
283
284 MatrixSdk,
286 MatrixSdkClient,
287 MatrixSdkCrypto,
288 MatrixSdkCryptoAccount,
289 MatrixSdkEventCache,
290 MatrixSdkEventCacheStore,
291 MatrixSdkHttpClient,
292 MatrixSdkLatestEvents,
293 MatrixSdkOidc,
294 MatrixSdkSendQueue,
295 MatrixSdkSlidingSync,
296
297 MatrixSdkUiTimeline,
299 MatrixSdkUiNotificationClient,
300}
301
302impl LogTarget {
303 fn as_str(&self) -> &'static str {
304 match self {
305 LogTarget::Hyper => "hyper",
306 LogTarget::MatrixSdkFfi => "matrix_sdk_ffi",
307 LogTarget::MatrixSdkBaseEventCache => "matrix_sdk_base::event_cache",
308 LogTarget::MatrixSdkBaseSlidingSync => "matrix_sdk_base::sliding_sync",
309 LogTarget::MatrixSdkBaseStoreAmbiguityMap => "matrix_sdk_base::store::ambiguity_map",
310 LogTarget::MatrixSdkBaseResponseProcessors => "matrix_sdk_base::response_processors",
311 LogTarget::MatrixSdkCommonCrossProcessLock => "matrix_sdk_common::cross_process_lock",
312 LogTarget::MatrixSdkCommonDeserializedResponses => {
313 "matrix_sdk_common::deserialized_responses"
314 }
315 LogTarget::MatrixSdk => "matrix_sdk",
316 LogTarget::MatrixSdkClient => "matrix_sdk::client",
317 LogTarget::MatrixSdkCrypto => "matrix_sdk_crypto",
318 LogTarget::MatrixSdkCryptoAccount => "matrix_sdk_crypto::olm::account",
319 LogTarget::MatrixSdkOidc => "matrix_sdk::oidc",
320 LogTarget::MatrixSdkHttpClient => "matrix_sdk::http_client",
321 LogTarget::MatrixSdkSlidingSync => "matrix_sdk::sliding_sync",
322 LogTarget::MatrixSdkEventCache => "matrix_sdk::event_cache",
323 LogTarget::MatrixSdkLatestEvents => "matrix_sdk::latest_events",
324 LogTarget::MatrixSdkSendQueue => "matrix_sdk::send_queue",
325 LogTarget::MatrixSdkEventCacheStore => "matrix_sdk_sqlite::event_cache_store",
326 LogTarget::MatrixSdkUiTimeline => "matrix_sdk_ui::timeline",
327 LogTarget::MatrixSdkUiNotificationClient => "matrix_sdk_ui::notification_client",
328 }
329 }
330}
331
332const DEFAULT_TARGET_LOG_LEVELS: &[(LogTarget, LogLevel)] = &[
333 (LogTarget::Hyper, LogLevel::Warn),
334 (LogTarget::MatrixSdkFfi, LogLevel::Info),
335 (LogTarget::MatrixSdk, LogLevel::Info),
336 (LogTarget::MatrixSdkClient, LogLevel::Trace),
337 (LogTarget::MatrixSdkCrypto, LogLevel::Debug),
338 (LogTarget::MatrixSdkCryptoAccount, LogLevel::Trace),
339 (LogTarget::MatrixSdkOidc, LogLevel::Trace),
340 (LogTarget::MatrixSdkHttpClient, LogLevel::Debug),
341 (LogTarget::MatrixSdkSlidingSync, LogLevel::Info),
342 (LogTarget::MatrixSdkBaseSlidingSync, LogLevel::Info),
343 (LogTarget::MatrixSdkUiTimeline, LogLevel::Info),
344 (LogTarget::MatrixSdkSendQueue, LogLevel::Info),
345 (LogTarget::MatrixSdkEventCache, LogLevel::Info),
346 (LogTarget::MatrixSdkLatestEvents, LogLevel::Info),
347 (LogTarget::MatrixSdkBaseEventCache, LogLevel::Info),
348 (LogTarget::MatrixSdkEventCacheStore, LogLevel::Info),
349 (LogTarget::MatrixSdkCommonCrossProcessLock, LogLevel::Warn),
350 (LogTarget::MatrixSdkCommonDeserializedResponses, LogLevel::Warn),
351 (LogTarget::MatrixSdkBaseStoreAmbiguityMap, LogLevel::Warn),
352 (LogTarget::MatrixSdkUiNotificationClient, LogLevel::Info),
353 (LogTarget::MatrixSdkBaseResponseProcessors, LogLevel::Debug),
354];
355
356const IMMUTABLE_LOG_TARGETS: &[LogTarget] = &[
357 LogTarget::Hyper, LogTarget::MatrixSdk, LogTarget::MatrixSdkFfi, LogTarget::MatrixSdkCommonCrossProcessLock, LogTarget::MatrixSdkBaseStoreAmbiguityMap, ];
363
364#[derive(uniffi::Enum)]
367pub enum TraceLogPacks {
368 EventCache,
370 SendQueue,
372 Timeline,
374 NotificationClient,
376 SyncProfiling,
378 LatestEvents,
380}
381
382impl TraceLogPacks {
383 fn targets(&self) -> &[LogTarget] {
386 match self {
387 TraceLogPacks::EventCache => &[
388 LogTarget::MatrixSdkEventCache,
389 LogTarget::MatrixSdkBaseEventCache,
390 LogTarget::MatrixSdkEventCacheStore,
391 LogTarget::MatrixSdkCommonCrossProcessLock,
392 LogTarget::MatrixSdkCommonDeserializedResponses,
393 ],
394 TraceLogPacks::SendQueue => &[LogTarget::MatrixSdkSendQueue],
395 TraceLogPacks::Timeline => {
396 &[LogTarget::MatrixSdkUiTimeline, LogTarget::MatrixSdkCommonDeserializedResponses]
397 }
398 TraceLogPacks::NotificationClient => &[LogTarget::MatrixSdkUiNotificationClient],
399 TraceLogPacks::SyncProfiling => &[
400 LogTarget::MatrixSdkSlidingSync,
401 LogTarget::MatrixSdkBaseSlidingSync,
402 LogTarget::MatrixSdkBaseResponseProcessors,
403 LogTarget::MatrixSdkCrypto,
404 LogTarget::MatrixSdkCommonCrossProcessLock,
405 LogTarget::MatrixSdkCommonDeserializedResponses,
406 ],
407 TraceLogPacks::LatestEvents => &[
408 LogTarget::MatrixSdkLatestEvents,
409 LogTarget::MatrixSdkSendQueue,
410 LogTarget::MatrixSdkEventCache,
411 ],
412 }
413 }
414}
415
416#[cfg(feature = "sentry")]
417struct SentryLoggingCtx {
418 _guard: sentry::ClientInitGuard,
420
421 enabled: Arc<AtomicBool>,
423}
424
425struct LoggingCtx {
426 reload_handle: Option<ReloadHandle>,
427 #[cfg(feature = "sentry")]
428 sentry: Option<SentryLoggingCtx>,
429}
430
431static LOGGING: OnceLock<LoggingCtx> = OnceLock::new();
432
433#[derive(uniffi::Record)]
434pub struct TracingConfiguration {
435 log_level: LogLevel,
437
438 trace_log_packs: Vec<TraceLogPacks>,
440
441 extra_targets: Vec<String>,
447
448 write_to_stdout_or_system: bool,
450
451 write_to_files: Option<TracingFileConfiguration>,
453
454 #[cfg(feature = "sentry")]
456 sentry_dsn: Option<String>,
457}
458
459impl TracingConfiguration {
460 #[cfg_attr(not(feature = "sentry"), allow(unused_mut))]
463 fn build(mut self) -> LoggingCtx {
464 std::env::set_var("RUST_BACKTRACE", "1");
466
467 log_panics::init();
469
470 let env_filter = build_tracing_filter(&self);
471
472 let logging_ctx;
473 #[cfg(feature = "sentry")]
474 {
475 let (sentry_layer, sentry_logging_ctx) =
477 if let Some(sentry_dsn) = self.sentry_dsn.take() {
478 let sentry_guard = sentry::init((
480 sentry_dsn,
481 sentry::ClientOptions {
482 traces_sampler: Some(Arc::new(|ctx| {
483 if ctx.name() == BRIDGE_SPAN_NAME {
485 1.0
486 } else {
487 0.0
488 }
489 })),
490 attach_stacktrace: true,
491 release: Some(env!("VERGEN_GIT_SHA").into()),
492 ..sentry::ClientOptions::default()
493 },
494 ));
495
496 let sentry_enabled = Arc::new(AtomicBool::new(true));
497
498 let sentry_layer = sentry_tracing::layer()
504 .event_filter({
505 let enabled = sentry_enabled.clone();
506
507 move |metadata| {
508 if enabled.load(std::sync::atomic::Ordering::SeqCst)
509 && metadata.fields().field("sentry").is_some()
510 {
511 sentry_tracing::default_event_filter(metadata)
512 } else {
513 sentry_tracing::EventFilter::Ignore
515 }
516 }
517 })
518 .span_filter({
519 let enabled = sentry_enabled.clone();
520
521 move |metadata| {
522 if enabled.load(std::sync::atomic::Ordering::SeqCst) {
523 matches!(
524 metadata.level(),
525 &Level::ERROR | &Level::WARN | &Level::INFO | &Level::DEBUG
526 )
527 } else {
528 false
530 }
531 }
532 });
533
534 (
535 Some(sentry_layer),
536 Some(SentryLoggingCtx { _guard: sentry_guard, enabled: sentry_enabled }),
537 )
538 } else {
539 (None, None)
540 };
541 let (text_layers, reload_handle) = crate::platform::text_layers(self);
542
543 tracing_subscriber::registry()
544 .with(tracing_subscriber::EnvFilter::new(&env_filter))
545 .with(text_layers)
546 .with(sentry_layer)
547 .init();
548 logging_ctx = LoggingCtx { reload_handle, sentry: sentry_logging_ctx };
549 }
550 #[cfg(not(feature = "sentry"))]
551 {
552 let (text_layers, reload_handle) = crate::platform::text_layers(self);
553 tracing_subscriber::registry()
554 .with(tracing_subscriber::EnvFilter::new(&env_filter))
555 .with(text_layers)
556 .init();
557 logging_ctx = LoggingCtx { reload_handle };
558 }
559
560 tracing::info!(env_filter, "Logging has been set up");
562
563 logging_ctx
564 }
565}
566
567fn build_tracing_filter(config: &TracingConfiguration) -> String {
568 let mut filters = vec!["panic=error".to_owned()];
573
574 let global_level = config.log_level;
575
576 DEFAULT_TARGET_LOG_LEVELS.iter().for_each(|(target, default_level)| {
577 let level = if IMMUTABLE_LOG_TARGETS.contains(target) {
578 *default_level
580 } else if config.trace_log_packs.iter().any(|pack| pack.targets().contains(target)) {
581 LogLevel::Trace
583 } else if *default_level > global_level {
584 *default_level
586 } else {
587 global_level
589 };
590
591 filters.push(format!("{}={}", target.as_str(), level.as_str()));
592 });
593
594 for target in &config.extra_targets {
596 filters.push(format!("{}={}", target, config.log_level.as_str()));
597 }
598
599 filters.join(",")
600}
601
602#[matrix_sdk_ffi_macros::export]
609pub fn init_platform(
610 config: TracingConfiguration,
611 use_lightweight_tokio_runtime: bool,
612) -> Result<(), ClientError> {
613 #[cfg(all(feature = "js", target_family = "wasm"))]
614 {
615 console_error_panic_hook::set_once();
616 }
617 #[cfg(not(target_family = "wasm"))]
618 {
619 LOGGING.set(config.build()).map_err(|_| ClientError::Generic {
620 msg: "logger already initialized".to_owned(),
621 details: None,
622 })?;
623
624 if use_lightweight_tokio_runtime {
625 setup_lightweight_tokio_runtime();
626 } else {
627 setup_multithreaded_tokio_runtime();
628 }
629 }
630
631 Ok(())
632}
633
634#[matrix_sdk_ffi_macros::export]
637#[cfg(feature = "sentry")]
638pub fn enable_sentry_logging(enabled: bool) {
639 if let Some(ctx) = LOGGING.get() {
640 if let Some(sentry_ctx) = &ctx.sentry {
641 sentry_ctx.enabled.store(enabled, std::sync::atomic::Ordering::SeqCst);
642 } else {
643 warn!("Sentry logging is not enabled");
644 }
645 } else {
646 eprintln!("Logging hasn't been enabled yet");
648 };
649}
650
651#[matrix_sdk_ffi_macros::export]
657pub fn reload_tracing_file_writer(
658 configuration: TracingFileConfiguration,
659) -> Result<(), ClientError> {
660 let Some(logging_context) = LOGGING.get() else {
661 return Err(ClientError::Generic {
662 msg: "Logging hasn't been initialized yet".to_owned(),
663 details: None,
664 });
665 };
666
667 let Some(reload_handle) = logging_context.reload_handle.as_ref() else {
668 return Err(ClientError::Generic {
669 msg: "Logging wasn't initialized with a file config".to_owned(),
670 details: None,
671 });
672 };
673
674 let layer = make_file_layer(configuration);
675 reload_handle.reload(layer).map_err(|error| ClientError::Generic {
676 msg: format!("Failed to reload file config: {error}"),
677 details: None,
678 })
679}
680
681#[cfg(not(target_family = "wasm"))]
682fn setup_multithreaded_tokio_runtime() {
683 async_compat::set_runtime_builder(Box::new(|| {
684 eprintln!("spawning a multithreaded tokio runtime");
685
686 let mut builder = tokio::runtime::Builder::new_multi_thread();
687 builder.enable_all();
688 builder
689 }));
690}
691
692#[cfg(not(target_family = "wasm"))]
693fn setup_lightweight_tokio_runtime() {
694 async_compat::set_runtime_builder(Box::new(|| {
695 eprintln!("spawning a lightweight tokio runtime");
696
697 let num_available_cores =
699 std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
700
701 let num_worker_threads = num_available_cores.min(4);
703
704 let num_blocking_threads = 2;
706
707 let max_memory_bytes = 1024 * 1024;
709
710 let mut builder = tokio::runtime::Builder::new_multi_thread();
711
712 builder
713 .enable_all()
714 .worker_threads(num_worker_threads)
715 .thread_stack_size(max_memory_bytes)
716 .max_blocking_threads(num_blocking_threads);
717
718 builder
719 }));
720}
721
722#[cfg(test)]
723mod tests {
724 use similar_asserts::assert_eq;
725
726 use super::build_tracing_filter;
727 use crate::platform::TraceLogPacks;
728
729 #[test]
730 fn test_default_tracing_filter() {
731 let config = super::TracingConfiguration {
732 log_level: super::LogLevel::Error,
733 trace_log_packs: Vec::new(),
734 extra_targets: vec!["super_duper_app".to_owned()],
735 write_to_stdout_or_system: true,
736 write_to_files: None,
737 #[cfg(feature = "sentry")]
738 sentry_dsn: None,
739 };
740
741 let filter = build_tracing_filter(&config);
742
743 assert_eq!(
744 filter,
745 r#"panic=error,
746 hyper=warn,
747 matrix_sdk_ffi=info,
748 matrix_sdk=info,
749 matrix_sdk::client=trace,
750 matrix_sdk_crypto=debug,
751 matrix_sdk_crypto::olm::account=trace,
752 matrix_sdk::oidc=trace,
753 matrix_sdk::http_client=debug,
754 matrix_sdk::sliding_sync=info,
755 matrix_sdk_base::sliding_sync=info,
756 matrix_sdk_ui::timeline=info,
757 matrix_sdk::send_queue=info,
758 matrix_sdk::event_cache=info,
759 matrix_sdk::latest_events=info,
760 matrix_sdk_base::event_cache=info,
761 matrix_sdk_sqlite::event_cache_store=info,
762 matrix_sdk_common::cross_process_lock=warn,
763 matrix_sdk_common::deserialized_responses=warn,
764 matrix_sdk_base::store::ambiguity_map=warn,
765 matrix_sdk_ui::notification_client=info,
766 matrix_sdk_base::response_processors=debug,
767 super_duper_app=error"#
768 .split('\n')
769 .map(|s| s.trim())
770 .collect::<Vec<_>>()
771 .join("")
772 );
773 }
774
775 #[test]
776 fn test_trace_tracing_filter() {
777 let config = super::TracingConfiguration {
778 log_level: super::LogLevel::Trace,
779 trace_log_packs: Vec::new(),
780 extra_targets: vec!["super_duper_app".to_owned(), "some_other_span".to_owned()],
781 write_to_stdout_or_system: true,
782 write_to_files: None,
783 #[cfg(feature = "sentry")]
784 sentry_dsn: None,
785 };
786
787 let filter = build_tracing_filter(&config);
788
789 assert_eq!(
790 filter,
791 r#"panic=error,
792 hyper=warn,
793 matrix_sdk_ffi=info,
794 matrix_sdk=info,
795 matrix_sdk::client=trace,
796 matrix_sdk_crypto=trace,
797 matrix_sdk_crypto::olm::account=trace,
798 matrix_sdk::oidc=trace,
799 matrix_sdk::http_client=trace,
800 matrix_sdk::sliding_sync=trace,
801 matrix_sdk_base::sliding_sync=trace,
802 matrix_sdk_ui::timeline=trace,
803 matrix_sdk::send_queue=trace,
804 matrix_sdk::event_cache=trace,
805 matrix_sdk::latest_events=trace,
806 matrix_sdk_base::event_cache=trace,
807 matrix_sdk_sqlite::event_cache_store=trace,
808 matrix_sdk_common::cross_process_lock=warn,
809 matrix_sdk_common::deserialized_responses=trace,
810 matrix_sdk_base::store::ambiguity_map=warn,
811 matrix_sdk_ui::notification_client=trace,
812 matrix_sdk_base::response_processors=trace,
813 super_duper_app=trace,
814 some_other_span=trace"#
815 .split('\n')
816 .map(|s| s.trim())
817 .collect::<Vec<_>>()
818 .join("")
819 );
820 }
821
822 #[test]
823 fn test_trace_log_packs() {
824 let config = super::TracingConfiguration {
825 log_level: super::LogLevel::Info,
826 trace_log_packs: vec![TraceLogPacks::EventCache, TraceLogPacks::SendQueue],
827 extra_targets: vec!["super_duper_app".to_owned()],
828 write_to_stdout_or_system: true,
829 write_to_files: None,
830 #[cfg(feature = "sentry")]
831 sentry_dsn: None,
832 };
833
834 let filter = build_tracing_filter(&config);
835
836 assert_eq!(
837 filter,
838 r#"panic=error,
839 hyper=warn,
840 matrix_sdk_ffi=info,
841 matrix_sdk=info,
842 matrix_sdk::client=trace,
843 matrix_sdk_crypto=debug,
844 matrix_sdk_crypto::olm::account=trace,
845 matrix_sdk::oidc=trace,
846 matrix_sdk::http_client=debug,
847 matrix_sdk::sliding_sync=info,
848 matrix_sdk_base::sliding_sync=info,
849 matrix_sdk_ui::timeline=info,
850 matrix_sdk::send_queue=trace,
851 matrix_sdk::event_cache=trace,
852 matrix_sdk::latest_events=info,
853 matrix_sdk_base::event_cache=trace,
854 matrix_sdk_sqlite::event_cache_store=trace,
855 matrix_sdk_common::cross_process_lock=warn,
856 matrix_sdk_common::deserialized_responses=trace,
857 matrix_sdk_base::store::ambiguity_map=warn,
858 matrix_sdk_ui::notification_client=info,
859 matrix_sdk_base::response_processors=debug,
860 super_duper_app=info"#
861 .split('\n')
862 .map(|s| s.trim())
863 .collect::<Vec<_>>()
864 .join("")
865 );
866 }
867}