1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
use crate::prelude::*;
use std::fmt::Write;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
#[derive(Default)]
pub struct WasmSubscriber {
pub ids: AtomicUsize,
}
/// Very basic struct to hold strings that can be written to by the tracing crate and sent to the host when complete.
struct StringVisitor<'a> {
fields: &'a mut String,
message: &'a mut String,
}
impl<'a> tracing_core::field::Visit for StringVisitor<'a> {
fn record_debug(&mut self, field: &tracing_core::Field, value: &dyn std::fmt::Debug) {
// Special case the message field so that it doesn't appear in the key/value format.
if field.name() == "message" {
let did_write = write!(self.message, "{:?}", value);
if did_write.is_err() {
let _ = write!(self.message, "**failed to write message**");
}
// Everything else is a simple key/value debug output.
} else {
let did_write = write!(self.fields, "{} = {:?}; ", field.name(), value);
if did_write.is_err() {
let _ = write!(self.fields, "**failed to write {}**", field.name());
}
}
}
}
/// By implementing WasmSubscriber we integrate the rust tracing crate with the __trace host_fn without inventing some DIY DSL
///
/// Currently supports all the event macros for tracing such as `trace!`, `info!`, `debug!`, `warn!`, `error!`.
///
/// Does NOT support spans, so attempting to `#[instrument]` a function or similar will panic the wasm.
impl tracing_core::Subscriber for WasmSubscriber {
fn enabled(&self, _metadata: &tracing::Metadata<'_>) -> bool {
true
}
/// Not really implemented yet.
/// Placeholder logic only.
fn new_span(&self, _attributes: &tracing_core::span::Attributes<'_>) -> tracing::Id {
let next = self.ids.fetch_add(1, Ordering::SeqCst) as u64;
tracing::Id::from_u64(next)
}
fn record(&self, _span: &tracing::Id, _values: &tracing::span::Record<'_>) {
// unimplemented
}
fn record_follows_from(&self, _span: &tracing::Id, _follows: &tracing::Id) {
// unimplemented
}
fn event(&self, event: &tracing::Event<'_>) {
// New strings for every event.
let mut visitor = StringVisitor {
message: &mut String::new(),
fields: &mut String::new(),
};
// This is handled by the Visit implementation above.
event.record(&mut visitor);
// The event is pushed to the host to be picked up by the subscriber on that side.
// The visitor is dropped.
hdi::hdi::HDI.with(|h| {
h.borrow()
.trace(TraceMsg {
level: event.metadata().level().into(),
msg: format!(
"{}:{}:{} {}{}",
event.metadata().module_path().unwrap_or(""),
event.metadata().file().unwrap_or(""),
event.metadata().line().unwrap_or(0),
visitor.fields,
visitor.message
),
})
.ok()
});
}
fn enter(&self, _span: &tracing::Id) {
// unimplemented
}
fn exit(&self, _span: &tracing::Id) {
// unimplemented
}
}