Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
//! Logging utility to forward logging.
use std::{io, sync::Arc};
use tracing::{Level, debug};
use tracing_subscriber::{filter::LevelFilter, fmt::MakeWriter};
/// Log levels used by libthreema.
#[derive(Clone, Copy, Debug, uniffi::Enum)]
pub enum LogLevel {
/// Extremely verbose, often leaking sensitive data. Only use this for specific debugging
/// sessions.
Trace,
/// Verbose. Should not leak sensitive data and is therefore safe to use in debug builds.
Debug,
/// Useful information. Should be used by release builds.
Info,
/// Warns about unexpected situations.
Warn,
/// Indicates an internal error has been encountered.
Error,
}
impl From<LogLevel> for LevelFilter {
fn from(level: LogLevel) -> LevelFilter {
match level {
LogLevel::Trace => LevelFilter::TRACE,
LogLevel::Debug => LevelFilter::DEBUG,
LogLevel::Info => LevelFilter::INFO,
LogLevel::Warn => LevelFilter::WARN,
LogLevel::Error => LevelFilter::ERROR,
}
}
}
impl From<Level> for LogLevel {
fn from(level: Level) -> Self {
match level {
Level::TRACE => LogLevel::Trace,
Level::DEBUG => LogLevel::Debug,
Level::INFO => LogLevel::Info,
Level::WARN => LogLevel::Warn,
Level::ERROR => LogLevel::Error,
}
}
}
/// Dispatches log records from libthreema.
#[uniffi::export(with_foreign)]
pub trait LogDispatcher: Send + Sync {
/// Handle a log record.
///
/// # Errors
///
/// This function is considered infallible and should not return an error. If it does however,
/// libthreema will discard the error.
fn log(&self, level: LogLevel, record: String) -> Result<(), super::InfallibleError>;
}
struct DispatchWriter {
buffer: Vec<u8>,
level: LogLevel,
dispatcher: Arc<dyn LogDispatcher>,
}
impl io::Write for DispatchWriter {
fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
self.buffer.write(buffer.trim_ascii_end())
}
fn flush(&mut self) -> io::Result<()> {
// Nothing to-do here, we instead flush on drop
Ok(())
}
}
impl Drop for DispatchWriter {
fn drop(&mut self) {
let record = String::from_utf8_lossy(&self.buffer).to_string();
// Ignoring result as there's not much we can do here other than to log... which will likely
// fail again
let _ = self.dispatcher.log(self.level, record);
}
}
struct MakeDispatchWriter {
dispatcher: Arc<dyn LogDispatcher>,
}
impl MakeDispatchWriter {
fn new(dispatcher: Arc<dyn LogDispatcher>) -> Self {
Self { dispatcher }
}
}
impl<'writer> MakeWriter<'writer> for MakeDispatchWriter {
type Writer = DispatchWriter;
fn make_writer(&'writer self) -> Self::Writer {
Self::Writer {
buffer: vec![],
level: LogLevel::Debug,
dispatcher: Arc::clone(&self.dispatcher),
}
}
fn make_writer_for(&'writer self, meta: &tracing::Metadata<'_>) -> Self::Writer {
Self::Writer {
buffer: vec![],
level: (*meta.level()).into(),
dispatcher: Arc::clone(&self.dispatcher),
}
}
}
/// Initialise logging with the provided log dispatcher and minimum log level.
///
/// IMPORTANT: This may only be called **once**!
pub(super) fn init_logging(min_log_level: LogLevel, log_dispatcher: Arc<dyn LogDispatcher>) {
// Configure tracing
let level_filter: LevelFilter = min_log_level.into();
let subscriber = tracing_subscriber::fmt()
.with_max_level(level_filter)
.with_ansi(false)
.with_target(false)
.with_level(false)
.without_time()
.with_writer(MakeDispatchWriter::new(log_dispatcher))
.finish();
tracing::subscriber::set_global_default(subscriber)
.expect("Cannot initialize logging multiple times. Did you call this more than once?");
debug!(?level_filter, "Configured log level filter");
}
[Dauer der Verarbeitung: 0.3 Sekunden, vorverarbeitet 2026-04-27]