• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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