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   basic_sink_frontend.hpp
9  * \author Andrey Semashev
10  * \date   14.07.2009
11  *
12  * The header contains implementation of a base class for sink frontends.
13  */
14 
15 #ifndef BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_
16 #define BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_
17 
18 #include <boost/mpl/bool.hpp>
19 #include <boost/log/detail/config.hpp>
20 #include <boost/log/detail/code_conversion.hpp>
21 #include <boost/log/detail/attachable_sstream_buf.hpp>
22 #include <boost/log/detail/fake_mutex.hpp>
23 #include <boost/log/core/record_view.hpp>
24 #include <boost/log/sinks/sink.hpp>
25 #include <boost/log/sinks/frontend_requirements.hpp>
26 #include <boost/log/expressions/filter.hpp>
27 #include <boost/log/expressions/formatter.hpp>
28 #if !defined(BOOST_LOG_NO_THREADS)
29 #include <boost/memory_order.hpp>
30 #include <boost/atomic/atomic.hpp>
31 #include <boost/thread/exceptions.hpp>
32 #include <boost/thread/tss.hpp>
33 #include <boost/log/detail/locks.hpp>
34 #include <boost/log/detail/light_rw_mutex.hpp>
35 #endif // !defined(BOOST_LOG_NO_THREADS)
36 #include <boost/log/detail/header.hpp>
37 
38 #ifdef BOOST_HAS_PRAGMA_ONCE
39 #pragma once
40 #endif
41 
42 namespace boost {
43 
44 BOOST_LOG_OPEN_NAMESPACE
45 
46 namespace sinks {
47 
48 //! A base class for a logging sink frontend
49 class BOOST_LOG_NO_VTABLE basic_sink_frontend :
50     public sink
51 {
52     //! Base type
53     typedef sink base_type;
54 
55 public:
56     //! An exception handler type
57     typedef base_type::exception_handler_type exception_handler_type;
58 
59 #if !defined(BOOST_LOG_NO_THREADS)
60 protected:
61     //! Mutex type
62     typedef boost::log::aux::light_rw_mutex mutex_type;
63 
64 private:
65     //! Synchronization mutex
66     mutable mutex_type m_Mutex;
67 #endif
68 
69 private:
70     //! Filter
71     filter m_Filter;
72     //! Exception handler
73     exception_handler_type m_ExceptionHandler;
74 
75 public:
76     /*!
77      * \brief Initializing constructor
78      *
79      * \param cross_thread The flag indicates whether the sink passes log records between different threads
80      */
basic_sink_frontend(bool cross_thread)81     explicit basic_sink_frontend(bool cross_thread) : sink(cross_thread)
82     {
83     }
84 
85     /*!
86      * The method sets sink-specific filter functional object
87      */
88     template< typename FunT >
set_filter(FunT const & filter)89     void set_filter(FunT const& filter)
90     {
91         BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);)
92         m_Filter = filter;
93     }
94     /*!
95      * The method resets the filter
96      */
reset_filter()97     void reset_filter()
98     {
99         BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);)
100         m_Filter.reset();
101     }
102 
103     /*!
104      * The method sets an exception handler function
105      */
106     template< typename FunT >
set_exception_handler(FunT const & handler)107     void set_exception_handler(FunT const& handler)
108     {
109         BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);)
110         m_ExceptionHandler = handler;
111     }
112 
113     /*!
114      * The method resets the exception handler function
115      */
reset_exception_handler()116     void reset_exception_handler()
117     {
118         BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);)
119         m_ExceptionHandler.clear();
120     }
121 
122     /*!
123      * The method returns \c true if no filter is set or the attribute values pass the filter
124      *
125      * \param attrs A set of attribute values of a logging record
126      */
will_consume(attribute_value_set const & attrs)127     bool will_consume(attribute_value_set const& attrs) BOOST_OVERRIDE
128     {
129         BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);)
130         try
131         {
132             return m_Filter(attrs);
133         }
134 #if !defined(BOOST_LOG_NO_THREADS)
135         catch (thread_interrupted&)
136         {
137             throw;
138         }
139 #endif
140         catch (...)
141         {
142             if (m_ExceptionHandler.empty())
143                 throw;
144             m_ExceptionHandler();
145             return false;
146         }
147     }
148 
149 protected:
150 #if !defined(BOOST_LOG_NO_THREADS)
151     //! Returns reference to the frontend mutex
frontend_mutex() const152     mutex_type& frontend_mutex() const { return m_Mutex; }
153 #endif
154 
155     //! Returns reference to the exception handler
exception_handler()156     exception_handler_type& exception_handler() { return m_ExceptionHandler; }
157     //! Returns reference to the exception handler
exception_handler() const158     exception_handler_type const& exception_handler() const { return m_ExceptionHandler; }
159 
160     //! Feeds log record to the backend
161     template< typename BackendMutexT, typename BackendT >
feed_record(record_view const & rec,BackendMutexT & backend_mutex,BackendT & backend)162     void feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend)
163     {
164         try
165         {
166             BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);)
167             backend.consume(rec);
168         }
169 #if !defined(BOOST_LOG_NO_THREADS)
170         catch (thread_interrupted&)
171         {
172             throw;
173         }
174 #endif
175         catch (...)
176         {
177             BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);)
178             if (m_ExceptionHandler.empty())
179                 throw;
180             m_ExceptionHandler();
181         }
182     }
183 
184     //! Attempts to feeds log record to the backend, does not block if \a backend_mutex is locked
185     template< typename BackendMutexT, typename BackendT >
try_feed_record(record_view const & rec,BackendMutexT & backend_mutex,BackendT & backend)186     bool try_feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend)
187     {
188 #if !defined(BOOST_LOG_NO_THREADS)
189         try
190         {
191             if (!backend_mutex.try_lock())
192                 return false;
193         }
194         catch (thread_interrupted&)
195         {
196             throw;
197         }
198         catch (...)
199         {
200             boost::log::aux::shared_lock_guard< mutex_type > frontend_lock(this->frontend_mutex());
201             if (this->exception_handler().empty())
202                 throw;
203             this->exception_handler()();
204             return false;
205         }
206 
207         boost::log::aux::exclusive_auto_unlocker< BackendMutexT > unlocker(backend_mutex);
208 #endif
209         // No need to lock anything in the feed_record method
210         boost::log::aux::fake_mutex m;
211         feed_record(rec, m, backend);
212         return true;
213     }
214 
215     //! Flushes record buffers in the backend, if one supports it
216     template< typename BackendMutexT, typename BackendT >
flush_backend(BackendMutexT & backend_mutex,BackendT & backend)217     void flush_backend(BackendMutexT& backend_mutex, BackendT& backend)
218     {
219         typedef typename BackendT::frontend_requirements frontend_requirements;
220         flush_backend_impl(backend_mutex, backend,
221             typename has_requirement< frontend_requirements, flushing >::type());
222     }
223 
224 private:
225     //! Flushes record buffers in the backend (the actual implementation)
226     template< typename BackendMutexT, typename BackendT >
flush_backend_impl(BackendMutexT & backend_mutex,BackendT & backend,mpl::true_)227     void flush_backend_impl(BackendMutexT& backend_mutex, BackendT& backend, mpl::true_)
228     {
229         try
230         {
231             BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);)
232             backend.flush();
233         }
234 #if !defined(BOOST_LOG_NO_THREADS)
235         catch (thread_interrupted&)
236         {
237             throw;
238         }
239 #endif
240         catch (...)
241         {
242             BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);)
243             if (m_ExceptionHandler.empty())
244                 throw;
245             m_ExceptionHandler();
246         }
247     }
248     //! Flushes record buffers in the backend (stub for backends that don't support flushing)
249     template< typename BackendMutexT, typename BackendT >
flush_backend_impl(BackendMutexT &,BackendT &,mpl::false_)250     void flush_backend_impl(BackendMutexT&, BackendT&, mpl::false_)
251     {
252     }
253 };
254 
255 //! A base class for a logging sink frontend with formatting support
256 template< typename CharT >
257 class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend :
258     public basic_sink_frontend
259 {
260     typedef basic_sink_frontend base_type;
261 
262 public:
263     //! Character type
264     typedef CharT char_type;
265     //! Formatted string type
266     typedef std::basic_string< char_type > string_type;
267 
268     //! Formatter function object type
269     typedef basic_formatter< char_type > formatter_type;
270     //! Output stream type
271     typedef typename formatter_type::stream_type stream_type;
272 
273 #if !defined(BOOST_LOG_NO_THREADS)
274 protected:
275     //! Mutex type
276     typedef typename base_type::mutex_type mutex_type;
277 #endif
278 
279 private:
280     struct formatting_context
281     {
282         class cleanup_guard
283         {
284         private:
285             formatting_context& m_context;
286 
287         public:
cleanup_guard(formatting_context & ctx)288             explicit cleanup_guard(formatting_context& ctx) BOOST_NOEXCEPT : m_context(ctx)
289             {
290             }
291 
~cleanup_guard()292             ~cleanup_guard()
293             {
294                 m_context.m_FormattedRecord.clear();
295                 m_context.m_FormattingStream.rdbuf()->max_size(m_context.m_FormattedRecord.max_size());
296                 m_context.m_FormattingStream.rdbuf()->storage_overflow(false);
297                 m_context.m_FormattingStream.clear();
298             }
299 
300             BOOST_DELETED_FUNCTION(cleanup_guard(cleanup_guard const&))
301             BOOST_DELETED_FUNCTION(cleanup_guard& operator=(cleanup_guard const&))
302         };
303 
304 #if !defined(BOOST_LOG_NO_THREADS)
305         //! Object version
306         const unsigned int m_Version;
307 #endif
308         //! Formatted log record storage
309         string_type m_FormattedRecord;
310         //! Formatting stream
311         stream_type m_FormattingStream;
312         //! Formatter functor
313         formatter_type m_Formatter;
314 
formatting_contextboost::sinks::basic_formatting_sink_frontend::formatting_context315         formatting_context() :
316 #if !defined(BOOST_LOG_NO_THREADS)
317             m_Version(0u),
318 #endif
319             m_FormattingStream(m_FormattedRecord)
320         {
321             m_FormattingStream.exceptions(std::ios_base::badbit | std::ios_base::failbit);
322         }
323 #if !defined(BOOST_LOG_NO_THREADS)
formatting_contextboost::sinks::basic_formatting_sink_frontend::formatting_context324         formatting_context(unsigned int version, std::locale const& loc, formatter_type const& formatter) :
325             m_Version(version),
326             m_FormattingStream(m_FormattedRecord),
327             m_Formatter(formatter)
328         {
329             m_FormattingStream.exceptions(std::ios_base::badbit | std::ios_base::failbit);
330             m_FormattingStream.imbue(loc);
331         }
332 #endif
333     };
334 
335 private:
336 #if !defined(BOOST_LOG_NO_THREADS)
337 
338     //! State version
339     boost::atomic< unsigned int > m_Version;
340 
341     //! Formatter functor
342     formatter_type m_Formatter;
343     //! Locale to perform formatting
344     std::locale m_Locale;
345 
346     //! Formatting state
347     thread_specific_ptr< formatting_context > m_pContext;
348 
349 #else
350 
351     //! Formatting state
352     formatting_context m_Context;
353 
354 #endif // !defined(BOOST_LOG_NO_THREADS)
355 
356 public:
357     /*!
358      * \brief Initializing constructor
359      *
360      * \param cross_thread The flag indicates whether the sink passes log records between different threads
361      */
basic_formatting_sink_frontend(bool cross_thread)362     explicit basic_formatting_sink_frontend(bool cross_thread) :
363         basic_sink_frontend(cross_thread)
364 #if !defined(BOOST_LOG_NO_THREADS)
365         , m_Version(0u)
366 #endif
367     {
368     }
369 
370     /*!
371      * The method sets sink-specific formatter function object
372      */
373     template< typename FunT >
set_formatter(FunT const & formatter)374     void set_formatter(FunT const& formatter)
375     {
376 #if !defined(BOOST_LOG_NO_THREADS)
377         boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex());
378         m_Formatter = formatter;
379         m_Version.opaque_add(1u, boost::memory_order_relaxed);
380 #else
381         m_Context.m_Formatter = formatter;
382 #endif
383     }
384     /*!
385      * The method resets the formatter
386      */
reset_formatter()387     void reset_formatter()
388     {
389 #if !defined(BOOST_LOG_NO_THREADS)
390         boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex());
391         m_Formatter.reset();
392         m_Version.opaque_add(1u, boost::memory_order_relaxed);
393 #else
394         m_Context.m_Formatter.reset();
395 #endif
396     }
397 
398     /*!
399      * The method returns the current locale used for formatting
400      */
getloc() const401     std::locale getloc() const
402     {
403 #if !defined(BOOST_LOG_NO_THREADS)
404         boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex());
405         return m_Locale;
406 #else
407         return m_Context.m_FormattingStream.getloc();
408 #endif
409     }
410     /*!
411      * The method sets the locale used for formatting
412      */
imbue(std::locale const & loc)413     void imbue(std::locale const& loc)
414     {
415 #if !defined(BOOST_LOG_NO_THREADS)
416         boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex());
417         m_Locale = loc;
418         m_Version.opaque_add(1u, boost::memory_order_relaxed);
419 #else
420         m_Context.m_FormattingStream.imbue(loc);
421 #endif
422     }
423 
424 protected:
425     //! Returns reference to the formatter
formatter()426     formatter_type& formatter()
427     {
428 #if !defined(BOOST_LOG_NO_THREADS)
429         return m_Formatter;
430 #else
431         return m_Context.m_Formatter;
432 #endif
433     }
434 
435     //! Feeds log record to the backend
436     template< typename BackendMutexT, typename BackendT >
feed_record(record_view const & rec,BackendMutexT & backend_mutex,BackendT & backend)437     void feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend)
438     {
439         formatting_context* context;
440 
441 #if !defined(BOOST_LOG_NO_THREADS)
442         context = m_pContext.get();
443         if (!context || context->m_Version != m_Version.load(boost::memory_order_relaxed))
444         {
445             {
446                 boost::log::aux::shared_lock_guard< mutex_type > lock(this->frontend_mutex());
447                 context = new formatting_context(m_Version.load(boost::memory_order_relaxed), m_Locale, m_Formatter);
448             }
449             m_pContext.reset(context);
450         }
451 #else
452         context = &m_Context;
453 #endif
454 
455         typename formatting_context::cleanup_guard cleanup(*context);
456 
457         try
458         {
459             // Perform the formatting
460             context->m_Formatter(rec, context->m_FormattingStream);
461             context->m_FormattingStream.flush();
462 
463             // Feed the record
464             BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);)
465             backend.consume(rec, context->m_FormattedRecord);
466         }
467 #if !defined(BOOST_LOG_NO_THREADS)
468         catch (thread_interrupted&)
469         {
470             throw;
471         }
472 #endif
473         catch (...)
474         {
475             BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(this->frontend_mutex());)
476             if (this->exception_handler().empty())
477                 throw;
478             this->exception_handler()();
479         }
480     }
481 
482     //! Attempts to feeds log record to the backend, does not block if \a backend_mutex is locked
483     template< typename BackendMutexT, typename BackendT >
try_feed_record(record_view const & rec,BackendMutexT & backend_mutex,BackendT & backend)484     bool try_feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend)
485     {
486 #if !defined(BOOST_LOG_NO_THREADS)
487         try
488         {
489             if (!backend_mutex.try_lock())
490                 return false;
491         }
492         catch (thread_interrupted&)
493         {
494             throw;
495         }
496         catch (...)
497         {
498             boost::log::aux::shared_lock_guard< mutex_type > frontend_lock(this->frontend_mutex());
499             if (this->exception_handler().empty())
500                 throw;
501             this->exception_handler()();
502             return false;
503         }
504 
505         boost::log::aux::exclusive_auto_unlocker< BackendMutexT > unlocker(backend_mutex);
506 #endif
507         // No need to lock anything in the feed_record method
508         boost::log::aux::fake_mutex m;
509         feed_record(rec, m, backend);
510         return true;
511     }
512 };
513 
514 namespace aux {
515 
516     template<
517         typename BackendT,
518         bool RequiresFormattingV = has_requirement<
519             typename BackendT::frontend_requirements,
520             formatted_records
521         >::value
522     >
523     struct make_sink_frontend_base
524     {
525         typedef basic_sink_frontend type;
526     };
527     template< typename BackendT >
528     struct make_sink_frontend_base< BackendT, true >
529     {
530         typedef basic_formatting_sink_frontend< typename BackendT::char_type > type;
531     };
532 
533 } // namespace aux
534 
535 } // namespace sinks
536 
537 BOOST_LOG_CLOSE_NAMESPACE // namespace log
538 
539 } // namespace boost
540 
541 #include <boost/log/detail/footer.hpp>
542 
543 #endif // BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_
544