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