initial commit
This commit is contained in:
parent
946c54f90c
commit
9b6c4e8daa
5
.gitignore
vendored
5
.gitignore
vendored
@ -12,3 +12,8 @@ Cargo.lock
|
|||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||||
*.pdb
|
*.pdb
|
||||||
|
|
||||||
|
|
||||||
|
# Added by cargo
|
||||||
|
|
||||||
|
/target
|
||||||
|
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
@ -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"] }
|
198
src/eventlog.rs
Normal file
198
src/eventlog.rs
Normal file
@ -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::<i8>()) };
|
||||||
|
|
||||||
|
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<S> Layer<S> 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::<Vec<String>>()
|
||||||
|
.join(" / "),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitor.log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct EventVisitor {
|
||||||
|
id: Option<u32>,
|
||||||
|
log_level: Level,
|
||||||
|
message: Option<String>,
|
||||||
|
parents: Option<String>,
|
||||||
|
fields: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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::<String>()
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
}
|
3
src/lib.rs
Normal file
3
src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod eventlog;
|
||||||
|
|
||||||
|
pub use eventlog::EventLogLayer;
|
Loading…
x
Reference in New Issue
Block a user