feat(ffi): add support for sentry logging

This commit is contained in:
Benjamin Bouvier
2025-03-20 17:33:30 +01:00
parent 1348525447
commit c8474511a7
5 changed files with 311 additions and 22 deletions

172
Cargo.lock generated
View File

@@ -1207,6 +1207,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
dependencies = [
"serde",
"uuid",
]
@@ -2060,6 +2061,12 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hkdf"
version = "0.12.4"
@@ -2078,6 +2085,17 @@ dependencies = [
"digest",
]
[[package]]
name = "hostname"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba"
dependencies = [
"cfg-if",
"libc",
"windows",
]
[[package]]
name = "html5ever"
version = "0.29.0"
@@ -3096,6 +3114,8 @@ dependencies = [
"once_cell",
"paranoid-android",
"ruma",
"sentry",
"sentry-tracing",
"serde",
"serde_json",
"thiserror 2.0.11",
@@ -3623,6 +3643,17 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "os_info"
version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a604e53c24761286860eba4e2c8b23a0161526476b1de520139d69cdb85a6b5"
dependencies = [
"log",
"serde",
"windows-sys 0.52.0",
]
[[package]]
name = "overload"
version = "0.1.1"
@@ -4301,6 +4332,7 @@ dependencies = [
"async-compression",
"base64",
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
@@ -4744,6 +4776,114 @@ dependencies = [
"serde",
]
[[package]]
name = "sentry"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a7332159e544e34db06b251b1eda5e546bd90285c3f58d9c8ff8450b484e0da"
dependencies = [
"httpdate",
"native-tls",
"reqwest",
"sentry-backtrace",
"sentry-contexts",
"sentry-core",
"sentry-debug-images",
"sentry-panic",
"sentry-tracing",
"tokio",
"ureq",
]
[[package]]
name = "sentry-backtrace"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "565ec31ad37bab8e6d9f289f34913ed8768347b133706192f10606dabd5c6bc4"
dependencies = [
"backtrace",
"once_cell",
"regex",
"sentry-core",
]
[[package]]
name = "sentry-contexts"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e860275f25f27e8c0c7726ce116c7d5c928c5bba2ee73306e52b20a752298ea6"
dependencies = [
"hostname",
"libc",
"os_info",
"rustc_version",
"sentry-core",
"uname",
]
[[package]]
name = "sentry-core"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "653942e6141f16651273159f4b8b1eaeedf37a7554c00cd798953e64b8a9bf72"
dependencies = [
"once_cell",
"rand",
"sentry-types",
"serde",
"serde_json",
]
[[package]]
name = "sentry-debug-images"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60bc2154e6df59beed0ac13d58f8dfaf5ad20a88548a53e29e4d92e8e835c2"
dependencies = [
"findshlibs",
"once_cell",
"sentry-core",
]
[[package]]
name = "sentry-panic"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "105e3a956c8aa9dab1e4087b1657b03271bfc49d838c6ae9bfc7c58c802fd0ef"
dependencies = [
"sentry-backtrace",
"sentry-core",
]
[[package]]
name = "sentry-tracing"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e75c831b4d8b34a5aec1f65f67c5d46a26c7c5d3c7abd8b5ef430796900cf8"
dependencies = [
"sentry-backtrace",
"sentry-core",
"tracing-core",
"tracing-subscriber",
]
[[package]]
name = "sentry-types"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d4203359e60724aa05cf2385aaf5d4f147e837185d7dd2b9ccf1ee77f4420c8"
dependencies = [
"debugid",
"hex",
"rand",
"serde",
"serde_json",
"thiserror 1.0.63",
"time",
"url",
"uuid",
]
[[package]]
name = "serde"
version = "1.0.217"
@@ -5613,6 +5753,15 @@ dependencies = [
"web-time",
]
[[package]]
name = "uname"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8"
dependencies = [
"libc",
]
[[package]]
name = "unarray"
version = "0.1.4"
@@ -5827,6 +5976,19 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
dependencies = [
"base64",
"log",
"native-tls",
"once_cell",
"url",
]
[[package]]
name = "url"
version = "2.5.4"
@@ -6153,6 +6315,16 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
dependencies = [
"windows-core",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.52.0"

View File

@@ -78,6 +78,8 @@ ruma = { git = "https://github.com/ruma/ruma", rev = "a8fd1b0322649bf59e2a5cfc73
"unstable-msc4278",
] }
ruma-common = { git = "https://github.com/ruma/ruma", rev = "a8fd1b0322649bf59e2a5cfc73ab4fe46b21edd7" }
sentry = "0.36.0"
sentry-tracing = "0.36.0"
serde = "1.0.217"
serde_html_form = "0.2.7"
serde_json = "1.0.138"

View File

@@ -19,6 +19,8 @@ Breaking changes:
Additions:
- Support for adding a Sentry layer to the FFI bindings has been added. Only `tracing` statements with
the field `sentry=true` will be forwarded to Sentry, in addition to default Sentry filters.
- Add room topic string to `StateEventContent`
- Add `UploadSource` for representing upload data - this is analogous to `matrix_sdk_ui::timeline::AttachmentSource`
- Add `Client::observe_account_data_event` and `Client::observe_room_account_data_event` to

View File

@@ -34,6 +34,8 @@ matrix-sdk-ui = { workspace = true, features = ["uniffi"] }
mime = "0.3.16"
once_cell = { workspace = true }
ruma = { workspace = true, features = ["html", "unstable-unspecified", "unstable-msc3488", "compat-unset-avatar", "unstable-msc3245-v1-compat", "unstable-msc4278"] }
sentry = { workspace = true }
sentry-tracing = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }

View File

@@ -1,3 +1,6 @@
use std::sync::{atomic::AtomicBool, Arc, OnceLock};
use tracing::warn;
use tracing_appender::rolling::{RollingFileAppender, Rotation};
use tracing_core::Subscriber;
use tracing_subscriber::{
@@ -8,19 +11,13 @@ use tracing_subscriber::{
time::FormatTime,
FormatEvent, FormatFields, FormattedFields,
},
layer::SubscriberExt,
layer::SubscriberExt as _,
registry::LookupSpan,
util::SubscriberInitExt,
EnvFilter, Layer,
util::SubscriberInitExt as _,
Layer,
};
use crate::tracing::LogLevel;
pub fn log_panics() {
std::env::set_var("RUST_BACKTRACE", "1");
log_panics::init();
}
use crate::{error::ClientError, tracing::LogLevel};
fn text_layers<S>(config: TracingConfiguration) -> impl Layer<S>
where
@@ -343,6 +340,20 @@ impl TraceLogPacks {
}
}
struct SentryLoggingCtx {
/// The Sentry client guard, which keeps the Sentry context alive.
_guard: sentry::ClientInitGuard,
/// Whether the Sentry layer is enabled or not, at a global level.
enabled: Arc<AtomicBool>,
}
struct LoggingCtx {
sentry: Option<SentryLoggingCtx>,
}
static LOGGING: OnceLock<LoggingCtx> = OnceLock::new();
#[derive(uniffi::Record)]
pub struct TracingConfiguration {
/// The desired log level.
@@ -363,6 +374,89 @@ pub struct TracingConfiguration {
/// If set, configures rotated log files where to write additional logs.
write_to_files: Option<TracingFileConfiguration>,
/// If set, the Sentry DSN to use for error reporting.
sentry_dsn: Option<String>,
}
impl TracingConfiguration {
/// Sets up the tracing configuration and return a [`Logger`] instance
/// holding onto it.
fn build(mut self) -> LoggingCtx {
// Show full backtraces, if we run into panics.
std::env::set_var("RUST_BACKTRACE", "1");
// Log panics.
log_panics::init();
// Prepare the Sentry layer, if a DSN is provided.
let (sentry_layer, sentry_logging_ctx) = if let Some(sentry_dsn) = self.sentry_dsn.take() {
// Initialize the Sentry client with the given options.
let sentry_guard = sentry::init((
sentry_dsn,
sentry::ClientOptions {
traces_sample_rate: 0.0,
attach_stacktrace: true,
..sentry::ClientOptions::default()
},
));
let sentry_enabled = Arc::new(AtomicBool::new(true));
// Add a Sentry layer to the tracing subscriber.
//
// Pass custom event and span filters, which will ignore anything, if the Sentry
// support has been globally disabled, or if the statement doesn't include a
// `sentry` field set to `true`.
let sentry_layer = sentry_tracing::layer()
.event_filter({
let enabled = sentry_enabled.clone();
move |metadata| {
if enabled.load(std::sync::atomic::Ordering::SeqCst)
&& metadata.fields().field("sentry").is_some()
{
sentry_tracing::default_event_filter(metadata)
} else {
// Ignore the event.
sentry_tracing::EventFilter::Ignore
}
}
})
.span_filter({
let enabled = sentry_enabled.clone();
move |metadata| {
if enabled.load(std::sync::atomic::Ordering::SeqCst) {
sentry_tracing::default_span_filter(metadata)
} else {
// Ignore, if sentry is globally disabled.
false
}
}
});
(
Some(sentry_layer),
Some(SentryLoggingCtx { _guard: sentry_guard, enabled: sentry_enabled }),
)
} else {
(None, None)
};
let env_filter = build_tracing_filter(&self);
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(&env_filter))
.with(crate::platform::text_layers(self))
.with(sentry_layer)
.init();
// Log the log levels 🧠.
tracing::info!(env_filter, "Logging has been set up");
LoggingCtx { sentry: sentry_logging_ctx }
}
}
fn build_tracing_filter(config: &TracingConfiguration) -> String {
@@ -407,24 +501,38 @@ fn build_tracing_filter(config: &TracingConfiguration) -> String {
/// the NSE process on iOS). Otherwise, this can remain false, in which case a
/// multithreaded tokio runtime will be set up.
#[matrix_sdk_ffi_macros::export]
pub fn init_platform(config: TracingConfiguration, use_lightweight_tokio_runtime: bool) {
log_panics();
let env_filter = build_tracing_filter(&config);
tracing_subscriber::registry()
.with(EnvFilter::new(&env_filter))
.with(text_layers(config))
.init();
// Log the log levels 🧠.
tracing::info!(env_filter, "Logging has been set up");
pub fn init_platform(
config: TracingConfiguration,
use_lightweight_tokio_runtime: bool,
) -> Result<(), ClientError> {
LOGGING.set(config.build()).map_err(|_| ClientError::Generic {
msg: "logger already initialized".to_owned(),
details: None,
})?;
if use_lightweight_tokio_runtime {
setup_lightweight_tokio_runtime();
} else {
setup_multithreaded_tokio_runtime();
}
Ok(())
}
/// Set the global enablement level for the Sentry layer (after the logs have
/// been set up).
#[matrix_sdk_ffi_macros::export]
pub fn enable_sentry_logging(enabled: bool) {
if let Some(ctx) = LOGGING.get() {
if let Some(sentry_ctx) = &ctx.sentry {
sentry_ctx.enabled.store(enabled, std::sync::atomic::Ordering::SeqCst);
} else {
warn!("Sentry logging is not enabled");
}
} else {
// Can't use log statements here, since logging hasn't been enabled yet 🧠
eprintln!("Logging hasn't been enabled yet");
};
}
fn setup_multithreaded_tokio_runtime() {
@@ -479,6 +587,7 @@ mod tests {
extra_targets: vec!["super_duper_app".to_owned()],
write_to_stdout_or_system: true,
write_to_files: None,
sentry_dsn: None,
};
let filter = build_tracing_filter(&config);
@@ -515,6 +624,7 @@ mod tests {
extra_targets: vec!["super_duper_app".to_owned(), "some_other_span".to_owned()],
write_to_stdout_or_system: true,
write_to_files: None,
sentry_dsn: None,
};
let filter = build_tracing_filter(&config);
@@ -552,6 +662,7 @@ mod tests {
extra_targets: vec!["super_duper_app".to_owned()],
write_to_stdout_or_system: true,
write_to_files: None,
sentry_dsn: None,
};
let filter = build_tracing_filter(&config);