blob: b3c58e2ab02aa986db84798c0190c287e23464e5 [file] [log] [blame]
/*
* 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 record_ostream.hpp
* \author Andrey Semashev
* \date 09.03.2009
*
* This header contains a wrapper class around a logging record that allows to compose the
* record message with a streaming expression.
*/
#ifndef BOOST_LOG_SOURCES_RECORD_OSTREAM_HPP_INCLUDED_
#define BOOST_LOG_SOURCES_RECORD_OSTREAM_HPP_INCLUDED_
#include <string>
#include <ostream>
#include <boost/assert.hpp>
#include <boost/move/core.hpp>
#include <boost/move/utility.hpp>
#include <boost/utility/addressof.hpp>
#include <boost/log/detail/config.hpp>
#include <boost/log/detail/native_typeof.hpp>
#include <boost/log/detail/unhandled_exception_count.hpp>
#include <boost/log/core/record.hpp>
#include <boost/log/utility/unique_identifier_name.hpp>
#include <boost/utility/explicit_operator_bool.hpp>
#include <boost/log/utility/formatting_ostream.hpp>
#include <boost/log/detail/header.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
#pragma once
#endif
namespace boost {
BOOST_LOG_OPEN_NAMESPACE
/*!
* \brief Logging record adapter with a streaming capability
*
* This class allows to compose the logging record message by streaming operations. It
* aggregates the log record and provides the standard output stream interface.
*/
template< typename CharT >
class basic_record_ostream :
public basic_formatting_ostream< CharT >
{
//! Self type
typedef basic_record_ostream< CharT > this_type;
//! Base stream class
typedef basic_formatting_ostream< CharT > base_type;
public:
//! Character type
typedef CharT char_type;
//! String type to be used as a message text holder
typedef std::basic_string< char_type > string_type;
//! Stream type
typedef std::basic_ostream< char_type > stream_type;
private:
//! Log record
record* m_record;
public:
/*!
* Default constructor. Creates an empty record that is equivalent to the invalid record handle.
* The stream capability is not available after construction.
*
* \post <tt>!*this == true</tt>
*/
basic_record_ostream() BOOST_NOEXCEPT : m_record(NULL) {}
/*!
* Constructor from a record object. Attaches to the provided record.
*
* \pre <tt>!!rec == true</tt>
* \post <tt>&this->get_record() == &rec</tt>
* \param rec The record handle being attached to
*/
explicit basic_record_ostream(record& rec)
{
BOOST_ASSERT_MSG(!!rec, "Boost.Log: basic_record_ostream should only be attached to a valid record");
m_record = &rec;
init_stream();
}
/*!
* Destructor. Destroys the record, releases any sinks and attribute values that were involved in processing this record.
*/
~basic_record_ostream() BOOST_NOEXCEPT
{
detach_from_record();
}
/*!
* Conversion to an unspecified boolean type
*
* \return \c true, if stream is valid and ready for formatting, \c false, if the stream is not valid. The latter also applies to
* the case when the stream is not attached to a log record.
*/
BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT()
/*!
* Inverted conversion to an unspecified boolean type
*
* \return \c false, if stream is valid and ready for formatting, \c true, if the stream is not valid. The latter also applies to
* the case when the stream is not attached to a log record.
*/
bool operator! () const BOOST_NOEXCEPT
{
return (!m_record || base_type::fail());
}
/*!
* Flushes internal buffers to complete all pending formatting operations and returns the aggregated log record
*
* \return The aggregated record object
*/
record& get_record()
{
BOOST_ASSERT(m_record != NULL);
this->flush();
return *m_record;
}
/*!
* Flushes internal buffers to complete all pending formatting operations and returns the aggregated log record
*
* \return The aggregated record object
*/
record const& get_record() const
{
BOOST_ASSERT(m_record != NULL);
const_cast< this_type* >(this)->flush();
return *m_record;
}
/*!
* If the stream is attached to a log record, flushes internal buffers to complete all pending formatting operations.
* Then reattaches the stream to another log record.
*
* \param rec New log record to attach to
*/
void attach_record(record& rec)
{
BOOST_ASSERT_MSG(!!rec, "Boost.Log: basic_record_ostream should only be attached to a valid record");
detach_from_record();
m_record = &rec;
init_stream();
}
//! The function resets the stream into a detached (default initialized) state
BOOST_LOG_API void detach_from_record() BOOST_NOEXCEPT;
private:
//! The function initializes the stream and the stream buffer
BOOST_LOG_API void init_stream();
// Copy and assignment are closed
BOOST_DELETED_FUNCTION(basic_record_ostream(basic_record_ostream const&))
BOOST_DELETED_FUNCTION(basic_record_ostream& operator= (basic_record_ostream const&))
};
#ifdef BOOST_LOG_USE_CHAR
typedef basic_record_ostream< char > record_ostream; //!< Convenience typedef for narrow-character logging
#endif
#ifdef BOOST_LOG_USE_WCHAR_T
typedef basic_record_ostream< wchar_t > wrecord_ostream; //!< Convenience typedef for wide-character logging
#endif
namespace aux {
//! Internal class that provides formatting streams for record pumps
template< typename CharT >
struct stream_provider
{
//! Character type
typedef CharT char_type;
//! Formatting stream compound
struct stream_compound
{
stream_compound* next;
//! Log record stream adapter
basic_record_ostream< char_type > stream;
//! Initializing constructor
explicit stream_compound(record& rec) : next(NULL), stream(rec) {}
};
//! The method returns an allocated stream compound
BOOST_LOG_API static stream_compound* allocate_compound(record& rec);
//! The method releases a compound
BOOST_LOG_API static void release_compound(stream_compound* compound) BOOST_NOEXCEPT;
// Non-constructible, non-copyable, non-assignable
BOOST_DELETED_FUNCTION(stream_provider())
BOOST_DELETED_FUNCTION(stream_provider(stream_provider const&))
BOOST_DELETED_FUNCTION(stream_provider& operator= (stream_provider const&))
};
/*!
* \brief Logging record pump implementation
*
* The pump is used to format the logging record message text and then
* push it to the logging core. It is constructed on each attempt to write
* a log record and destroyed afterwards.
*
* The pump class template is instantiated on the logger type.
*/
template< typename LoggerT >
class record_pump
{
BOOST_MOVABLE_BUT_NOT_COPYABLE(record_pump)
private:
//! Logger type
typedef LoggerT logger_type;
//! Character type
typedef typename logger_type::char_type char_type;
//! Stream compound provider
typedef stream_provider< char_type > stream_provider_type;
//! Stream compound type
typedef typename stream_provider_type::stream_compound stream_compound;
//! Stream compound release guard
class auto_release;
friend class auto_release;
class auto_release
{
stream_compound* m_pCompound;
public:
explicit auto_release(stream_compound* p) BOOST_NOEXCEPT : m_pCompound(p) {}
~auto_release() BOOST_NOEXCEPT { stream_provider_type::release_compound(m_pCompound); }
};
protected:
//! A reference to the logger
logger_type* m_pLogger;
//! Stream compound
stream_compound* m_pStreamCompound;
//! Exception state
const unsigned int m_ExceptionCount;
public:
//! Constructor
explicit record_pump(logger_type& lg, record& rec) :
m_pLogger(boost::addressof(lg)),
m_pStreamCompound(stream_provider_type::allocate_compound(rec)),
m_ExceptionCount(unhandled_exception_count())
{
}
//! Move constructor
record_pump(BOOST_RV_REF(record_pump) that) BOOST_NOEXCEPT :
m_pLogger(that.m_pLogger),
m_pStreamCompound(that.m_pStreamCompound),
m_ExceptionCount(that.m_ExceptionCount)
{
that.m_pLogger = 0;
that.m_pStreamCompound = 0;
}
//! Destructor. Pushes the composed message to log.
~record_pump() BOOST_NOEXCEPT_IF(false)
{
if (m_pLogger)
{
auto_release cleanup(m_pStreamCompound); // destructor doesn't throw
// Only push the record if no exception has been thrown in the streaming expression (if possible)
if (m_ExceptionCount >= unhandled_exception_count())
m_pLogger->push_record(boost::move(m_pStreamCompound->stream.get_record()));
}
}
//! Returns the stream to be used for message text formatting
basic_record_ostream< char_type >& stream() const BOOST_NOEXCEPT
{
BOOST_ASSERT(m_pStreamCompound != 0);
return m_pStreamCompound->stream;
}
};
template< typename LoggerT >
BOOST_FORCEINLINE record_pump< LoggerT > make_record_pump(LoggerT& lg, record& rec)
{
return record_pump< LoggerT >(lg, rec);
}
} // namespace aux
#ifndef BOOST_LOG_DOXYGEN_PASS
#define BOOST_LOG_STREAM_INTERNAL(logger, rec_var)\
for (::boost::log::record rec_var = (logger).open_record(); !!rec_var;)\
::boost::log::aux::make_record_pump((logger), rec_var).stream()
#define BOOST_LOG_STREAM_WITH_PARAMS_INTERNAL(logger, rec_var, params_seq)\
for (::boost::log::record rec_var = (logger).open_record((BOOST_PP_SEQ_ENUM(params_seq))); !!rec_var;)\
::boost::log::aux::make_record_pump((logger), rec_var).stream()
#endif // BOOST_LOG_DOXYGEN_PASS
//! The macro writes a record to the log
#define BOOST_LOG_STREAM(logger)\
BOOST_LOG_STREAM_INTERNAL(logger, BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_record_))
//! The macro writes a record to the log and allows to pass additional named arguments to the logger
#define BOOST_LOG_STREAM_WITH_PARAMS(logger, params_seq)\
BOOST_LOG_STREAM_WITH_PARAMS_INTERNAL(logger, BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_record_), params_seq)
#ifndef BOOST_LOG_NO_SHORTHAND_NAMES
//! An equivalent to BOOST_LOG_STREAM(logger)
#define BOOST_LOG(logger) BOOST_LOG_STREAM(logger)
//! An equivalent to BOOST_LOG_STREAM_WITH_PARAMS(logger, params_seq)
#define BOOST_LOG_WITH_PARAMS(logger, params_seq) BOOST_LOG_STREAM_WITH_PARAMS(logger, params_seq)
#endif // BOOST_LOG_NO_SHORTHAND_NAMES
BOOST_LOG_CLOSE_NAMESPACE // namespace log
} // namespace boost
#include <boost/log/detail/footer.hpp>
#endif // BOOST_LOG_SOURCES_RECORD_OSTREAM_HPP_INCLUDED_