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