1#![warn(missing_docs)]
5
6use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
7use widestring::U16CString;
8use windows_sys::Win32::{
9 Foundation::HANDLE,
10 System::EventLog::{
11 DeregisterEventSource, RegisterEventSourceW, ReportEventW, EVENTLOG_ERROR_TYPE,
12 EVENTLOG_INFORMATION_TYPE, EVENTLOG_WARNING_TYPE,
13 },
14};
15use winreg::{enums::*, RegKey};
16
17const MSG_ERROR: u32 = 0xC0000001;
19const MSG_WARNING: u32 = 0x80000002;
20const MSG_INFO: u32 = 0x40000003;
21const MSG_DEBUG: u32 = 0x40000004;
22const MSG_TRACE: u32 = 0x40000005;
23
24const REG_BASEKEY: &str = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application";
25
26#[derive(Debug, thiserror::Error)]
28pub enum Error {
29 #[error("IO error: {0}")]
31 Io(#[from] std::io::Error),
32 #[error("String convertion failed")]
34 StringConvertionFailed,
35 #[error("Set logger failed: {0}")]
37 SetLoggerFailed(#[from] SetLoggerError),
38}
39
40#[cfg(not(feature = "env_logger"))]
41struct Filter {}
42#[cfg(not(feature = "env_logger"))]
43impl Filter {
44 fn enabled(&self, _metadata: &Metadata) -> bool {
45 true
46 }
47 fn matches(&self, _record: &Record) -> bool {
48 true
49 }
50}
51#[cfg(not(feature = "env_logger"))]
52fn make_filter() -> Filter {
53 Filter {}
54}
55
56#[cfg(feature = "env_logger")]
57use env_logger::Logger as Filter;
58#[cfg(feature = "env_logger")]
59fn make_filter() -> Filter {
60 use env_logger::Builder;
61 let mut builder = Builder::from_env("RUST_LOG");
62 builder.build()
63}
64
65struct WinLogger {
66 handle: HANDLE,
67 filter: Filter,
68}
69
70pub fn init(name: &str) -> Result<(), Error> {
73 log::set_boxed_logger(Box::new(WinLogger::new(name)?))?;
74 log::set_max_level(LevelFilter::Trace);
75 Ok(())
76}
77
78pub fn deregister(name: &str) -> Result<(), Error> {
81 let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
82 let cur_ver = hklm.open_subkey(REG_BASEKEY)?;
83 cur_ver.delete_subkey(name).map_err(From::from)
84}
85
86pub fn register(name: &str) -> Result<(), Error> {
92 let current_exe = ::std::env::current_exe()?;
93 let exe_path = current_exe.to_str().ok_or(Error::StringConvertionFailed)?;
94
95 let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
96 let cur_ver = hklm.open_subkey(REG_BASEKEY)?;
97 let (app_key, _) = cur_ver.create_subkey(name)?;
98 app_key.set_value("EventMessageFile", &exe_path)?;
99 app_key.set_value("TypesSupported", &7u32)?;
100 Ok(())
101}
102
103impl WinLogger {
104 pub fn new(name: &str) -> Result<WinLogger, Error> {
105 let name = U16CString::from_str(name).map_err(|_| Error::StringConvertionFailed)?;
106 let handle = unsafe { RegisterEventSourceW(std::ptr::null_mut(), name.as_ptr()) };
107
108 if handle.is_null() {
109 Err(Error::Io(std::io::Error::last_os_error()))
110 } else {
111 Ok(WinLogger {
112 handle,
113 filter: make_filter(),
114 })
115 }
116 }
117}
118
119impl Drop for WinLogger {
120 fn drop(&mut self) {
121 unsafe { DeregisterEventSource(self.handle) };
122 }
123}
124
125unsafe impl Send for WinLogger {}
127unsafe impl Sync for WinLogger {}
128
129impl log::Log for WinLogger {
130 fn enabled(&self, metadata: &Metadata) -> bool {
131 self.filter.enabled(metadata)
132 }
133
134 fn log(&self, record: &Record) {
135 if self.filter.matches(record) {
136 let level = record.level();
137 let (wtype, dweventid) = match level {
138 Level::Error => (EVENTLOG_ERROR_TYPE, MSG_ERROR),
139 Level::Warn => (EVENTLOG_WARNING_TYPE, MSG_WARNING),
140 Level::Info => (EVENTLOG_INFORMATION_TYPE, MSG_INFO),
141 Level::Debug => (EVENTLOG_INFORMATION_TYPE, MSG_DEBUG),
142 Level::Trace => (EVENTLOG_INFORMATION_TYPE, MSG_TRACE),
143 };
144
145 let msg = U16CString::from_str_truncate(format!("{}", record.args()));
146 let msg_ptr = msg.as_ptr();
147
148 unsafe {
149 ReportEventW(
150 self.handle,
151 wtype, 0, dweventid, std::ptr::null_mut(),
155 1,
156 0,
157 &msg_ptr,
158 std::ptr::null_mut(),
159 )
160 };
161 }
162 }
163
164 fn flush(&self) {}
165}