Agora  1.2.0
Agora project
win_eventlog_sink.h
Go to the documentation of this file.
1 // Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
2 // Distributed under the MIT License (http://opensource.org/licenses/MIT)
3 
4 // Writing to Windows Event Log requires the registry entries below to be present, with the following modifications:
5 // 1. <log_name> should be replaced with your log name (e.g. your application name)
6 // 2. <source_name> should be replaced with the specific source name and the key should be duplicated for
7 // each source used in the application
8 //
9 // Since typically modifications of this kind require elevation, it's better to do it as a part of setup procedure.
10 // The snippet below uses mscoree.dll as the message file as it exists on most of the Windows systems anyway and
11 // happens to contain the needed resource.
12 //
13 // You can also specify a custom message file if needed.
14 // Please refer to Event Log functions descriptions in MSDN for more details on custom message files.
15 
16 /*---------------------------------------------------------------------------------------
17 
18 Windows Registry Editor Version 5.00
19 
20 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog<log_name>]
21 
22 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog<log_name><source_name>]
23 "TypesSupported"=dword:00000007
24 "EventMessageFile"=hex(2):25,00,73,00,79,00,73,00,74,00,65,00,6d,00,72,00,6f,\
25  00,6f,00,74,00,25,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,\
26  5c,00,6d,00,73,00,63,00,6f,00,72,00,65,00,65,00,2e,00,64,00,6c,00,6c,00,00,\
27  00
28 
29 -----------------------------------------------------------------------------------------*/
30 
31 #pragma once
32 
34 #include <spdlog/sinks/base_sink.h>
35 
37 #include <winbase.h>
38 
39 #include <mutex>
40 #include <string>
41 #include <vector>
42 
43 namespace spdlog {
44 namespace sinks {
45 
46 namespace win_eventlog {
47 
48 namespace internal {
49 
51 {
52  HLOCAL hlocal_;
53 
55 
56  local_alloc_t(local_alloc_t const &) = delete;
57  local_alloc_t &operator=(local_alloc_t const &) = delete;
58 
60  {
61  if (hlocal_)
62  {
63  LocalFree(hlocal_);
64  }
65  }
66 };
67 
69 struct win32_error : public spdlog_ex
70 {
72  static std::string format(std::string const &user_message, DWORD error_code = GetLastError())
73  {
74  std::string system_message;
75 
76  local_alloc_t format_message_result{};
77  auto format_message_succeeded =
78  ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
79  error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&format_message_result.hlocal_, 0, nullptr);
80 
81  if (format_message_succeeded && format_message_result.hlocal_)
82  {
83  system_message = fmt_lib::format(" ({})", (LPSTR)format_message_result.hlocal_);
84  }
85 
86  return fmt_lib::format("{}: {}{}", user_message, error_code, system_message);
87  }
88 
89  explicit win32_error(std::string const &func_name, DWORD error = GetLastError())
90  : spdlog_ex(format(func_name, error))
91  {}
92 };
93 
95 struct sid_t
96 {
97  std::vector<char> buffer_;
98 
99 public:
100  sid_t() {}
101 
103  static sid_t duplicate_sid(PSID psid)
104  {
105  if (!::IsValidSid(psid))
106  {
107  throw_spdlog_ex("sid_t::sid_t(): invalid SID received");
108  }
109 
110  auto const sid_length{::GetLengthSid(psid)};
111 
112  sid_t result;
113  result.buffer_.resize(sid_length);
114  if (!::CopySid(sid_length, (PSID)result.as_sid(), psid))
115  {
116  SPDLOG_THROW(win32_error("CopySid"));
117  }
118 
119  return result;
120  }
121 
123  SID *as_sid() const
124  {
125  return buffer_.empty() ? nullptr : (SID *)buffer_.data();
126  }
127 
130  {
131  /* create and init RAII holder for process token */
132  struct process_token_t
133  {
134  HANDLE token_handle_ = INVALID_HANDLE_VALUE;
135  explicit process_token_t(HANDLE process)
136  {
137  if (!::OpenProcessToken(process, TOKEN_QUERY, &token_handle_))
138  {
139  SPDLOG_THROW(win32_error("OpenProcessToken"));
140  }
141  }
142 
143  ~process_token_t()
144  {
145  ::CloseHandle(token_handle_);
146  }
147 
148  } current_process_token(::GetCurrentProcess()); // GetCurrentProcess returns pseudohandle, no leak here!
149 
150  // Get the required size, this is expected to fail with ERROR_INSUFFICIENT_BUFFER and return the token size
151  DWORD tusize = 0;
152  if (::GetTokenInformation(current_process_token.token_handle_, TokenUser, NULL, 0, &tusize))
153  {
154  SPDLOG_THROW(win32_error("GetTokenInformation should fail"));
155  }
156 
157  // get user token
158  std::vector<unsigned char> buffer(static_cast<size_t>(tusize));
159  if (!::GetTokenInformation(current_process_token.token_handle_, TokenUser, (LPVOID)buffer.data(), tusize, &tusize))
160  {
161  SPDLOG_THROW(win32_error("GetTokenInformation"));
162  }
163 
164  // create a wrapper of the SID data as stored in the user token
165  return sid_t::duplicate_sid(((TOKEN_USER *)buffer.data())->User.Sid);
166  }
167 };
168 
169 struct eventlog
170 {
171  static WORD get_event_type(details::log_msg const &msg)
172  {
173  switch (msg.level)
174  {
175  case level::trace:
176  case level::debug:
177  return EVENTLOG_SUCCESS;
178 
179  case level::info:
180  return EVENTLOG_INFORMATION_TYPE;
181 
182  case level::warn:
183  return EVENTLOG_WARNING_TYPE;
184 
185  case level::err:
186  case level::critical:
187  case level::off:
188  return EVENTLOG_ERROR_TYPE;
189 
190  default:
191  return EVENTLOG_INFORMATION_TYPE;
192  }
193  }
194 
195  static WORD get_event_category(details::log_msg const &msg)
196  {
197  return (WORD)msg.level;
198  }
199 };
200 
201 } // namespace internal
202 
203 /*
204  * Windows Event Log sink
205  */
206 template<typename Mutex>
207 class win_eventlog_sink : public base_sink<Mutex>
208 {
209 private:
210  HANDLE hEventLog_{NULL};
212  std::string source_;
213  WORD event_id_;
214 
216  {
217  if (!hEventLog_)
218  {
219  hEventLog_ = ::RegisterEventSourceA(nullptr, source_.c_str());
220  if (!hEventLog_ || hEventLog_ == (HANDLE)ERROR_ACCESS_DENIED)
221  {
222  SPDLOG_THROW(internal::win32_error("RegisterEventSource"));
223  }
224  }
225 
226  return hEventLog_;
227  }
228 
229 protected:
230  void sink_it_(const details::log_msg &msg) override
231  {
232  using namespace internal;
233 
234  bool succeeded;
235  memory_buf_t formatted;
236  base_sink<Mutex>::formatter_->format(msg, formatted);
237  formatted.push_back('\0');
238 
239 #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
240  wmemory_buf_t buf;
241  details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), buf);
242 
243  LPCWSTR lp_wstr = buf.data();
244  succeeded = ::ReportEventW(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
245  current_user_sid_.as_sid(), 1, 0, &lp_wstr, nullptr);
246 #else
247  LPCSTR lp_str = formatted.data();
248  succeeded = ::ReportEventA(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
249  current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr);
250 #endif
251 
252  if (!succeeded)
253  {
254  SPDLOG_THROW(win32_error("ReportEvent"));
255  }
256  }
257 
258  void flush_() override {}
259 
260 public:
261  win_eventlog_sink(std::string const &source, WORD event_id = 1000 /* according to mscoree.dll */)
262  : source_(source)
263  , event_id_(event_id)
264  {
265  try
266  {
268  }
269  catch (...)
270  {
271  // get_current_user_sid() is unlikely to fail and if it does, we can still proceed without
272  // current_user_sid but in the event log the record will have no user name
273  }
274  }
275 
277  {
278  if (hEventLog_)
279  DeregisterEventSource(hEventLog_);
280  }
281 };
282 
283 } // namespace win_eventlog
284 
287 
288 } // namespace sinks
289 } // namespace spdlog
spdlog::sinks::win_eventlog::internal::eventlog
Definition: win_eventlog_sink.h:169
windows_include.h
spdlog::sinks::win_eventlog::win_eventlog_sink::sink_it_
void sink_it_(const details::log_msg &msg) override
Definition: win_eventlog_sink.h:230
SPDLOG_NOEXCEPT
#define SPDLOG_NOEXCEPT
Definition: common.h:64
spdlog::level::trace
@ trace
Definition: common.h:213
fmt::v8::detail::buffer::push_back
void push_back(const T &value)
Definition: core.h:849
spdlog::sinks::win_eventlog::internal::win32_error::win32_error
win32_error(std::string const &func_name, DWORD error=GetLastError())
Definition: win_eventlog_sink.h:89
spdlog::sinks::win_eventlog::internal::local_alloc_t::~local_alloc_t
~local_alloc_t() SPDLOG_NOEXCEPT
Definition: win_eventlog_sink.h:59
base_sink.h
spdlog::level::off
@ off
Definition: common.h:219
spdlog::string_view_t
fmt::basic_string_view< char > string_view_t
Definition: common.h:154
spdlog::level::warn
@ warn
Definition: common.h:216
fmt::v8::detail::buffer::size
constexpr auto size() const -> size_t
Definition: core.h:820
spdlog::sinks::win_eventlog::internal::local_alloc_t::hlocal_
HLOCAL hlocal_
Definition: win_eventlog_sink.h:52
spdlog::sinks::win_eventlog::internal::sid_t
Definition: win_eventlog_sink.h:95
spdlog::sinks::win_eventlog::win_eventlog_sink::flush_
void flush_() override
Definition: win_eventlog_sink.h:258
spdlog::level::info
@ info
Definition: common.h:215
spdlog::sinks::win_eventlog::internal::sid_t::buffer_
std::vector< char > buffer_
Definition: win_eventlog_sink.h:97
spdlog::sinks::win_eventlog::win_eventlog_sink
Definition: win_eventlog_sink.h:207
spdlog::level::debug
@ debug
Definition: common.h:214
spdlog::level::err
@ err
Definition: common.h:217
spdlog::sinks::win_eventlog::win_eventlog_sink::current_user_sid_
internal::sid_t current_user_sid_
Definition: win_eventlog_sink.h:211
null_mutex.h
fmt::v8::basic_memory_buffer
Definition: format.h:677
spdlog::sinks::win_eventlog::win_eventlog_sink::~win_eventlog_sink
~win_eventlog_sink()
Definition: win_eventlog_sink.h:276
spdlog
Definition: async.h:25
spdlog::throw_spdlog_ex
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
Definition: common-inl.h:72
spdlog::sinks::base_sink
Definition: base_sink.h:19
spdlog::sinks::win_eventlog::internal::sid_t::duplicate_sid
static sid_t duplicate_sid(PSID psid)
Definition: win_eventlog_sink.h:103
spdlog::sinks::win_eventlog::internal::sid_t::as_sid
SID * as_sid() const
Definition: win_eventlog_sink.h:123
spdlog::details::log_msg::level
level::level_enum level
Definition: log_msg.h:21
spdlog::spdlog_ex
Definition: common.h:276
spdlog::sinks::win_eventlog::internal::local_alloc_t::operator=
local_alloc_t & operator=(local_alloc_t const &)=delete
spdlog::sinks::win_eventlog::win_eventlog_sink::win_eventlog_sink
win_eventlog_sink(std::string const &source, WORD event_id=1000)
Definition: win_eventlog_sink.h:261
fmt::v8::detail::buffer::data
auto data() -> T *
Definition: core.h:826
SPDLOG_THROW
#define SPDLOG_THROW(ex)
Definition: common.h:103
spdlog::sinks::win_eventlog::internal::eventlog::get_event_category
static WORD get_event_category(details::log_msg const &msg)
Definition: win_eventlog_sink.h:195
spdlog::sinks::win_eventlog::internal::sid_t::get_current_user_sid
static sid_t get_current_user_sid()
Definition: win_eventlog_sink.h:129
spdlog::details::log_msg
Definition: log_msg.h:11
spdlog::sinks::win_eventlog::internal::win32_error::format
static std::string format(std::string const &user_message, DWORD error_code=GetLastError())
Definition: win_eventlog_sink.h:72
spdlog::sinks::win_eventlog::internal::local_alloc_t::local_alloc_t
SPDLOG_CONSTEXPR local_alloc_t() SPDLOG_NOEXCEPT
Definition: win_eventlog_sink.h:54
spdlog::sinks::win_eventlog::internal::eventlog::get_event_type
static WORD get_event_type(details::log_msg const &msg)
Definition: win_eventlog_sink.h:171
spdlog::sinks::win_eventlog::win_eventlog_sink::event_id_
WORD event_id_
Definition: win_eventlog_sink.h:213
spdlog::sinks::win_eventlog::win_eventlog_sink::hEventLog_
HANDLE hEventLog_
Definition: win_eventlog_sink.h:210
spdlog::level::critical
@ critical
Definition: common.h:218
spdlog::sinks::win_eventlog::internal::local_alloc_t
Definition: win_eventlog_sink.h:50
spdlog::sinks::win_eventlog::win_eventlog_sink::source_
std::string source_
Definition: win_eventlog_sink.h:212
spdlog::sinks::win_eventlog::internal::sid_t::sid_t
sid_t()
Definition: win_eventlog_sink.h:100
spdlog::sinks::win_eventlog::internal::win32_error
Definition: win_eventlog_sink.h:69
SPDLOG_CONSTEXPR
#define SPDLOG_CONSTEXPR
Definition: common.h:65
fmt::v8::detail::digits::result
result
Definition: format-inl.h:640
spdlog::sinks::win_eventlog::win_eventlog_sink::event_log_handle
HANDLE event_log_handle()
Definition: win_eventlog_sink.h:215
utils::format
std::string format(const T &value)
Definition: utils.h:15