|
|
|
|
Quelle logging.rs
Sprache: unbekannt
|
|
Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
//! Logging utility to forward logging.
use std::{io, panic};
use tracing::{Level, debug, metadata::LevelFilter};
use tracing_subscriber::fmt::MakeWriter;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(typescript_custom_section)]
const PANIC_DISPATCHER: &'static str = r#"
/**
* Dispatches panics raised in libthreema.
*
* IMPORTANT: The panic handler should still crash the application, just more gracefully and wi th
* the provided information.
*/
interface PanicDispatcher {
readonly handle: (info: string) => void;
}
"#;
#[wasm_bindgen]
extern "C" {
/// Dispatches panics raised in libthreema.
#[wasm_bindgen(
extends = ::js_sys::Object,
js_name = PanicDispatcher,
typescript_type = "PanicDispatcher"
)]
#[derive(Debug, Clone)]
pub type PanicDispatcher;
/// Pass the panic info to the panic dispatcher
#[wasm_bindgen(method, js_class = "handle", js_name = handle)]
pub fn handle(this: &PanicDispatcher, panic_info: &str);
}
// SAFETY: Safe as long as WASM is single-threaded
unsafe impl Send for PanicDispatcher {}
// SAFETY: Safe as long as WASM is single-threaded
unsafe impl Sync for PanicDispatcher {}
#[wasm_bindgen(typescript_custom_section)]
const LOG_DISPATCHER: &'static str = r#"
/** Handle a log record. */
type LogRecordFn = (record: string) => void;
/** Dispatches log records from libthreema. */
interface LogDispatcher {
readonly debug: LogRecordFn;
readonly info: LogRecordFn;
readonly warn: LogRecordFn;
readonly error: LogRecordFn;
}
"#;
// Note: Unfortunately we cannot use string enums:
// https://github.com/rustwasm/wasm-bindgen/issues/2153
#[wasm_bindgen(typescript_custom_section)]
const LOG_LEVEL: &'static str = r#"
/**
* Log levels used by libthreema.
*
* - `'trace'`: Extremely verbose, often leaking sensitive data. Only use this for specific
* debugging sessions.
* - `'debug'`: Verbose. Should not leak sensitive data and is therefore safe to use in debug
* builds.
* - `'info'`: Useful information. Should be used by release builds.
* - `'warn'`: Warns about unexpected situations.
* - `'error'`: Indicates an internal error has been encountered.
*/
type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';
"#;
#[wasm_bindgen]
extern "C" {
/// Handle a log record
#[wasm_bindgen(
extends = ::js_sys::Object,
js_name = LogDispatcher,
typescript_type = "LogDispatcher"
)]
#[derive(Debug, Clone)]
pub type LogDispatcher;
/// Log a record at the debug level
#[wasm_bindgen(method, js_class = "LogDispatcher", js_name = debug)]
pub fn debug(this: &LogDispatcher, record: &str);
/// Log a record at the info level
#[wasm_bindgen(method, js_class = "LogDispatcher", js_name = info)]
pub fn info(this: &LogDispatcher, record: &str);
/// Log a record at the warn level
#[wasm_bindgen(method, js_class = "LogDispatcher", js_name = warn)]
pub fn warn(this: &LogDispatcher, record: &str);
/// Log a record at the error level
#[wasm_bindgen(method, js_class = "LogDispatcher", js_name = error)]
pub fn error(this: &LogDispatcher, record: &str);
/// Describes the level of verbosity of the logging
#[wasm_bindgen(extends = ::js_sys::Object, js_name = LogLevel, typescript_type = "LogLevel")]
pub type LogLevel;
}
// SAFETY: Safe as long as WASM is single-threaded
unsafe impl Send for LogDispatcher {}
// SAFETY: Safe as long as WASM is single-threaded
unsafe impl Sync for LogDispatcher {}
impl From<LogLevel> for LevelFilter {
fn from(level: LogLevel) -> LevelFilter {
match level.as_string().as_deref() {
Some("trace") => LevelFilter::TRACE,
Some("debug") => LevelFilter::DEBUG,
Some("info") => LevelFilter::INFO,
Some("warn") => LevelFilter::WARN,
Some("error") => LevelFilter::ERROR,
Some(_) | None => LevelFilter::OFF,
}
}
}
struct DispatchWriter {
buffer: Vec<u8>,
level: Option<Level>,
dispatcher: LogDispatcher,
}
impl io::Write for DispatchWriter {
fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
self.buffer.write(buffer)
}
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);
match self.level {
Some(Level::DEBUG | Level::TRACE) | None => self.dispatcher.debug(&record),
Some(Level::INFO) => self.dispatcher.info(&record),
Some(Level::WARN) => self.dispatcher.warn(&record),
Some(Level::ERROR) => self.dispatcher.error(&record),
}
}
}
struct MakeDispatchWriter {
dispatcher: LogDispatcher,
}
impl MakeDispatchWriter {
fn new(dispatcher: 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: None,
dispatcher: self.dispatcher.clone(),
}
}
fn make_writer_for(&'writer self, meta: &tracing::Metadata<'_>) -> Self::Writer {
Self::Writer {
buffer: vec![],
level: Some(*meta.level()),
dispatcher: self.dispatcher.clone(),
}
}
}
/// Initialise the panic dispatcher (set it as the panic hook).
pub(super) fn init_panic_dispatcher(panic_dispatcher: PanicDispatcher) {
// Set panic hook
panic::set_hook(Box::new(move |info| {
#[wasm_bindgen]
extern "C" {
type Error;
#[wasm_bindgen(constructor, final)]
fn new() -> Error;
#[wasm_bindgen(method, getter)]
fn stack(error: &Error) -> String;
}
// Stolen from https://github.com/rustwasm/console_error_panic_hook/blob/master/src/lib.rs
// which also explains why the message is formatted as such (gist: browsers doing browser
// things).
let mut message = info.to_string();
message.push_str("\n\nStack:\n\n");
let error = Error::new();
message.push_str(&error.stack());
message.push_str("\n\n");
// Call panic dispatcher
panic_dispatcher.handle(&message);
}));
}
/// Initialise logging with the provided log dispatcher and minimum log level.
///
/// IMPORTANT: This may only be called **once**!
pub(super) fn init_logging(log_dispatcher: &LogDispatcher, min_log_level: LogLevel) {
// 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.clone()))
.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.29 Sekunden, vorverarbeitet 2026-04-27]
|
2026-05-26
|
|
|
|
|