From c127e478b8710a631049b37891edf195c380bdef Mon Sep 17 00:00:00 2001 From: Jacob Date: Mon, 17 Mar 2025 21:48:50 -0400 Subject: [PATCH] feat: tracing to the Home Assistant logger --- src/lib.rs | 24 ++++++++--- src/tracing_to_home_assistant.rs | 74 ++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 src/tracing_to_home_assistant.rs diff --git a/src/lib.rs b/src/lib.rs index d399371..a30816d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,20 +4,32 @@ use home_assistant::home_assistant::HomeAssistant; use pyo3::prelude::*; use shadow_rs::shadow; use tokio::time::interval; -use tracing::Level; -use tracing_subscriber::fmt::format::FmtSpan; +use tracing::{level_filters::LevelFilter, Level}; +use tracing_subscriber::{ + fmt::{self, format::FmtSpan}, + layer::SubscriberExt, + registry, + util::SubscriberInitExt, + Layer, +}; +use tracing_to_home_assistant::TracingToHomeAssistant; mod arbitrary; mod home_assistant; mod python_utils; +mod tracing_to_home_assistant; shadow!(build_info); async fn real_main(home_assistant: HomeAssistant) -> ! { - tracing_subscriber::fmt() - .with_max_level(Level::TRACE) - .with_span_events(FmtSpan::ACTIVE) - .pretty() + registry() + .with( + fmt::layer() + .pretty() + .with_span_events(FmtSpan::ACTIVE) + .with_filter(LevelFilter::from_level(Level::TRACE)), + ) + .with(TracingToHomeAssistant) .init(); let built_at = build_info::BUILD_TIME; diff --git a/src/tracing_to_home_assistant.rs b/src/tracing_to_home_assistant.rs new file mode 100644 index 0000000..f4d161f --- /dev/null +++ b/src/tracing_to_home_assistant.rs @@ -0,0 +1,74 @@ +use std::fmt::Debug; + +use pyo3::Python; +use tracing::{ + field::{Field, Visit}, + Event, Level, Subscriber, +}; +use tracing_subscriber::{layer::Context, Layer}; + +use crate::home_assistant::logger::{HassLogger, LogData}; + +pub struct TracingToHomeAssistant; + +impl Layer for TracingToHomeAssistant { + fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) { + let meta = event.metadata(); + let file = meta.file(); + let level = meta.level(); + let line = meta.line(); + let target = meta.target(); + + let mut msg = String::new(); + + struct StringVisitor<'a> { + s: &'a mut String, + } + impl<'a> Visit for StringVisitor<'a> { + fn record_debug(&mut self, field: &Field, value: &dyn Debug) { + let field_name = field.name(); + if field_name != "message" { + self.s.push_str(field_name); + self.s.push_str(" = "); + } + self.s.push_str(&format!("{value:?}")); + self.s.push_str(", "); + } + } + let mut visitor = StringVisitor { s: &mut msg }; + event.record(&mut visitor); + + if let Some(file) = file { + msg.push_str(&format!("at {file}")); + if let Some(line) = line { + msg.push_str(&format!(":{line}")); + } + } + + let args = vec![]; + + let log_data: Option> = None; + + Python::with_gil(|py| { + let Ok(hass_logger) = HassLogger::new(py, target) else { + return; + }; + + if *level == Level::TRACE { + // Errors are ignored because there's nowhere to report them besides + // through the tracer itself! + let _ = hass_logger.debug(py, &msg, args, log_data); + } else if *level == Level::DEBUG { + let _ = hass_logger.debug(py, &msg, args, log_data); + } else if *level == Level::INFO { + let _ = hass_logger.info(py, &msg, args, log_data); + } else if *level == Level::WARN { + let _ = hass_logger.warning(py, &msg, args, log_data); + } else if *level == Level::ERROR { + let _ = hass_logger.error(py, &msg, args, log_data); + } else { + unreachable!("those are all 5 levels"); + } + }); + } +}