Hi John,
I'm working on integrating your last modifications about flushing
rolling files, but I encounter some problems.
With version V0.11.7, I change rolling_file.hpp (see attached file) to
flush logs each time. It works fine.
/destination::rolling_file_settings settings;
settings.file_count( 10 );
settings.max_size_bytes( 5242880 );
destination::rolling_file loggerFile( logFile.GetPath(), settings );
g_logger->writer().add_destination( loggerFile );
/
With last version (revision 42765), I just add one line. /
/
/destination::rolling_file_settings settings;/
/settings.file_count( 10 );/
/settings.max_size_bytes( 5242880 ); /
*/=> settings.flush_each_time(true); // add this line/*
/destination::rolling_file loggerFile( logFile.GetPath(), settings
);/
/g_logger->writer().add_destination( loggerFile );/
Then it doesn't work.
I put a break point in rolling_file_info#write but my program does not
enter in it anymore !
I think that rolling file objects are not instanciated because output
files are MY_FILE.txt and not MY_FILE.txt.1
Even if it will not compile alone, I give you my class Logger to see my
implementation.
Regards,
Benjamin
// destination_rolling_file.hpp
// Boost Logging library
//
// Author: John Torjo, www.torjo.com
//
// Copyright (C) 2007 John Torjo (see www.torjo.com for email)
//
// 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)
//
// See http://www.boost.org for updates, documentation, and revision history.
// See http://www.torjo.com/log2/ for more details
#ifndef JT28092007_destination_rolling_file_HPP_DEFINED
#define JT28092007_destination_rolling_file_HPP_DEFINED
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#if defined(_MSC_VER)
#pragma warning ( disable : 4355)
#endif
#include <boost/logging/detail/fwd.hpp>
#include <boost/logging/detail/manipulator.hpp>
#include <boost/logging/format/destination/convert_destination.hpp>
#include <fstream>
#include <string>
#include <sstream>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
namespace boost { namespace logging { namespace destination {
/**
@brief Settings you can pass to the rolling file. To see how it's used, see @ref dealing_with_flags.
*/
struct rolling_file_settings {
typedef boost::logging::detail::flag<rolling_file_settings> flag;
rolling_file_settings()
: max_size_bytes(this, 1024 * 1024)
, file_count(this, 10)
, initial_erase(this, false)
, start_where_size_not_exceeded(this, true)
{}
/// maximum size in bytes, by default 1Mb
flag::t<int> max_size_bytes;
/// how many files has a rolling file, by default, 10
flag::t<int> file_count;
/// if true, it initially erases all files from the rolling file (by default, false)
flag::t<bool> initial_erase;
/// if true, it starts with the first file that hasn't exceeded the max size;
/// otherwise, it starts with the first file (default = true)
flag::t<bool> start_where_size_not_exceeded;
};
namespace detail {
template<class convert_dest >
struct rolling_file_info {
rolling_file_info (const std::string& name_prefix, rolling_file_settings flags )
// many thanks to Martin Bauer
: m_name_prefix(name_prefix), m_flags(flags), m_cur_idx(0) {
namespace fs = boost::filesystem;
if ( m_flags.initial_erase()) {
for ( int idx = 0; idx < m_flags.file_count(); ++idx)
if ( fs::exists( file_name(idx) ))
fs::remove( file_name(idx) );
}
// see what file to start from
if ( m_flags.start_where_size_not_exceeded() ) {
for ( m_cur_idx = 0; m_cur_idx < m_flags.file_count(); ++m_cur_idx )
if ( fs::exists( file_name(m_cur_idx) )) {
if ( fs::file_size( file_name(m_cur_idx)) < m_flags.max_size_bytes() )
// file hasn't reached max size
break;
}
else
// file not found, we'll create it now
break;
}
recreate_file();
}
std::string file_name(int idx) {
std::ostringstream out;
out << m_name_prefix << "." << (idx+1);
return out.str();
}
void recreate_file() {
m_out = boost::shared_ptr< std::basic_ofstream<char_type> >(new std::basic_ofstream<char_type>( file_name(m_cur_idx).c_str(),
std::ios_base::out | std::ios_base::app));
m_cur_size = 0;
}
template<class msg_type> void write( const msg_type& msg) {
convert_dest::write(msg, (*m_out) );
// bdd 2007-12-10 : flush each time
m_cur_size += static_cast<int>( msg.size() );
m_out->flush();
// bdd
//if ( m_out->tellp() > m_flags.max_size_bytes()) {
if ( m_cur_size > m_flags.max_size_bytes()) {
m_cur_idx = (m_cur_idx + 1) % m_flags.file_count();
recreate_file();
}
}
boost::shared_ptr< std::basic_ofstream<char_type> > m_out;
std::string m_name_prefix;
rolling_file_settings m_flags;
// the index of the current file
int m_cur_idx;
// bdd 2007-12-10 : flush each time
//size_t m_cur_size;
int m_cur_size;
};
}
/**
@brief Writes to multiple files: name_prefix.1, name_prefix.2, ... name_prefix.N, and then restarts from 1.
We first write to name_prefix.1.
The log has a max_size. When max_size is reached, we start writing to name_prefix.2. When max_size is reached, we start writing to name_prefix.3.
And so on, until we reach name_prefix.N (N = file_count). When that gets fool, we start over, with name_prefix.1.
*/
template<class convert_dest = do_convert_destination > struct rolling_file_t : is_generic, non_const_context<detail::rolling_file_info<convert_dest> > {
typedef non_const_context<detail::rolling_file_info<convert_dest> > non_const_context_base;
/**
Constructs a rolling file
@param name_prefix the name to be used as prefix for the files
@param flags [optional] extra settings to pass to the rolling file. See rolling_file_settings and @ref dealing_with_flags.
*/
rolling_file_t(const std::string & name_prefix, rolling_file_settings flags = rolling_file_settings() ) : non_const_context_base(name_prefix, flags) {}
template<class msg_type> void operator()( const msg_type & msg) const {
non_const_context_base::context().write(msg);
}
bool operator==(const rolling_file_t & other) const {
return non_const_context_base::context().m_name_prefix == other.context().m_name_prefix;
}
};
/** @brief rolling_file_t with default values. See rolling_file_t
@copydoc rolling_file_t
*/
typedef rolling_file_t<> rolling_file;
}}}
#endif
//=============================================================================
// Includes
//=============================================================================
#include "StdAfx.h"
#include "common/log/Logger.h"
//----- MEDCommon -----
#include "common/system/ApplicationHelper.h"
#include "common/io/Directory.h"
#include "common/system/Time.h"
//----- boost -----
#pragma warning(disable: 4541)
#include "boost/logging/tags.hpp"
#include "boost/logging/defaults.hpp"
#include "boost/logging/format.hpp"
#include "boost/logging/format_all.hpp"
#include "boost/logging/format_fwd.hpp"
#include "boost/logging/format_ts.hpp"
#include "boost/logging/logging.hpp"
#include "boost/logging/detail/error.hpp"
#include "boost/logging/detail/filter.hpp"
#include "boost/logging/detail/find_gather.hpp"
#include "boost/logging/detail/format_msg_type.hpp"
#include "boost/logging/detail/format_write_detail.hpp"
#include "boost/logging/detail/forward_constructor.hpp"
#include "boost/logging/detail/fwd.hpp"
#include "boost/logging/detail/level.hpp"
#include "boost/logging/detail/log_keeper.hpp"
#include "boost/logging/detail/logger.hpp"
#include "boost/logging/detail/macros.hpp"
#include "boost/logging/detail/manipulator.hpp"
#include "boost/logging/detail/scenario.hpp"
#include "boost/logging/detail/template.hpp"
#include "boost/logging/detail/use_format_write.hpp"
// raw_doc ?
#include "boost/logging/detail/ts/ts.hpp"
#include "boost/logging/detail/ts/ts_boost.hpp"
#include "boost/logging/detail/ts/ts_none.hpp"
//#include "boost/logging/detail/ts/ts_posix.hpp"
#include "boost/logging/detail/ts/ts_resource.hpp"
#include "boost/logging/detail/ts/ts_win32.hpp"
#include "boost/logging/detail/tss/tss.hpp"
#include "boost/logging/detail/tss/tss_ensure_proper_delete.hpp"
#include "boost/logging/detail/tss/tss_impl.hpp"
//#include "boost/logging/detail/tss/tss_impl_pthread.hpp"
#include "boost/logging/detail/tss/tss_impl_win32.hpp"
#include "boost/logging/format/array.hpp"
#include "boost/logging/format/op_equal.hpp"
#include "boost/logging/format/optimize.hpp"
#include "boost/logging/format/destination/convert_destination.hpp"
#include "boost/logging/format/destination/defaults.hpp"
#include "boost/logging/format/destination/file.hpp"
#include "boost/logging/format/destination/rolling_file.hpp"
#include "boost/logging/format/destination/shared_memory.hpp"
#include "boost/logging/format/formatter/convert_format.hpp"
#include "boost/logging/format/formatter/defaults.hpp"
#include "boost/logging/format/formatter/tags.hpp"
#include "boost/logging/format/formatter/thread_id.hpp"
#include "boost/logging/format/formatter/time.hpp"
#include "boost/logging/gather/ostream_like.hpp"
#include "boost/logging/tag/defaults.hpp"
#include "boost/logging/writer/format_write.hpp"
#include "boost/logging/writer/on_dedicated_thread.hpp"
#include "boost/logging/writer/ts_write.hpp"
using namespace boost::logging;
//----- Step 3 : Specify your logging class(es) -----
//typedef logger_format_write< > log_type;
// thread safe
typedef logger_format_write< default_, default_, writer::threading::ts_write > log_type;
//typedef logger_format_write< default_, default_, writer::threading::on_dedicated_thread > log_type;
//----- Step 4: declare which filters and loggers you'll use (usually in a header file) -----
// filter
BOOST_DECLARE_LOG_FILTER(g_log_level, level::holder )
BOOST_DECLARE_LOG_FILTER(g_audit_filter, filter::ts )
// logger
BOOST_DECLARE_LOG(g_logger, log_type)
BOOST_DECLARE_LOG(g_audit, log_type)
BOOST_DECLARE_LOG(g_debug, log_type)
//----- Step 5: define the macros through which you'll log -----
//----- Step 6: Define the filters and loggers you'll use (usually in a source file) -----
// filter
BOOST_DEFINE_LOG_FILTER(g_log_level, level::holder ) // holds the application log level
BOOST_DEFINE_LOG_FILTER(g_audit_filter, filter::ts )
BOOST_DEFINE_LOG_FILTER(g_debug_filter, filter::ts )
//BOOST_DEFINE_LOG_FILTER(g_audit_filter, filter::no_ts )
// logger
BOOST_DEFINE_LOG(g_logger, log_type)
BOOST_DEFINE_LOG(g_audit, log_type)
BOOST_DEFINE_LOG(g_debug, log_type)
using namespace common::log;
//=============================================================================
// Global functions
//=============================================================================
void
Audit( const std::ostringstream & msg )
{
Info( msg );
BOOST_LOG_USE_LOG_IF_FILTER(g_audit, g_audit_filter->is_enabled() ) << "\t[AUDIT]\t" << msg.str();
}
void
Debug( const std::ostringstream & msg )
{
BOOST_LOG_USE_LOG_IF_FILTER(g_debug, g_debug_filter->is_enabled() ) << "\t[DEBUG]\t" << msg.str();
}
void
Trace2( const std::ostringstream & msg )
{
Debug( msg );
common::log::Logger::s_traces.Push( msg.str() );
}
void
Info( const std::ostringstream & msg )
{
BOOST_LOG_USE_LOG_IF_LEVEL(g_logger, g_log_level, info ) << "\t[INFO]\t" << msg.str();
BOOST_LOG_USE_LOG_IF_FILTER(g_debug, g_debug_filter->is_enabled() ) << "\t[INFO]\t" << msg.str();
}
void
Warning( const std::ostringstream & msg )
{
BOOST_LOG_USE_LOG_IF_LEVEL(g_logger, g_log_level, warning ) << "\t[WARN]\t" << msg.str();
BOOST_LOG_USE_LOG_IF_FILTER(g_debug, g_debug_filter->is_enabled() ) << "\t[WARN]\t" << msg.str();
}
void
Error( const std::ostringstream & msg )
{
if ( ! common::log::Logger::IsInitialized() )
{
common::log::Logger::Init( "INIT" );
}
if ( ! common::log::Logger::s_traces.IsEmpty() )
{
//
vector<string> traces = common::log::Logger::s_traces.ToArray();
for ( size_t i=0; i < traces.size(); i++ )
{
BOOST_LOG_USE_LOG_IF_LEVEL(g_logger, g_log_level, error ) << "\t[TRACE]\t" << traces[i];
}
}
BOOST_LOG_USE_LOG_IF_LEVEL(g_logger, g_log_level, error ) << "\t[ERROR]\t" << msg.str();
BOOST_LOG_USE_LOG_IF_FILTER(g_debug, g_debug_filter->is_enabled() ) << "\t[ERROR]\t" << msg.str();
}
void
Exception( const std::ostringstream & msg )
{
if ( ! common::log::Logger::IsInitialized() )
{
common::log::Logger::Init( "INIT" );
}
if ( ! common::log::Logger::s_traces.IsEmpty() )
{
//
vector<string> traces = common::log::Logger::s_traces.ToArray();
for ( size_t i=0; i < traces.size(); i++ )
{
BOOST_LOG_USE_LOG_IF_LEVEL(g_logger, g_log_level, fatal ) << "\t[TRACE]\t" << traces[i];
}
}
BOOST_LOG_USE_LOG_IF_LEVEL(g_logger, g_log_level, fatal ) << "\t[FATAL]\t" << msg.str();
BOOST_LOG_USE_LOG_IF_FILTER(g_debug, g_debug_filter->is_enabled() ) << "\t[FATAL]\t" << msg.str();
}
//=============================================================================
// Class attributs
//=============================================================================
bool Logger::s_isInitialized = false;
bool Logger::MODE_DEBUG = false;
common::container::RollingStack<string> Logger::s_traces( 15 );
//=============================================================================
// Class methods
//=============================================================================
void
Logger::SetLevel( LEVEL level )
{
g_audit_filter->set_enabled(true);
g_debug_filter->set_enabled(false);
switch ( level )
{
case LEVEL_DEBUG :
Logger::MODE_DEBUG = true;
g_debug_filter->set_enabled(true);
g_log_level->set_enabled(level::debug); // debug
break;
case LEVEL_INFO :
g_log_level->set_enabled(level::info); // info + warning + ...
break;
case LEVEL_ERROR :
g_log_level->set_enabled(level::error); // error + fatal + ...
break;
default:
g_log_level->set_enabled(level::info);
break;
}
}
bool
Logger::IsInitialized()
{
return s_isInitialized;
}
void
Logger::Init( const string & product )
{
if ( s_isInitialized )
{
return;
}
// check log dir
common::system::ApplicationHelper ah;
common::io::Directory dir( ah.GetPath() + "logfiles" );
dir.CheckAndCreate();
// init file names
//string date = common::system::Time::GetTime( common::system::Time::FORMAT_AAAA_MM_JJ );
//common::io::File logFile( dir.GetPath() + date + "_" + product + ".txt" );
//common::io::File auditFile( dir.GetPath() + date + "_AUDIT_" + product + ".txt" );
common::io::File logFile( dir.GetPath() + product + ".txt" );
common::io::File auditFile( dir.GetPath() + "AUDIT_" + product + ".txt" );
common::io::File debugFile( dir.GetPath() + "DEBUG_" + product + ".txt" );
// Step 7: add formatters and destinations
// That is, how the message is to be formatted...
g_logger->writer().add_formatter( formatter::time("$dd/$MM/$yyyy - $hh:$mm.$ss ") );
g_logger->writer().add_formatter( formatter::idx() );
g_logger->writer().add_formatter( formatter::append_newline() );
//g_log_err->writer().add_formatter( formatter::tag::module() ); // tag::file_line() );
//g_log_err->writer().add_formatter( formatter::tag::level() );
g_audit->writer().add_formatter( formatter::time("$dd/$MM/$yyyy - $hh:$mm.$ss ") );
g_audit->writer().add_formatter( formatter::idx() );
g_audit->writer().add_formatter( formatter::append_newline() );
g_debug->writer().add_formatter( formatter::time("$dd/$MM/$yyyy - $hh:$mm.$ss ") );
g_debug->writer().add_formatter( formatter::idx() );
g_debug->writer().add_formatter( formatter::append_newline() );
g_logger->writer().add_destination( destination::cout() );
// bdd : rolling file : pb de flush (garde les donnees en cache)
destination::rolling_file_settings settings;
settings.file_count( 10 );
settings.max_size_bytes( 5242880 ); // bytes = 5M
//settings.flush_each_time(true);
destination::rolling_file loggerFile( logFile.GetPath(), settings );
g_logger->writer().add_destination( loggerFile );
g_debug->writer().add_destination( destination::dbg_window() );
destination::rolling_file_settings settingsD;
settingsD.file_count( 30 );
settingsD.max_size_bytes( 10000000 ); // ~10M
//settingsD.flush_each_time(true);
g_debug->writer().add_destination( destination::rolling_file( debugFile.GetPath(), settingsD ) );
// bdd xml : en attente
//g_log_err->writer().add_destination( as_xml("logerror.xml") );
g_audit->writer().add_destination( destination::file( auditFile.GetPath() ) );
#ifdef _DEBUG
SetLevel( Logger::LEVEL_DEBUG );
#else
SetLevel( Logger::LEVEL_INFO );
#endif
s_isInitialized = true;
// Step 9 : Enjoy!
}
//=============================================================================
// Constructor and destructor
//=============================================================================
Logger::Logger(void)
: common::app::Object("common::log::Logger")
{
}
Logger::~Logger(void)
{
}
//=============================================================================
// Public methods
//=============================================================================
//* Step 1: (optional) Specify your format message class and/or destination message class. By default, it's std::(w)string. You'll use this when you want a optimize string class.
//* Step 2: (optional) Specify your formatter & destination base classes
//* Step 3: Specify your logger class(es)
//* Step 4: Declare the filters and loggers you'll use (in a header file)
//* Step 5: Define the macros through which you'll do logging
//* Step 6: Define the loggers and the filters you'll use (in a source file). We need this separation (into declaring and defining the logs/filters), in order to make compilation times fast.
//* Step 7: Add formatters and destinations. That is, how the message is to be formatted...
//* Step 8: Use it
//* Step 9: Enjoy the results!
//* debug (smallest level),
//* info,
//* warning ,
//* error ,
//* fatal (highest level)
#ifndef _COMMON_LOG_LOGGER_H_
#define _COMMON_LOG_LOGGER_H_
#pragma once
//=============================================================================
// Includes
//=============================================================================
//----- MEDCommon -----
#include "../commonIncludes.h"
#include "../app/Object.h"
#include "StackLog.h"
#include "common/txt/StringHelper.h"
#include "common/container/RollingStack.h"
void MEDCOMMON_API Audit( const std::ostringstream & msg );
void MEDCOMMON_API Debug( const std::ostringstream & msg );
void MEDCOMMON_API Trace2( const std::ostringstream & msg );
void MEDCOMMON_API Info( const std::ostringstream & msg );
void MEDCOMMON_API Warning( const std::ostringstream & msg );
void MEDCOMMON_API Error( const std::ostringstream & msg );
void MEDCOMMON_API Exception( const std::ostringstream & msg );
#define MED_FUNCTION \
common::log::StackLog log( __FUNCTION__ );
#define MED_FUNCTION2( msg ) \
ostringstream s; \
s << msg; \
common::log::StackLog log( __FUNCTION__, s );
#define MED_METHOD( object ) \
common::log::StackLog log( object,__FUNCTION__ );
#define MED_METHOD2( object, params ) \
ostringstream s; \
s << params; \
common::log::StackLog log( object,__FUNCTION__, s );
#define MED_AUDIT( module, msg ) \
{ \
ostringstream s; \
s << "[" << module << "]\t" \
<< msg; \
Audit( s ); \
}
#define MED_DBG2( module, msg ) \
{ \
if ( common::log::Logger::MODE_DEBUG ) \
{ \
string file = __FILE__; \
string fileName = common::txt::StringHelper::LastToken(file,"\\"); \
ostringstream s; \
s << "[" << module << "]\t" \
<< msg \
<< " (" \
<< fileName \
<< "#" \
<< __FUNCTION__ \
<< " L." \
<< __LINE__ \
<< ")"; \
Debug( s ); \
} \
}
#define MED_TRC2( module, msg ) \
{ \
string file = __FILE__; \
string fileName = common::txt::StringHelper::LastToken(file,"\\"); \
ostringstream s; \
s << "[" << module << "]\t" \
<< msg \
<< " (" \
<< fileName \
<< "#" \
<< __FUNCTION__ \
<< " L." \
<< __LINE__ \
<< ")"; \
Trace2( s ); \
}
#define MED_INF2( module, msg ) \
{ \
ostringstream s; \
s << "[" << module << "]\t" \
<< msg; \
Info( s ); \
}
#define MED_WARN2( module, msg ) \
{ \
string file = __FILE__; \
string fileName = common::txt::StringHelper::LastToken(file,"\\"); \
ostringstream s; \
s << "[" << module << "]\t" \
<< msg \
<< " (" \
<< fileName \
<< "#" \
<< __FUNCTION__ \
<< " L." \
<< __LINE__ \
<< ")"; \
Warning( s ); \
}
#define MED_ERR2( module, msg ) \
{ \
string file = __FILE__; \
string fileName = common::txt::StringHelper::LastToken(file,"\\"); \
ostringstream s; \
s << "[" << module << "]\t" \
<< msg \
<< " (" \
<< fileName \
<< "#" \
<< __FUNCTION__ \
<< " L." \
<< __LINE__ \
<< ")"; \
Error( s ); \
}
#define MED_EXC2( module, msg ) \
{ \
string file = __FILE__; \
string fileName = common::txt::StringHelper::LastToken(file,"\\"); \
ostringstream s; \
s << "[" << module << "]\t" \
<< msg \
<< " (" \
<< fileName \
<< "#" \
<< __FUNCTION__ \
<< " L." \
<< __LINE__ \
<< ")"; \
Exception( s ); \
}
namespace common
{
namespace log
{
class MEDCOMMON_API Logger : public common::app::Object
{
//=============================================================================
// Constants
//=============================================================================
public:
enum LEVEL {
LEVEL_DEBUG ,
LEVEL_INFO ,
LEVEL_ERROR };
//=============================================================================
// Class attributs
//=============================================================================
public:
static bool MODE_DEBUG;
// traces
static common::container::RollingStack<string> s_traces;
private:
static bool s_isInitialized;
//=============================================================================
// Class methods
//=============================================================================
public:
static void SetProduct( const string & product );
static bool IsInitialized();
static void Init( const string & product );
static void SetLevel( LEVEL level );
//=============================================================================
// Attributs
//=============================================================================
private:
//=============================================================================
// Constructor and destructor
//=============================================================================
private:
Logger();
virtual ~Logger();
//=============================================================================
// Public methods
//=============================================================================
public:
//=============================================================================
// Other methods
//=============================================================================
protected:
};
} // namespace log
} // namespace common
#endif // _COMMON_LOG_LOGGER_H_