#![cfg(windows)]
use std::ffi::{OsStr, c_void};
use std::os::windows::ffi::OsStrExt;
use std::sync::atomic::{AtomicPtr, Ordering};
use tracing::field::{Field, Visit};
use tracing::{Event, Level, Subscriber};
use tracing_subscriber::layer::{Context, Layer};
use tracing_subscriber::registry::LookupSpan;
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::EventLog::{
DeregisterEventSource, EVENTLOG_ERROR_TYPE, EVENTLOG_INFORMATION_TYPE, EVENTLOG_WARNING_TYPE,
RegisterEventSourceW, ReportEventW,
};
pub const EVENT_SOURCE_NAME: &str = "cfgd";
static EVENT_SOURCE: AtomicPtr<c_void> = AtomicPtr::new(std::ptr::null_mut());
fn to_wide(s: &str) -> Vec<u16> {
OsStr::new(s)
.encode_wide()
.chain(std::iter::once(0))
.collect()
}
fn register_source() -> HANDLE {
let existing = EVENT_SOURCE.load(Ordering::Acquire);
if !existing.is_null() {
return existing;
}
let source = to_wide(EVENT_SOURCE_NAME);
let handle = unsafe { RegisterEventSourceW(std::ptr::null(), source.as_ptr()) };
if !handle.is_null() {
EVENT_SOURCE.store(handle, Ordering::Release);
}
handle
}
pub fn deregister_source() {
let handle = EVENT_SOURCE.swap(std::ptr::null_mut(), Ordering::AcqRel);
if !handle.is_null() {
unsafe {
DeregisterEventSource(handle);
}
}
}
fn level_to_event_type(level: &Level) -> u16 {
match *level {
Level::ERROR => EVENTLOG_ERROR_TYPE,
Level::WARN => EVENTLOG_WARNING_TYPE,
_ => EVENTLOG_INFORMATION_TYPE,
}
}
struct MessageBuilder {
out: String,
}
impl MessageBuilder {
fn new() -> Self {
Self { out: String::new() }
}
fn push(&mut self, name: &str, value: &dyn std::fmt::Debug) {
if !self.out.is_empty() {
self.out.push(' ');
}
if name == "message" {
let _ = std::fmt::write(&mut self.out, format_args!("{:?}", value));
} else {
let _ = std::fmt::write(&mut self.out, format_args!("{}={:?}", name, value));
}
}
}
impl Visit for MessageBuilder {
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
self.push(field.name(), value);
}
fn record_str(&mut self, field: &Field, value: &str) {
self.push(field.name(), &value);
}
fn record_i64(&mut self, field: &Field, value: i64) {
self.push(field.name(), &value);
}
fn record_u64(&mut self, field: &Field, value: u64) {
self.push(field.name(), &value);
}
fn record_bool(&mut self, field: &Field, value: bool) {
self.push(field.name(), &value);
}
}
#[derive(Default)]
pub struct EventLogLayer;
impl<S> Layer<S> for EventLogLayer
where
S: Subscriber + for<'lookup> LookupSpan<'lookup>,
{
fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
let handle = register_source();
if handle.is_null() {
return;
}
let mut builder = MessageBuilder::new();
event.record(&mut builder);
let target = event.metadata().target();
let composed = if target.is_empty() {
builder.out
} else {
format!("[{target}] {}", builder.out)
};
let wide = to_wide(&composed);
let strings: [*const u16; 1] = [wide.as_ptr()];
unsafe {
ReportEventW(
handle,
level_to_event_type(event.metadata().level()),
0, 1, std::ptr::null_mut(),
strings.len() as u16,
0,
strings.as_ptr(),
std::ptr::null(),
);
}
}
}