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