From 9b6c4e8daad8bce1a3f49ddec840875f2edc4b78 Mon Sep 17 00:00:00 2001 From: itsscb Date: Wed, 17 Jul 2024 14:33:28 +0200 Subject: [PATCH] initial commit --- .gitignore | 5 ++ Cargo.toml | 9 +++ src/eventlog.rs | 198 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + 4 files changed, 215 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/eventlog.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore index 6985cf1..196e176 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,8 @@ Cargo.lock # MSVC Windows builds of rustc generate these, which store debugging information *.pdb + + +# Added by cargo + +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..79da01d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "tracing-layer-win-eventlog" +version = "0.1.0" +edition = "2021" + +[dependencies] +tracing = "0.1.40" +tracing-subscriber = "0.3.18" +winapi = { version = "0.3.9", features = ["fileapi", "handleapi", "winbase", "winnt"] } diff --git a/src/eventlog.rs b/src/eventlog.rs new file mode 100644 index 0000000..7bb20b4 --- /dev/null +++ b/src/eventlog.rs @@ -0,0 +1,198 @@ +use std::collections::HashMap; +use std::ffi::CString; +use tracing::field::Visit; +use tracing::{Level, Subscriber}; +use tracing_subscriber::registry::LookupSpan; +use tracing_subscriber::Layer; +use winapi::shared::minwindef::DWORD; +use winapi::um::winbase::{DeregisterEventSource, RegisterEventSourceA, ReportEventA}; +use winapi::um::winnt::{EVENTLOG_ERROR_TYPE, EVENTLOG_INFORMATION_TYPE, EVENTLOG_WARNING_TYPE}; + +#[allow(clippy::manual_c_str_literals)] +pub fn write_to_event_log(event_id: u32, level: Level, message: &str) { + let event_source = + unsafe { RegisterEventSourceA(std::ptr::null(), "rotx\0".as_ptr().cast::()) }; + + if event_source.is_null() { + eprintln!("Failed to register event source"); + return; + } + + let event_type = match level { + Level::ERROR => EVENTLOG_ERROR_TYPE, + Level::WARN => EVENTLOG_WARNING_TYPE, + Level::INFO | Level::DEBUG | Level::TRACE => EVENTLOG_INFORMATION_TYPE, + }; + + let Ok(message_cstr) = CString::new(message) else { + eprintln!("failed to create CString from message: {message}"); + return; + }; + + let result = unsafe { + ReportEventA( + event_source, + event_type, + 0, + event_id as DWORD, + std::ptr::null_mut(), + 1, + 0, + &mut message_cstr.as_ptr(), + std::ptr::null_mut(), + ) + }; + + if result == 0 { + eprintln!("Failed to write to event log"); + } + + unsafe { + DeregisterEventSource(event_source); + } +} + +pub struct EventLogLayer; +impl Layer for EventLogLayer +where + S: Subscriber + for<'a> LookupSpan<'a>, +{ + fn on_event(&self, event: &tracing::Event<'_>, ctx: tracing_subscriber::layer::Context<'_, S>) { + let metadata = event.metadata(); + + let mut visitor = EventVisitor { + id: None, + message: None, + parents: None, + log_level: *metadata.level(), + fields: HashMap::new(), + }; + + event.record(&mut visitor); + + let mut parents = Vec::new(); + + let span = ctx.lookup_current().map(|s| { + let mut current_span = s; + while let Some(span) = current_span.parent() { + parents.push(span.name().to_owned()); + + current_span = span; + } + current_span.name().to_owned() + }); + + if parents.is_empty() { + visitor.parents = span; + } else { + visitor.parents = Some( + parents + .into_iter() + .rev() + .collect::>() + .join(" / "), + ); + } + + visitor.log(); + } +} + +#[derive(Debug)] +struct EventVisitor { + id: Option, + log_level: Level, + message: Option, + parents: Option, + fields: HashMap, +} + +impl EventVisitor { + fn log(&self) { + let id: u32 = self.id.unwrap_or(match self.log_level { + Level::TRACE => 0, + Level::DEBUG => 1, + Level::INFO => 2, + Level::WARN => 3, + Level::ERROR => 4, + }); + + let mut msg = format!("ID: {id}\n\n"); + + if let Some(m) = &self.parents { + msg.push_str(&format!("source: {m}\n")); + } + if let Some(m) = &self.message { + msg.push_str(&format!("message: {m}\n")); + } + + self.fields.iter().for_each(|i| { + msg.push_str(&format!( + "{}: {:?}\n", + i.0, + i.1.split('=') + .skip(1) + .take(1) + .collect::() + .replace(r"\\", r"\") + )); + }); + + write_to_event_log(id, self.log_level, &msg); + } +} + +impl Visit for EventVisitor { + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + fn record_u64(&mut self, field: &tracing::field::Field, value: u64) { + if field.name().to_lowercase() == "id" { + self.id = Some(value as u32); + } else { + self.fields + .insert(field.name().to_string(), format!("{value}")); + } + } + + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + fn record_i64(&mut self, field: &tracing::field::Field, value: i64) { + if field.name().to_lowercase() == "id" { + self.id = Some(value as u32); + } else { + self.fields + .insert(field.name().to_string(), format!("{value:?}")); + } + } + + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { + if field.name().to_lowercase() == "id" { + self.id = Some(format!("{value:?}").parse().unwrap_or(0)); + } else if field.name() == "message" { + self.message = Some(format!("{value:?}")); + } else { + self.fields.insert( + field.name().to_string(), + format!("{} = {:?}, ", field.name(), value), + ); + } + } + + fn record_f64(&mut self, field: &tracing::field::Field, value: f64) { + self.record_debug(field, &value); + } + + fn record_i128(&mut self, field: &tracing::field::Field, value: i128) { + self.record_debug(field, &value); + } + + fn record_u128(&mut self, field: &tracing::field::Field, value: u128) { + self.record_debug(field, &value); + } + + fn record_bool(&mut self, field: &tracing::field::Field, value: bool) { + self.record_debug(field, &value); + } + + fn record_str(&mut self, field: &tracing::field::Field, value: &str) { + self.record_debug(field, &value); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..c3bd618 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +mod eventlog; + +pub use eventlog::EventLogLayer;