Compare commits

..

4 Commits

Author SHA1 Message Date
638a5ff7f7 Update Cargo.toml 2025-06-04 12:10:38 +00:00
8190e33ca6 Update README.md 2025-06-04 12:09:44 +00:00
c545290e3c Update src/eventlog.rs 2025-06-04 12:08:18 +00:00
4eee1a63b6
Merge pull request #2 from itsscb/features
v0.1.3 - fix u32 overflow on ID
2024-07-17 19:16:12 +02:00
3 changed files with 82 additions and 66 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "tracing-layer-win-eventlog" name = "tracing-layer-win-eventlog"
version = "0.1.3" version = "1.0.0"
edition = "2021" edition = "2021"
author = ["itsscb <dev@itsscb.de>"] author = ["itsscb <dev@itsscb.de>"]
description = "Layer for the tracing_subscriber to write to the Windows EventLog" description = "Layer for the tracing_subscriber to write to the Windows EventLog"
@ -9,7 +9,14 @@ license = "MIT"
repository = "https://github.com/itsscb/tracing-layer-win-eventlog" repository = "https://github.com/itsscb/tracing-layer-win-eventlog"
categories =["os::windows-apis"] categories =["os::windows-apis"]
[dependencies.windows]
version = "0.61"
features = [
"Win32_Security",
"Win32_System_EventLog"
]
[dependencies] [dependencies]
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = "0.3.18" tracing-subscriber = "0.3.18"
winapi = { version = "0.3.9", features = ["fileapi", "handleapi", "winbase", "winnt"] } windows-result = "0.3"

View File

@ -14,7 +14,7 @@ New-EventLog -LogName Application -Source hello_world
use tracing_subscriber::{layer::SubscriberExt as _, util::SubscriberInitExt as _}; use tracing_subscriber::{layer::SubscriberExt as _, util::SubscriberInitExt as _};
fn main() { fn main() {
let eventlog = tracing_layer_win_eventlog::EventLogLayer::new("hello_world".to_owned()); let eventlog = tracing_layer_win_eventlog::EventLogLayer::new("hello_world");
tracing_subscriber::registry() tracing_subscriber::registry()
.with(eventlog) .with(eventlog)
@ -49,4 +49,4 @@ source: windows
message: currently in windir message: currently in windir
path: "\"C:\\Windows\"" path: "\"C:\\Windows\""
``` ```

View File

@ -1,69 +1,84 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::CString;
use tracing::field::Visit; use tracing::field::Visit;
use tracing::{Level, Subscriber}; use tracing::{Level, Subscriber};
use tracing_subscriber::registry::LookupSpan; use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::Layer; use tracing_subscriber::Layer;
use winapi::shared::minwindef::DWORD; use windows::core::{HSTRING, PCWSTR};
use winapi::um::winbase::{DeregisterEventSource, RegisterEventSourceA, ReportEventA}; use windows::Win32::Foundation::HANDLE;
use winapi::um::winnt::{EVENTLOG_ERROR_TYPE, EVENTLOG_INFORMATION_TYPE, EVENTLOG_WARNING_TYPE}; use windows::Win32::System::EventLog::{DeregisterEventSource, RegisterEventSourceW, ReportEventW, EVENTLOG_ERROR_TYPE, EVENTLOG_INFORMATION_TYPE, EVENTLOG_WARNING_TYPE};
#[allow(clippy::manual_c_str_literals)] /// Wrapper to mark the HANDLE as Send & Sync
pub fn write_to_event_log(event_id: u32, level: Level, message: &str, log_name: &str) { #[repr(transparent)]
let event_source = unsafe { #[derive(Clone, Copy, Debug)]
RegisterEventSourceA( struct EventSourceHandle {
std::ptr::null(), hwnd: *mut std::ffi::c_void,
format!("{log_name}\0").as_ptr().cast::<i8>(), }
) unsafe impl Send for EventSourceHandle {}
}; unsafe impl Sync for EventSourceHandle {}
if event_source.is_null() { impl From<EventSourceHandle> for HANDLE {
eprintln!("Failed to register event source"); fn from(value: EventSourceHandle) -> Self {
return; Self(value.hwnd)
} }
}
impl From<HANDLE> for EventSourceHandle {
fn from(value: HANDLE) -> Self {
Self{ hwnd: value.0 }
}
}
pub fn write_to_event_log(event_source: HANDLE, event_id: u32, level: Level, message: &str) {
let event_type = match level { let event_type = match level {
Level::ERROR => EVENTLOG_ERROR_TYPE, Level::ERROR => EVENTLOG_ERROR_TYPE,
Level::WARN => EVENTLOG_WARNING_TYPE, Level::WARN => EVENTLOG_WARNING_TYPE,
Level::INFO | Level::DEBUG | Level::TRACE => EVENTLOG_INFORMATION_TYPE, Level::INFO | Level::DEBUG | Level::TRACE => EVENTLOG_INFORMATION_TYPE,
}; };
let Ok(message_cstr) = CString::new(message) else { let message = HSTRING::from(message);
eprintln!("failed to create CString from message: {message}"); if let Err(e) = unsafe {
return; ReportEventW(
};
let result = unsafe {
ReportEventA(
event_source, event_source,
event_type, event_type,
0, 0,
event_id as DWORD, event_id,
std::ptr::null_mut(), None,
1,
0, 0,
&mut message_cstr.as_ptr(), Some(&[PCWSTR(message.as_ptr())]),
std::ptr::null_mut(), None,
) )
} {
eprintln!("Failed to write to event log: {:?}", e);
}; };
if result == 0 {
eprintln!("Failed to write to event log");
}
unsafe {
DeregisterEventSource(event_source);
}
} }
pub struct EventLogLayer { pub struct EventLogLayer {
log_name: String, event_source: EventSourceHandle,
default_id: Option<u32>
}
impl Drop for EventLogLayer {
fn drop(&mut self) {
let _ = unsafe { DeregisterEventSource(self.event_source.into()) };
}
} }
impl EventLogLayer { impl EventLogLayer {
#[must_use] pub fn new(log_name: &str) -> Result<Self, windows_result::Error> {
pub const fn new(log_name: String) -> Self { Self::new_with_default_id(log_name, None)
Self { log_name } }
pub fn new_with_default_id(log_name: &str, default_id: Option<u32>) -> Result<Self, windows_result::Error> {
let log_name = HSTRING::from(log_name);
let Ok(event_source) = (unsafe {
RegisterEventSourceW(
None,
PCWSTR(log_name.as_ptr())
)
}) else {
return Err(windows_result::Error::from_win32());
};
Ok(Self { event_source: event_source.into(), default_id })
} }
} }
impl<S> Layer<S> for EventLogLayer impl<S> Layer<S> for EventLogLayer
@ -74,11 +89,12 @@ where
let metadata = event.metadata(); let metadata = event.metadata();
let mut visitor = EventVisitor { let mut visitor = EventVisitor {
event_source: self.event_source.into(),
default_id: self.default_id,
id: None, id: None,
message: None, message: None,
parents: None, parents: None,
log_level: *metadata.level(), log_level: *metadata.level(),
log_name: &self.log_name,
fields: HashMap::new(), fields: HashMap::new(),
}; };
@ -113,50 +129,44 @@ where
} }
#[derive(Debug)] #[derive(Debug)]
struct EventVisitor<'a> { struct EventVisitor {
event_source: HANDLE,
default_id: Option<u32>,
id: Option<u32>, id: Option<u32>,
log_level: Level, log_level: Level,
message: Option<String>, message: Option<String>,
parents: Option<String>, parents: Option<String>,
fields: HashMap<String, String>, fields: HashMap<String, String>,
log_name: &'a str,
} }
impl<'a> EventVisitor<'a> { impl EventVisitor {
fn log(&self) { fn log(&self) {
let id: u32 = self.id.unwrap_or(match self.log_level { let id: u32 = self.id.unwrap_or(self.default_id.unwrap_or_default());
Level::TRACE => 0,
Level::DEBUG => 1,
Level::INFO => 2,
Level::WARN => 3,
Level::ERROR => 4,
});
let mut msg = format!("ID: {id}\n\n"); let mut msg = String::new();
if let Some(m) = &self.message {
msg.push_str(&format!("{m}\n\n"));
}
if let Some(m) = &self.parents { if let Some(m) = &self.parents {
msg.push_str(&format!("source: {m}\n")); 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| { self.fields.iter().for_each(|i| {
msg.push_str(&format!("{}: {:?}\n", i.0, i.1.replace(r"\\", r"\"))); msg.push_str(&format!("{}: {:?}\n", i.0, i.1.replace(r"\\", r"\")));
}); });
write_to_event_log(id, self.log_level, &msg, self.log_name); write_to_event_log(self.event_source, id, self.log_level, &msg);
} }
} }
impl<'a> Visit for EventVisitor<'a> { impl Visit for EventVisitor {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
fn record_u64(&mut self, field: &tracing::field::Field, value: u64) { fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
if field.name().to_lowercase() == "id" && value <= u32::MAX.into() { if field.name().to_lowercase() == "id" && value <= u32::MAX.into() {
self.id = Some(value as u32); self.id = Some(value as u32);
} else { } else {
self.fields self.record_debug(field, &value);
.insert(field.name().to_string(), format!("{value}"));
} }
} }
@ -165,8 +175,7 @@ impl<'a> Visit for EventVisitor<'a> {
if field.name().to_lowercase() == "id" && value >= 0 && value <= u32::MAX.into() { if field.name().to_lowercase() == "id" && value >= 0 && value <= u32::MAX.into() {
self.id = Some(value as u32); self.id = Some(value as u32);
} else { } else {
self.fields self.record_debug(field, &value);
.insert(field.name().to_string(), format!("{value:?}"));
} }
} }
@ -198,4 +207,4 @@ impl<'a> Visit for EventVisitor<'a> {
fn record_str(&mut self, field: &tracing::field::Field, value: &str) { fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
self.record_debug(field, &value); self.record_debug(field, &value);
} }
} }