Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
638a5ff7f7 | |||
8190e33ca6 | |||
c545290e3c | |||
4eee1a63b6 |
11
Cargo.toml
11
Cargo.toml
@ -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"
|
@ -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\""
|
||||||
|
|
||||||
```
|
```
|
133
src/eventlog.rs
133
src/eventlog.rs
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user