src/ipc_log_layer.rs
use std::sync::atomic::{AtomicBool, Ordering};
use tracing::{Event, Subscriber}; use tracing_subscriber::layer::Context; use tracing_subscriber::Layer;
use crate::ipc::{send_msg, OutMsg};
/// A flag that gates whether the IPC layer should attempt to send.
/// Set to true once the IPC writer thread is ready.
static IPC_LOG_READY: AtomicBool = AtomicBool::new(false);
pub fn mark_ipc_log_ready() { IPC_LOG_READY.store(true, Ordering::Release); }
pub struct IpcLogLayer;
impl<S: Subscriber> Layer for IpcLogLayer {
fn on_event(&self, event: &Event<'_>, ctx: Context<', S>) {
if !IPC_LOG_READY.load(Ordering::Acquire) {
return;
}
let metadata = event.metadata();
let level = metadata.level();
// Only forward info, warn, error (skip debug/trace to avoid flooding IPC).
if *level > tracing::Level::INFO {
return;
}
let level_str = match *level {
tracing::Level::ERROR => "error",
tracing::Level::WARN => "warn",
tracing::Level::INFO => "info",
_ => return,
};
let target = metadata.target().to_string();
// Collect fields from the event into a JSON object.
let mut fields = serde_json::Map::new();
let mut message = String::new();
struct FieldVisitor<'a> {
fields: &'a mut serde_json::Map<String, serde_json::Value>,
message: &'a mut String,
}
impl<'a> tracing::field::Visit for FieldVisitor<'a> {
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
if field.name() == "message" {
*self.message = value.to_string();
} else {
self.fields.insert(
field.name().to_string(),
serde_json::Value::String(value.to_string()),
);
}
}
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
let s = format!("{value:?}");
if field.name() == "message" {
*self.message = s;
} else {
self.fields
.insert(field.name().to_string(), serde_json::Value::String(s));
}
}
fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
self.fields.insert(
field.name().to_string(),
serde_json::Value::Number(value.into()),
);
}
fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
self.fields.insert(
field.name().to_string(),
serde_json::Value::Number(value.into()),
);
}
fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
self.fields
.insert(field.name().to_string(), serde_json::Value::Bool(value));
}
fn record_f64(&mut self, field: &tracing::field::Field, value: f64) {
if let Some(n) = serde_json::Number::from_f64(value) {
self.fields
.insert(field.name().to_string(), serde_json::Value::Number(n));
}
}
}
event.record(&mut FieldVisitor {
fields: &mut fields,
message: &mut message,
});
send_msg(&OutMsg::Log {
level: level_str.to_string(),
target,
message,
fields: serde_json::Value::Object(fields),
});
}
}
