1 /*
2 * Copyright Andrey Semashev 2007 - 2015.
3 * Distributed under the Boost Software License, Version 1.0.
4 * (See accompanying file LICENSE_1_0.txt or copy at
5 * http://www.boost.org/LICENSE_1_0.txt)
6 */
7 /*!
8 * \file event_log_backend.cpp
9 * \author Andrey Semashev
10 * \date 07.11.2008
11 *
12 * \brief A logging sink backend that uses Windows NT event log API
13 * for signalling application events.
14 */
15
16 #ifndef BOOST_LOG_WITHOUT_EVENT_LOG
17
18 #include "windows_version.hpp"
19 #include <string>
20 #include <vector>
21 #include <ostream>
22 #include <stdexcept>
23 #include <boost/scoped_array.hpp>
24 #include <boost/log/exceptions.hpp>
25 #include <boost/log/sinks/event_log_backend.hpp>
26 #include <boost/log/sinks/event_log_constants.hpp>
27 #include <boost/log/utility/once_block.hpp>
28 #include <boost/log/detail/cleanup_scope_guard.hpp>
29 #include <boost/log/detail/attachable_sstream_buf.hpp>
30 #include <boost/log/detail/code_conversion.hpp>
31 #include <boost/log/utility/formatting_ostream.hpp>
32 #include "unique_ptr.hpp"
33 #include "event_log_registry.hpp"
34 #include <windows.h>
35 #include <psapi.h>
36 #include "simple_event_log.h"
37 #include <boost/log/detail/header.hpp>
38
39 #ifdef _MSC_VER
40 #pragma comment(lib, "psapi.lib")
41 #pragma comment(lib, "advapi32.lib")
42 #endif
43
44
45 namespace boost {
46
47 BOOST_LOG_OPEN_NAMESPACE
48
49 namespace sinks {
50
51 namespace event_log {
52
53 //! The function constructs log record level from an integer
make_event_type(unsigned short lev)54 BOOST_LOG_API event_type make_event_type(unsigned short lev)
55 {
56 switch (lev)
57 {
58 case success: return success;
59 case warning: return warning;
60 case error: return error;
61 default:
62 BOOST_THROW_EXCEPTION(std::out_of_range("Windows NT event type is out of range"));
63 BOOST_LOG_UNREACHABLE_RETURN(info);
64 case info: return info;
65 }
66 }
67
68 } // namespace event_log
69
70 BOOST_LOG_ANONYMOUS_NAMESPACE {
71
72 #ifdef BOOST_LOG_USE_CHAR
73 //! A simple forwarder to the ReportEvent API
74 inline BOOL report_event(
75 HANDLE hEventLog,
76 WORD wType,
77 WORD wCategory,
78 DWORD dwEventID,
79 PSID lpUserSid,
80 WORD wNumStrings,
81 DWORD dwDataSize,
82 const char** lpStrings,
83 LPVOID lpRawData)
84 {
85 return ReportEventA(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize, lpStrings, lpRawData);
86 }
87 //! A simple forwarder to the GetModuleFileName API
88 inline DWORD get_module_file_name(HMODULE hModule, char* lpFilename, DWORD nSize)
89 {
90 return GetModuleFileNameA(hModule, lpFilename, nSize);
91 }
92 //! A simple forwarder to the RegisterEventSource API
93 inline HANDLE register_event_source(const char* lpUNCServerName, const char* lpSourceName)
94 {
95 return RegisterEventSourceA(lpUNCServerName, lpSourceName);
96 }
97 //! The function completes default source name for the sink backend
98 inline void complete_default_simple_event_log_source_name(std::string& name)
99 {
100 name += " simple event source";
101 }
102 //! The function completes default source name for the sink backend
103 inline void complete_default_event_log_source_name(std::string& name)
104 {
105 name += " event source";
106 }
107 #endif // BOOST_LOG_USE_CHAR
108
109 #ifdef BOOST_LOG_USE_WCHAR_T
110 //! A simple forwarder to the ReportEvent API
111 inline BOOL report_event(
112 HANDLE hEventLog,
113 WORD wType,
114 WORD wCategory,
115 DWORD dwEventID,
116 PSID lpUserSid,
117 WORD wNumStrings,
118 DWORD dwDataSize,
119 const wchar_t** lpStrings,
120 LPVOID lpRawData)
121 {
122 return ReportEventW(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize, lpStrings, lpRawData);
123 }
124 //! A simple forwarder to the GetModuleFileName API
125 inline DWORD get_module_file_name(HMODULE hModule, wchar_t* lpFilename, DWORD nSize)
126 {
127 return GetModuleFileNameW(hModule, lpFilename, nSize);
128 }
129 //! A simple forwarder to the RegisterEventSource API
130 inline HANDLE register_event_source(const wchar_t* lpUNCServerName, const wchar_t* lpSourceName)
131 {
132 return RegisterEventSourceW(lpUNCServerName, lpSourceName);
133 }
134 //! The function completes default source name for the sink backend
135 inline void complete_default_simple_event_log_source_name(std::wstring& name)
136 {
137 name += L" simple event source";
138 }
139 //! The function completes default source name for the sink backend
140 inline void complete_default_event_log_source_name(std::wstring& name)
141 {
142 name += L" event source";
143 }
144 #endif // BOOST_LOG_USE_WCHAR_T
145
146 //! The function finds the handle for the current module
147 void init_self_module_handle(HMODULE& handle)
148 {
149 // Acquire all modules of the current process
150 HANDLE hProcess = GetCurrentProcess();
151 std::vector< HMODULE > modules;
152 DWORD module_count = 1024;
153 do
154 {
155 modules.resize(module_count, HMODULE(0));
156 BOOL res = EnumProcessModules(
157 hProcess,
158 &modules[0],
159 static_cast< DWORD >(modules.size() * sizeof(HMODULE)),
160 &module_count);
161 module_count /= sizeof(HMODULE);
162
163 if (!res)
164 BOOST_LOG_THROW_DESCR(system_error, "Could not enumerate process modules");
165 }
166 while (module_count > modules.size());
167 modules.resize(module_count, HMODULE(0));
168
169 // Now find the current module among them
170 void* p = (void*)&init_self_module_handle;
171 for (std::size_t i = 0, n = modules.size(); i < n; ++i)
172 {
173 MODULEINFO info;
174 if (!GetModuleInformation(hProcess, modules[i], &info, sizeof(info)))
175 BOOST_LOG_THROW_DESCR(system_error, "Could not acquire module information");
176
177 if (info.lpBaseOfDll <= p && (static_cast< unsigned char* >(info.lpBaseOfDll) + info.SizeOfImage) > p)
178 {
179 // Found it
180 handle = modules[i];
181 break;
182 }
183 }
184
185 if (!handle)
186 BOOST_LOG_THROW_DESCR(system_error, "Could not find self module information");
187 }
188
189 //! Retrieves the full name of the current module (be that dll or exe)
190 template< typename CharT >
191 std::basic_string< CharT > get_current_module_name()
192 {
193 static HMODULE hSelfModule = 0;
194
195 BOOST_LOG_ONCE_BLOCK()
196 {
197 init_self_module_handle(hSelfModule);
198 }
199
200 // Get the module file name
201 CharT buf[MAX_PATH];
202 DWORD size = get_module_file_name(hSelfModule, buf, sizeof(buf) / sizeof(*buf));
203 if (size == 0)
204 BOOST_LOG_THROW_DESCR(system_error, "Could not get module file name");
205
206 return std::basic_string< CharT >(buf, buf + size);
207 }
208
209 } // namespace
210
211 //////////////////////////////////////////////////////////////////////////
212 // Simple event log backend implementation
213 //////////////////////////////////////////////////////////////////////////
214 //! Sink backend implementation
215 template< typename CharT >
216 struct basic_simple_event_log_backend< CharT >::implementation
217 {
218 //! A handle for the registered event provider
219 HANDLE m_SourceHandle;
220 //! A level mapping functor
221 event_type_mapper_type m_LevelMapper;
222
implementationboost::sinks::basic_simple_event_log_backend::implementation223 implementation() : m_SourceHandle(0)
224 {
225 }
226 };
227
228 //! Default constructor. Registers event source Boost.Log <Boost version> in the Application log.
229 template< typename CharT >
basic_simple_event_log_backend()230 BOOST_LOG_API basic_simple_event_log_backend< CharT >::basic_simple_event_log_backend()
231 {
232 construct(log::aux::empty_arg_list());
233 }
234
235 //! Destructor
236 template< typename CharT >
~basic_simple_event_log_backend()237 BOOST_LOG_API basic_simple_event_log_backend< CharT >::~basic_simple_event_log_backend()
238 {
239 DeregisterEventSource(m_pImpl->m_SourceHandle);
240 delete m_pImpl;
241 }
242
243 //! Constructs backend implementation
244 template< typename CharT >
construct(string_type const & target,string_type const & log_name,string_type const & source_name,event_log::registration_mode reg_mode)245 BOOST_LOG_API void basic_simple_event_log_backend< CharT >::construct(
246 string_type const& target, string_type const& log_name, string_type const& source_name, event_log::registration_mode reg_mode)
247 {
248 if (reg_mode != event_log::never)
249 {
250 aux::registry_params< char_type > reg_params;
251 reg_params.event_message_file = get_current_module_name< char_type >();
252 reg_params.types_supported = DWORD(
253 EVENTLOG_SUCCESS |
254 EVENTLOG_INFORMATION_TYPE |
255 EVENTLOG_WARNING_TYPE |
256 EVENTLOG_ERROR_TYPE);
257 aux::init_event_log_registry(log_name, source_name, reg_mode == event_log::forced, reg_params);
258 }
259
260 log::aux::unique_ptr< implementation > p(new implementation());
261
262 const char_type* target_unc = NULL;
263 if (!target.empty())
264 target_unc = target.c_str();
265
266 HANDLE hSource = register_event_source(target_unc, source_name.c_str());
267 if (!hSource)
268 BOOST_LOG_THROW_DESCR(system_error, "Could not register event source");
269
270 p->m_SourceHandle = hSource;
271
272 m_pImpl = p.release();
273 }
274
275 //! Returns default log name
276 template< typename CharT >
277 BOOST_LOG_API typename basic_simple_event_log_backend< CharT >::string_type
get_default_log_name()278 basic_simple_event_log_backend< CharT >::get_default_log_name()
279 {
280 return aux::registry_traits< char_type >::make_default_log_name();
281 }
282
283 //! Returns default source name
284 template< typename CharT >
285 BOOST_LOG_API typename basic_simple_event_log_backend< CharT >::string_type
get_default_source_name()286 basic_simple_event_log_backend< CharT >::get_default_source_name()
287 {
288 string_type source_name = aux::registry_traits< char_type >::make_default_source_name();
289 complete_default_simple_event_log_source_name(source_name);
290 return source_name;
291 }
292
293 //! The method installs the function object that maps application severity levels to WinAPI event types
294 template< typename CharT >
set_event_type_mapper(event_type_mapper_type const & mapper)295 BOOST_LOG_API void basic_simple_event_log_backend< CharT >::set_event_type_mapper(event_type_mapper_type const& mapper)
296 {
297 m_pImpl->m_LevelMapper = mapper;
298 }
299
300 //! The method puts the formatted message to the event log
301 template< typename CharT >
consume(record_view const & rec,string_type const & formatted_message)302 BOOST_LOG_API void basic_simple_event_log_backend< CharT >::consume(record_view const& rec, string_type const& formatted_message)
303 {
304 const char_type* message = formatted_message.c_str();
305 event_log::event_type evt_type = event_log::info;
306 if (!m_pImpl->m_LevelMapper.empty())
307 evt_type = m_pImpl->m_LevelMapper(rec);
308
309 DWORD event_id;
310 switch (evt_type)
311 {
312 case event_log::success:
313 event_id = BOOST_LOG_MSG_DEBUG; break;
314 case event_log::warning:
315 event_id = BOOST_LOG_MSG_WARNING; break;
316 case event_log::error:
317 event_id = BOOST_LOG_MSG_ERROR; break;
318 default:
319 event_id = BOOST_LOG_MSG_INFO; break;
320 }
321
322 report_event(
323 m_pImpl->m_SourceHandle, // Event log handle.
324 static_cast< WORD >(evt_type), // Event type.
325 0, // Event category.
326 event_id, // Event identifier.
327 NULL, // No user security identifier.
328 1, // Number of substitution strings.
329 0, // No data.
330 &message, // Pointer to strings.
331 NULL); // No data.
332 }
333
334 //////////////////////////////////////////////////////////////////////////
335 // Customizable event log backend implementation
336 //////////////////////////////////////////////////////////////////////////
337 namespace event_log {
338
339 template< typename CharT >
340 class basic_event_composer< CharT >::insertion_composer
341 {
342 public:
343 //! Function object result type
344 typedef void result_type;
345
346 private:
347 //! The list of insertion composers (in backward order)
348 typedef std::vector< formatter_type > formatters;
349
350 private:
351 //! The insertion string composers
352 formatters m_Formatters;
353
354 public:
355 //! Default constructor
insertion_composer()356 insertion_composer() {}
357 //! Composition operator
operator ()(record_view const & rec,insertion_list & insertions) const358 void operator() (record_view const& rec, insertion_list& insertions) const
359 {
360 std::size_t size = m_Formatters.size();
361 insertions.resize(size);
362 for (std::size_t i = 0; i < size; ++i)
363 {
364 typename formatter_type::stream_type strm(insertions[i]);
365 m_Formatters[i](rec, strm);
366 strm.flush();
367 }
368 }
369 //! Adds a new formatter to the list
add_formatter(formatter_type const & fmt)370 void add_formatter(formatter_type const& fmt)
371 {
372 m_Formatters.push_back(formatter_type(fmt));
373 }
374 };
375
376 //! Default constructor
377 template< typename CharT >
basic_event_composer(event_id_mapper_type const & id_mapper)378 basic_event_composer< CharT >::basic_event_composer(event_id_mapper_type const& id_mapper) :
379 m_EventIDMapper(id_mapper)
380 {
381 }
382 //! Copy constructor
383 template< typename CharT >
basic_event_composer(basic_event_composer const & that)384 basic_event_composer< CharT >::basic_event_composer(basic_event_composer const& that) :
385 m_EventIDMapper(that.m_EventIDMapper),
386 m_EventMap(that.m_EventMap)
387 {
388 }
389 //! Destructor
390 template< typename CharT >
~basic_event_composer()391 basic_event_composer< CharT >::~basic_event_composer()
392 {
393 }
394
395 //! Assignment
396 template< typename CharT >
operator =(basic_event_composer that)397 basic_event_composer< CharT >& basic_event_composer< CharT >::operator= (basic_event_composer that)
398 {
399 swap(that);
400 return *this;
401 }
402 //! Swapping
403 template< typename CharT >
swap(basic_event_composer & that)404 void basic_event_composer< CharT >::swap(basic_event_composer& that)
405 {
406 m_EventIDMapper.swap(that.m_EventIDMapper);
407 m_EventMap.swap(that.m_EventMap);
408 }
409 //! Creates a new entry for a message
410 template< typename CharT >
411 typename basic_event_composer< CharT >::event_map_reference
operator [](event_id id)412 basic_event_composer< CharT >::operator[] (event_id id)
413 {
414 return event_map_reference(id, *this);
415 }
416 //! Creates a new entry for a message
417 template< typename CharT >
418 typename basic_event_composer< CharT >::event_map_reference
operator [](int id)419 basic_event_composer< CharT >::operator[] (int id)
420 {
421 return event_map_reference(make_event_id(id), *this);
422 }
423
424 //! Event composition operator
425 template< typename CharT >
operator ()(record_view const & rec,insertion_list & insertions) const426 event_id basic_event_composer< CharT >::operator() (record_view const& rec, insertion_list& insertions) const
427 {
428 event_id id = m_EventIDMapper(rec);
429 typename event_map::const_iterator it = m_EventMap.find(id);
430 if (it != m_EventMap.end())
431 it->second(rec, insertions);
432 return id;
433 }
434
435 //! Adds a formatter to the insertion composers list
436 template< typename CharT >
437 typename basic_event_composer< CharT >::insertion_composer*
add_formatter(event_id id,insertion_composer * composer,formatter_type const & fmt)438 basic_event_composer< CharT >::add_formatter(event_id id, insertion_composer* composer, formatter_type const& fmt)
439 {
440 if (!composer)
441 composer = &m_EventMap[id];
442 composer->add_formatter(fmt);
443 return composer;
444 }
445
446 #ifdef BOOST_LOG_USE_CHAR
447 template class BOOST_LOG_API basic_event_composer< char >;
448 #endif
449 #ifdef BOOST_LOG_USE_WCHAR_T
450 template class BOOST_LOG_API basic_event_composer< wchar_t >;
451 #endif
452
453 } // namespace event_log
454
455
456 //! Backend implementation
457 template< typename CharT >
458 struct basic_event_log_backend< CharT >::implementation
459 {
460 // NOTE: This order of data members is critical for MSVC 9.0 in debug mode,
461 // as it ICEs if boost::functions are not the first members. Doh!
462
463 //! An event category mapper
464 event_category_mapper_type m_CategoryMapper;
465 //! A level mapping functor
466 event_type_mapper_type m_LevelMapper;
467
468 //! A handle for the registered event provider
469 HANDLE m_SourceHandle;
470 //! A functor that composes an event
471 event_composer_type m_EventComposer;
472 //! An array of formatted insertions
473 insertion_list m_Insertions;
474
implementationboost::sinks::basic_event_log_backend::implementation475 implementation() : m_SourceHandle(0)
476 {
477 }
478 };
479
480 //! Destructor
481 template< typename CharT >
~basic_event_log_backend()482 BOOST_LOG_API basic_event_log_backend< CharT >::~basic_event_log_backend()
483 {
484 DeregisterEventSource(m_pImpl->m_SourceHandle);
485 delete m_pImpl;
486 }
487
488 //! Constructs backend implementation
489 template< typename CharT >
construct(filesystem::path const & message_file_name,string_type const & target,string_type const & log_name,string_type const & source_name,event_log::registration_mode reg_mode)490 BOOST_LOG_API void basic_event_log_backend< CharT >::construct(
491 filesystem::path const& message_file_name,
492 string_type const& target,
493 string_type const& log_name,
494 string_type const& source_name,
495 event_log::registration_mode reg_mode)
496 {
497 if (reg_mode != event_log::never)
498 {
499 if (message_file_name.empty())
500 BOOST_THROW_EXCEPTION(std::invalid_argument("Message file name not specified."));
501 aux::registry_params< char_type > reg_params;
502 string_type file_name;
503 log::aux::code_convert(message_file_name.string(), file_name);
504 reg_params.event_message_file = file_name;
505 reg_params.types_supported = DWORD(
506 EVENTLOG_SUCCESS |
507 EVENTLOG_INFORMATION_TYPE |
508 EVENTLOG_WARNING_TYPE |
509 EVENTLOG_ERROR_TYPE);
510 aux::init_event_log_registry(log_name, source_name, reg_mode == event_log::forced, reg_params);
511 }
512
513 log::aux::unique_ptr< implementation > p(new implementation());
514
515 const char_type* target_unc = NULL;
516 if (!target.empty())
517 target_unc = target.c_str();
518
519 HANDLE hSource = register_event_source(target_unc, source_name.c_str());
520 if (!hSource)
521 BOOST_LOG_THROW_DESCR(system_error, "Could not register event source");
522
523 p->m_SourceHandle = hSource;
524
525 m_pImpl = p.release();
526 }
527
528 //! The method puts the formatted message to the event log
529 template< typename CharT >
consume(record_view const & rec)530 BOOST_LOG_API void basic_event_log_backend< CharT >::consume(record_view const& rec)
531 {
532 if (!m_pImpl->m_EventComposer.empty())
533 {
534 log::aux::cleanup_guard< insertion_list > cleaner(m_pImpl->m_Insertions);
535
536 // Get event ID and construct insertions
537 DWORD id = m_pImpl->m_EventComposer(rec, m_pImpl->m_Insertions);
538 WORD string_count = static_cast< WORD >(m_pImpl->m_Insertions.size());
539 scoped_array< const char_type* > strings(new const char_type*[string_count]);
540 for (WORD i = 0; i < string_count; ++i)
541 strings[i] = m_pImpl->m_Insertions[i].c_str();
542
543 // Get event type
544 WORD event_type = EVENTLOG_INFORMATION_TYPE;
545 if (!m_pImpl->m_LevelMapper.empty())
546 event_type = static_cast< WORD >(m_pImpl->m_LevelMapper(rec));
547
548 WORD event_category = 0;
549 if (!m_pImpl->m_CategoryMapper.empty())
550 event_category = static_cast< WORD >(m_pImpl->m_CategoryMapper(rec));
551
552 report_event(
553 m_pImpl->m_SourceHandle, // Event log handle.
554 event_type, // Event type.
555 event_category, // Event category.
556 id, // Event identifier.
557 NULL, // No user security identifier.
558 string_count, // Number of substitution strings.
559 0, // No data.
560 strings.get(), // Pointer to strings.
561 NULL); // No data.
562 }
563 }
564
565 //! Returns default log name
566 template< typename CharT >
567 BOOST_LOG_API typename basic_event_log_backend< CharT >::string_type
get_default_log_name()568 basic_event_log_backend< CharT >::get_default_log_name()
569 {
570 return aux::registry_traits< char_type >::make_default_log_name();
571 }
572
573 //! Returns default source name
574 template< typename CharT >
575 BOOST_LOG_API typename basic_event_log_backend< CharT >::string_type
get_default_source_name()576 basic_event_log_backend< CharT >::get_default_source_name()
577 {
578 string_type source_name = aux::registry_traits< char_type >::make_default_source_name();
579 complete_default_event_log_source_name(source_name);
580 return source_name;
581 }
582
583 //! The method installs the function object that maps application severity levels to WinAPI event types
584 template< typename CharT >
set_event_type_mapper(event_type_mapper_type const & mapper)585 BOOST_LOG_API void basic_event_log_backend< CharT >::set_event_type_mapper(event_type_mapper_type const& mapper)
586 {
587 m_pImpl->m_LevelMapper = mapper;
588 }
589
590 //! The method installs the function object that extracts event category from attribute values
591 template< typename CharT >
set_event_category_mapper(event_category_mapper_type const & mapper)592 BOOST_LOG_API void basic_event_log_backend< CharT >::set_event_category_mapper(event_category_mapper_type const& mapper)
593 {
594 m_pImpl->m_CategoryMapper = mapper;
595 }
596
597 /*!
598 * The method installs the function object that extracts event identifier from the attributes and creates
599 * insertion strings that will replace placeholders in the event message.
600 */
601 template< typename CharT >
set_event_composer(event_composer_type const & composer)602 BOOST_LOG_API void basic_event_log_backend< CharT >::set_event_composer(event_composer_type const& composer)
603 {
604 m_pImpl->m_EventComposer = composer;
605 }
606
607
608 #ifdef BOOST_LOG_USE_CHAR
609 template class basic_simple_event_log_backend< char >;
610 template class basic_event_log_backend< char >;
611 #endif
612 #ifdef BOOST_LOG_USE_WCHAR_T
613 template class basic_simple_event_log_backend< wchar_t >;
614 template class basic_event_log_backend< wchar_t >;
615 #endif
616
617 } // namespace sinks
618
619 BOOST_LOG_CLOSE_NAMESPACE // namespace log
620
621 } // namespace boost
622
623 #include <boost/log/detail/footer.hpp>
624
625 #endif // !defined(BOOST_LOG_WITHOUT_EVENT_LOG)
626