544 lines
16 KiB
C++
544 lines
16 KiB
C++
/*
|
|
* Copyright Andrey Semashev 2007 - 2015.
|
|
* 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 <boost/type_traits/integral_constant.hpp>
|
|
#include <boost/log/detail/config.hpp>
|
|
#include <boost/log/detail/code_conversion.hpp>
|
|
#include <boost/log/detail/attachable_sstream_buf.hpp>
|
|
#include <boost/log/detail/fake_mutex.hpp>
|
|
#include <boost/log/core/record_view.hpp>
|
|
#include <boost/log/sinks/sink.hpp>
|
|
#include <boost/log/sinks/frontend_requirements.hpp>
|
|
#include <boost/log/expressions/filter.hpp>
|
|
#include <boost/log/expressions/formatter.hpp>
|
|
#if !defined(BOOST_LOG_NO_THREADS)
|
|
#include <boost/memory_order.hpp>
|
|
#include <boost/atomic/atomic.hpp>
|
|
#include <boost/thread/exceptions.hpp>
|
|
#include <boost/thread/tss.hpp>
|
|
#include <boost/log/detail/locks.hpp>
|
|
#include <boost/log/detail/light_rw_mutex.hpp>
|
|
#endif // !defined(BOOST_LOG_NO_THREADS)
|
|
#include <boost/log/detail/header.hpp>
|
|
|
|
#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_OVERRIDE
|
|
{
|
|
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)
|
|
try
|
|
{
|
|
if (!backend_mutex.try_lock())
|
|
return false;
|
|
}
|
|
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;
|
|
}
|
|
|
|
boost::log::aux::exclusive_auto_unlocker< BackendMutexT > unlocker(backend_mutex);
|
|
#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, boost::true_type)
|
|
{
|
|
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&, boost::false_type)
|
|
{
|
|
}
|
|
};
|
|
|
|
//! 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
|
|
{
|
|
class cleanup_guard
|
|
{
|
|
private:
|
|
formatting_context& m_context;
|
|
|
|
public:
|
|
explicit cleanup_guard(formatting_context& ctx) BOOST_NOEXCEPT : m_context(ctx)
|
|
{
|
|
}
|
|
|
|
~cleanup_guard()
|
|
{
|
|
m_context.m_FormattedRecord.clear();
|
|
m_context.m_FormattingStream.rdbuf()->max_size(m_context.m_FormattedRecord.max_size());
|
|
m_context.m_FormattingStream.rdbuf()->storage_overflow(false);
|
|
m_context.m_FormattingStream.clear();
|
|
}
|
|
|
|
BOOST_DELETED_FUNCTION(cleanup_guard(cleanup_guard const&))
|
|
BOOST_DELETED_FUNCTION(cleanup_guard& operator=(cleanup_guard const&))
|
|
};
|
|
|
|
#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(0u),
|
|
#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
|
|
boost::atomic< 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(0u)
|
|
#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.opaque_add(1u, boost::memory_order_relaxed);
|
|
#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.opaque_add(1u, boost::memory_order_relaxed);
|
|
#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.opaque_add(1u, boost::memory_order_relaxed);
|
|
#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.load(boost::memory_order_relaxed))
|
|
{
|
|
{
|
|
boost::log::aux::shared_lock_guard< mutex_type > lock(this->frontend_mutex());
|
|
context = new formatting_context(m_Version.load(boost::memory_order_relaxed), m_Locale, m_Formatter);
|
|
}
|
|
m_pContext.reset(context);
|
|
}
|
|
#else
|
|
context = &m_Context;
|
|
#endif
|
|
|
|
typename formatting_context::cleanup_guard cleanup(*context);
|
|
|
|
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)
|
|
try
|
|
{
|
|
if (!backend_mutex.try_lock())
|
|
return false;
|
|
}
|
|
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;
|
|
}
|
|
|
|
boost::log::aux::exclusive_auto_unlocker< BackendMutexT > unlocker(backend_mutex);
|
|
#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 <boost/log/detail/footer.hpp>
|
|
|
|
#endif // BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_
|