Chris@16: /* Chris@101: * Copyright Andrey Semashev 2007 - 2015. Chris@16: * Distributed under the Boost Software License, Version 1.0. Chris@16: * (See accompanying file LICENSE_1_0.txt or copy at Chris@16: * http://www.boost.org/LICENSE_1_0.txt) Chris@16: */ Chris@16: /*! Chris@16: * \file basic_sink_frontend.hpp Chris@16: * \author Andrey Semashev Chris@16: * \date 14.07.2009 Chris@16: * Chris@16: * The header contains implementation of a base class for sink frontends. Chris@16: */ Chris@16: Chris@16: #ifndef BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_ Chris@16: #define BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_ Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #endif // !defined(BOOST_LOG_NO_THREADS) Chris@16: #include Chris@16: Chris@16: #ifdef BOOST_HAS_PRAGMA_ONCE Chris@16: #pragma once Chris@16: #endif Chris@16: Chris@16: namespace boost { Chris@16: Chris@16: BOOST_LOG_OPEN_NAMESPACE Chris@16: Chris@16: namespace sinks { Chris@16: Chris@16: //! A base class for a logging sink frontend Chris@16: class BOOST_LOG_NO_VTABLE basic_sink_frontend : Chris@16: public sink Chris@16: { Chris@16: //! Base type Chris@16: typedef sink base_type; Chris@16: Chris@16: public: Chris@16: //! An exception handler type Chris@16: typedef base_type::exception_handler_type exception_handler_type; Chris@16: Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: protected: Chris@16: //! Mutex type Chris@16: typedef boost::log::aux::light_rw_mutex mutex_type; Chris@16: Chris@16: private: Chris@16: //! Synchronization mutex Chris@16: mutable mutex_type m_Mutex; Chris@16: #endif Chris@16: Chris@16: private: Chris@16: //! Filter Chris@16: filter m_Filter; Chris@16: //! Exception handler Chris@16: exception_handler_type m_ExceptionHandler; Chris@16: Chris@16: public: Chris@16: /*! Chris@16: * \brief Initializing constructor Chris@16: * Chris@16: * \param cross_thread The flag indicates whether the sink passes log records between different threads Chris@16: */ Chris@16: explicit basic_sink_frontend(bool cross_thread) : sink(cross_thread) Chris@16: { Chris@16: } Chris@16: Chris@16: /*! Chris@16: * The method sets sink-specific filter functional object Chris@16: */ Chris@16: template< typename FunT > Chris@16: void set_filter(FunT const& filter) Chris@16: { Chris@16: BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);) Chris@16: m_Filter = filter; Chris@16: } Chris@16: /*! Chris@16: * The method resets the filter Chris@16: */ Chris@16: void reset_filter() Chris@16: { Chris@16: BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);) Chris@16: m_Filter.reset(); Chris@16: } Chris@16: Chris@16: /*! Chris@16: * The method sets an exception handler function Chris@16: */ Chris@16: template< typename FunT > Chris@16: void set_exception_handler(FunT const& handler) Chris@16: { Chris@16: BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);) Chris@16: m_ExceptionHandler = handler; Chris@16: } Chris@16: Chris@16: /*! Chris@16: * The method resets the exception handler function Chris@16: */ Chris@16: void reset_exception_handler() Chris@16: { Chris@16: BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);) Chris@16: m_ExceptionHandler.clear(); Chris@16: } Chris@16: Chris@16: /*! Chris@16: * The method returns \c true if no filter is set or the attribute values pass the filter Chris@16: * Chris@16: * \param attrs A set of attribute values of a logging record Chris@16: */ Chris@16: bool will_consume(attribute_value_set const& attrs) Chris@16: { Chris@16: BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);) Chris@16: try Chris@16: { Chris@16: return m_Filter(attrs); Chris@16: } Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: catch (thread_interrupted&) Chris@16: { Chris@16: throw; Chris@16: } Chris@16: #endif Chris@16: catch (...) Chris@16: { Chris@16: if (m_ExceptionHandler.empty()) Chris@16: throw; Chris@16: m_ExceptionHandler(); Chris@16: return false; Chris@16: } Chris@16: } Chris@16: Chris@16: protected: Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: //! Returns reference to the frontend mutex Chris@16: mutex_type& frontend_mutex() const { return m_Mutex; } Chris@16: #endif Chris@16: Chris@16: //! Returns reference to the exception handler Chris@16: exception_handler_type& exception_handler() { return m_ExceptionHandler; } Chris@16: //! Returns reference to the exception handler Chris@16: exception_handler_type const& exception_handler() const { return m_ExceptionHandler; } Chris@16: Chris@16: //! Feeds log record to the backend Chris@16: template< typename BackendMutexT, typename BackendT > Chris@16: void feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend) Chris@16: { Chris@16: try Chris@16: { Chris@16: BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);) Chris@16: backend.consume(rec); Chris@16: } Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: catch (thread_interrupted&) Chris@16: { Chris@16: throw; Chris@16: } Chris@16: #endif Chris@16: catch (...) Chris@16: { Chris@16: BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);) Chris@16: if (m_ExceptionHandler.empty()) Chris@16: throw; Chris@16: m_ExceptionHandler(); Chris@16: } Chris@16: } Chris@16: Chris@16: //! Attempts to feeds log record to the backend, does not block if \a backend_mutex is locked Chris@16: template< typename BackendMutexT, typename BackendT > Chris@16: bool try_feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend) Chris@16: { Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: unique_lock< BackendMutexT > lock; Chris@16: try Chris@16: { Chris@16: unique_lock< BackendMutexT > tmp_lock(backend_mutex, try_to_lock); Chris@16: if (!tmp_lock.owns_lock()) Chris@16: return false; Chris@16: lock.swap(tmp_lock); Chris@16: } Chris@16: catch (thread_interrupted&) Chris@16: { Chris@16: throw; Chris@16: } Chris@16: catch (...) Chris@16: { Chris@16: boost::log::aux::shared_lock_guard< mutex_type > frontend_lock(this->frontend_mutex()); Chris@16: if (this->exception_handler().empty()) Chris@16: throw; Chris@16: this->exception_handler()(); Chris@101: return false; Chris@16: } Chris@16: #endif Chris@16: // No need to lock anything in the feed_record method Chris@16: boost::log::aux::fake_mutex m; Chris@16: feed_record(rec, m, backend); Chris@16: return true; Chris@16: } Chris@16: Chris@16: //! Flushes record buffers in the backend, if one supports it Chris@16: template< typename BackendMutexT, typename BackendT > Chris@16: void flush_backend(BackendMutexT& backend_mutex, BackendT& backend) Chris@16: { Chris@16: typedef typename BackendT::frontend_requirements frontend_requirements; Chris@16: flush_backend_impl(backend_mutex, backend, Chris@16: typename has_requirement< frontend_requirements, flushing >::type()); Chris@16: } Chris@16: Chris@16: private: Chris@16: //! Flushes record buffers in the backend (the actual implementation) Chris@16: template< typename BackendMutexT, typename BackendT > Chris@16: void flush_backend_impl(BackendMutexT& backend_mutex, BackendT& backend, mpl::true_) Chris@16: { Chris@16: try Chris@16: { Chris@16: BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);) Chris@16: backend.flush(); Chris@16: } Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: catch (thread_interrupted&) Chris@16: { Chris@16: throw; Chris@16: } Chris@16: #endif Chris@16: catch (...) Chris@16: { Chris@16: BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);) Chris@16: if (m_ExceptionHandler.empty()) Chris@16: throw; Chris@16: m_ExceptionHandler(); Chris@16: } Chris@16: } Chris@16: //! Flushes record buffers in the backend (stub for backends that don't support flushing) Chris@16: template< typename BackendMutexT, typename BackendT > Chris@16: void flush_backend_impl(BackendMutexT&, BackendT&, mpl::false_) Chris@16: { Chris@16: } Chris@16: }; Chris@16: Chris@16: //! A base class for a logging sink frontend with formatting support Chris@16: template< typename CharT > Chris@16: class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend : Chris@16: public basic_sink_frontend Chris@16: { Chris@16: typedef basic_sink_frontend base_type; Chris@16: Chris@16: public: Chris@16: //! Character type Chris@16: typedef CharT char_type; Chris@16: //! Formatted string type Chris@16: typedef std::basic_string< char_type > string_type; Chris@16: Chris@16: //! Formatter function object type Chris@16: typedef basic_formatter< char_type > formatter_type; Chris@16: //! Output stream type Chris@16: typedef typename formatter_type::stream_type stream_type; Chris@16: Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: protected: Chris@16: //! Mutex type Chris@16: typedef typename base_type::mutex_type mutex_type; Chris@16: #endif Chris@16: Chris@16: private: Chris@16: struct formatting_context Chris@16: { Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: //! Object version Chris@16: const unsigned int m_Version; Chris@16: #endif Chris@16: //! Formatted log record storage Chris@16: string_type m_FormattedRecord; Chris@16: //! Formatting stream Chris@16: stream_type m_FormattingStream; Chris@16: //! Formatter functor Chris@16: formatter_type m_Formatter; Chris@16: Chris@16: formatting_context() : Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: m_Version(0), Chris@16: #endif Chris@16: m_FormattingStream(m_FormattedRecord) Chris@16: { Chris@16: m_FormattingStream.exceptions(std::ios_base::badbit | std::ios_base::failbit); Chris@16: } Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: formatting_context(unsigned int version, std::locale const& loc, formatter_type const& formatter) : Chris@16: m_Version(version), Chris@16: m_FormattingStream(m_FormattedRecord), Chris@16: m_Formatter(formatter) Chris@16: { Chris@16: m_FormattingStream.exceptions(std::ios_base::badbit | std::ios_base::failbit); Chris@16: m_FormattingStream.imbue(loc); Chris@16: } Chris@16: #endif Chris@16: }; Chris@16: Chris@16: private: Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: Chris@16: //! State version Chris@16: volatile unsigned int m_Version; Chris@16: Chris@16: //! Formatter functor Chris@16: formatter_type m_Formatter; Chris@16: //! Locale to perform formatting Chris@16: std::locale m_Locale; Chris@16: Chris@16: //! Formatting state Chris@16: thread_specific_ptr< formatting_context > m_pContext; Chris@16: Chris@16: #else Chris@16: Chris@16: //! Formatting state Chris@16: formatting_context m_Context; Chris@16: Chris@16: #endif // !defined(BOOST_LOG_NO_THREADS) Chris@16: Chris@16: public: Chris@16: /*! Chris@16: * \brief Initializing constructor Chris@16: * Chris@16: * \param cross_thread The flag indicates whether the sink passes log records between different threads Chris@16: */ Chris@16: explicit basic_formatting_sink_frontend(bool cross_thread) : Chris@16: basic_sink_frontend(cross_thread) Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: , m_Version(0) Chris@16: #endif Chris@16: { Chris@16: } Chris@16: Chris@16: /*! Chris@16: * The method sets sink-specific formatter function object Chris@16: */ Chris@16: template< typename FunT > Chris@16: void set_formatter(FunT const& formatter) Chris@16: { Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex()); Chris@16: m_Formatter = formatter; Chris@16: ++m_Version; Chris@16: #else Chris@16: m_Context.m_Formatter = formatter; Chris@16: #endif Chris@16: } Chris@16: /*! Chris@16: * The method resets the formatter Chris@16: */ Chris@16: void reset_formatter() Chris@16: { Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex()); Chris@16: m_Formatter.reset(); Chris@16: ++m_Version; Chris@16: #else Chris@16: m_Context.m_Formatter.reset(); Chris@16: #endif Chris@16: } Chris@16: Chris@16: /*! Chris@16: * The method returns the current locale used for formatting Chris@16: */ Chris@16: std::locale getloc() const Chris@16: { Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex()); Chris@16: return m_Locale; Chris@16: #else Chris@16: return m_Context.m_FormattingStream.getloc(); Chris@16: #endif Chris@16: } Chris@16: /*! Chris@16: * The method sets the locale used for formatting Chris@16: */ Chris@16: void imbue(std::locale const& loc) Chris@16: { Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex()); Chris@16: m_Locale = loc; Chris@16: ++m_Version; Chris@16: #else Chris@16: m_Context.m_FormattingStream.imbue(loc); Chris@16: #endif Chris@16: } Chris@16: Chris@16: protected: Chris@16: //! Returns reference to the formatter Chris@16: formatter_type& formatter() Chris@16: { Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: return m_Formatter; Chris@16: #else Chris@16: return m_Context.m_Formatter; Chris@16: #endif Chris@16: } Chris@16: Chris@16: //! Feeds log record to the backend Chris@16: template< typename BackendMutexT, typename BackendT > Chris@16: void feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend) Chris@16: { Chris@16: formatting_context* context; Chris@16: Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: context = m_pContext.get(); Chris@16: if (!context || context->m_Version != m_Version) Chris@16: { Chris@16: { Chris@16: boost::log::aux::shared_lock_guard< mutex_type > lock(this->frontend_mutex()); Chris@16: context = new formatting_context(m_Version, m_Locale, m_Formatter); Chris@16: } Chris@16: m_pContext.reset(context); Chris@16: } Chris@16: #else Chris@16: context = &m_Context; Chris@16: #endif Chris@16: Chris@16: boost::log::aux::cleanup_guard< stream_type > cleanup1(context->m_FormattingStream); Chris@16: boost::log::aux::cleanup_guard< string_type > cleanup2(context->m_FormattedRecord); Chris@16: Chris@16: try Chris@16: { Chris@16: // Perform the formatting Chris@16: context->m_Formatter(rec, context->m_FormattingStream); Chris@16: context->m_FormattingStream.flush(); Chris@16: Chris@16: // Feed the record Chris@16: BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);) Chris@16: backend.consume(rec, context->m_FormattedRecord); Chris@16: } Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: catch (thread_interrupted&) Chris@16: { Chris@16: throw; Chris@16: } Chris@16: #endif Chris@16: catch (...) Chris@16: { Chris@16: BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(this->frontend_mutex());) Chris@16: if (this->exception_handler().empty()) Chris@16: throw; Chris@16: this->exception_handler()(); Chris@16: } Chris@16: } Chris@16: Chris@16: //! Attempts to feeds log record to the backend, does not block if \a backend_mutex is locked Chris@16: template< typename BackendMutexT, typename BackendT > Chris@16: bool try_feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend) Chris@16: { Chris@16: #if !defined(BOOST_LOG_NO_THREADS) Chris@16: unique_lock< BackendMutexT > lock; Chris@16: try Chris@16: { Chris@16: unique_lock< BackendMutexT > tmp_lock(backend_mutex, try_to_lock); Chris@16: if (!tmp_lock.owns_lock()) Chris@16: return false; Chris@16: lock.swap(tmp_lock); Chris@16: } Chris@16: catch (thread_interrupted&) Chris@16: { Chris@16: throw; Chris@16: } Chris@16: catch (...) Chris@16: { Chris@16: boost::log::aux::shared_lock_guard< mutex_type > frontend_lock(this->frontend_mutex()); Chris@16: if (this->exception_handler().empty()) Chris@16: throw; Chris@16: this->exception_handler()(); Chris@101: return false; Chris@16: } Chris@16: #endif Chris@16: // No need to lock anything in the feed_record method Chris@16: boost::log::aux::fake_mutex m; Chris@16: feed_record(rec, m, backend); Chris@16: return true; Chris@16: } Chris@16: }; Chris@16: Chris@16: namespace aux { Chris@16: Chris@16: template< Chris@16: typename BackendT, Chris@16: bool RequiresFormattingV = has_requirement< Chris@16: typename BackendT::frontend_requirements, Chris@16: formatted_records Chris@16: >::value Chris@16: > Chris@16: struct make_sink_frontend_base Chris@16: { Chris@16: typedef basic_sink_frontend type; Chris@16: }; Chris@16: template< typename BackendT > Chris@16: struct make_sink_frontend_base< BackendT, true > Chris@16: { Chris@16: typedef basic_formatting_sink_frontend< typename BackendT::char_type > type; Chris@16: }; Chris@16: Chris@16: } // namespace aux Chris@16: Chris@16: } // namespace sinks Chris@16: Chris@16: BOOST_LOG_CLOSE_NAMESPACE // namespace log Chris@16: Chris@16: } // namespace boost Chris@16: Chris@16: #include Chris@16: Chris@16: #endif // BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_