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