Boost logo

Boost Users :

Subject: Re: [Boost-users] boost logging to file
From: Leo Carreon (lcarreon_at_[hidden])
Date: 2013-08-03 19:01:42


Hi Jarrett,

I commiserate with your struggle in trying to get Boost.Log going. Sorry, I
didn't try building your test program because in my opinion creating a
logger class is not the way to go because doing so means you loose the
flexibility of using stream-like syntax when logging. Instead all you
really need is a function to initialize Boost.Log and then use Boost.Log's
global logger mechanism.

For example, here is my final test program when I was initially
experimenting with Boost.Log:

#include <iostream>
#include <iomanip>
#include <string>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/attributes/clock.hpp>
#include <boost/log/attributes/current_process_name.hpp>
#include <boost/log/attributes/current_process_id.hpp>
#include <boost/log/attributes/current_thread_id.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/formatter_parser.hpp>
#include <boost/log/utility/setup/filter_parser.hpp>

using namespace std;

namespace logging = boost::log;
namespace keywords = boost::log::keywords;
namespace expr = boost::log::expressions;
namespace attr = boost::log::attributes;
namespace src = boost::log::sources;

#define LFC1_LOG(logger, level) BOOST_LOG_SEV(logger, level) << "(" <<
__FILE__ << ", " << __LINE__ << ") "

#define LFC1_LOG_TRACE(logger) LFC1_LOG(logger, trace)
#define LFC1_LOG_DEBUG(logger) LFC1_LOG(logger, debug)
#define LFC1_LOG_INFO(logger) LFC1_LOG(logger, info)
#define LFC1_LOG_WARNING(logger) LFC1_LOG(logger, warning)
#define LFC1_LOG_ERROR(logger) LFC1_LOG(logger, error)

static const auto RET_SUCCESS = 0;
static const auto RET_FAIL = 1;

enum ESeverityLevel
{
trace,
debug,
info,
warning,
error
};

BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", ESeverityLevel)

static const char* gpStrings[] =
{
"trace",
"debug",
"info",
"warning",
"error"
};

ostream& operator<<(ostream& arStream, const ESeverityLevel& arLevel)
{
if (arStream.good())
{
  if (static_cast<size_t>(arLevel) < sizeof(gpStrings) / sizeof(*gpStrings))
  {
   arStream << gpStrings[arLevel];
  }
  else
  {
   arStream << static_cast<int>(arLevel);
  }
}
return arStream;
}

istream& operator>>(istream& arStream, ESeverityLevel& arLevel)
{
if (arStream.good())
{
  string vLevel;
  arStream >> vLevel;
  for (unsigned vI = 0; vI < sizeof(gpStrings) / sizeof(*gpStrings); ++vI)
  {
   if (vLevel == gpStrings[vI])
   {
    arLevel = static_cast<ESeverityLevel>(vI);
   }
  }
}
return arStream;
}

BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(SLogger,
src::severity_logger<ESeverityLevel>)

static void gvLoggingInit()
{
// Register a formatter and filter for severity level.
logging::register_simple_formatter_factory<ESeverityLevel,
char>("Severity");
logging::register_simple_filter_factory<ESeverityLevel, char>("Severity");

// Setup the default values for the log settings.
string vFileName = "logs/log_%Y%m%d_%H%M%S_%3N.log";
int vRotationSize = 1024;
//string vTarget = "logs";
//int vMaxSize = 10 * 1024;
ESeverityLevel vSeverity = warning;

// Add a file log with the given settings.
logging::add_file_log
(
  keywords::file_name = vFileName,
  keywords::rotation_size = vRotationSize,
  //keywords::target = vTarget,
  //keywords::max_size = vMaxSize,
  //keywords::scan_method = logging::sinks::file::scan_all,
  keywords::format = expr::stream
   << expr::format_date_time<boost::posix_time::ptime>("TimeStamp",
"%Y-%m-%d %H:%M:%S.%f")
   << " ["
   << expr::attr<ESeverityLevel>("Severity")
   << "] "
   << expr::attr<string>("Process")
   << ":"
   << expr::attr<logging::process_id>("ProcessID")
   << ":"
   << expr::attr<logging::thread_id>("ThreadID")
   << " "
   << expr::message
);

// Add the logging attributes that we require.
logging::core_ptr pCore = logging::core::get();
pCore->add_global_attribute("TimeStamp", attr::local_clock());
pCore->add_global_attribute("Process", attr::current_process_name());
pCore->add_global_attribute("ProcessID", attr::current_process_id());
pCore->add_global_attribute("ThreadID", attr::current_thread_id());
pCore->set_filter(severity >= vSeverity);
}

int main()
{
try
{
  gvLoggingInit();

  src::severity_logger<ESeverityLevel>& rLogger = SLogger::get();

     LFC1_LOG_TRACE(rLogger) << "A trace severity message";
     LFC1_LOG_DEBUG(rLogger) << "A debug severity message";
     LFC1_LOG_INFO(rLogger) << "An informational severity message";
     LFC1_LOG_WARNING(rLogger) << "A warning severity message";
     LFC1_LOG_ERROR(rLogger) << "An error severity message";

     LFC1_LOG_TRACE(rLogger) << "Another trace severity message";
     LFC1_LOG_DEBUG(rLogger) << "Another debug severity message";
     LFC1_LOG_INFO(rLogger) << "Another informational severity message";
     LFC1_LOG_WARNING(rLogger) << "Another warning severity message";
     LFC1_LOG_ERROR(rLogger) << "Another error severity message";

     return RET_SUCCESS;
}
catch (exception& crException)
{
  cerr << crException.what() << endl;
  return RET_FAIL;
}
catch (...)
{
  cerr << "Unknown exception" << endl;
  return RET_FAIL;
}
}

My test program above also logs to a file, in fact it uses rotating log
files. It also defines its own severity level, which requires two functions
(operator<< and operator>>) to be defined and registered (really needed
otherwise the severity level string will not appear in the logs) with
Boost.Log. I also experimented with using a settings file which is not
shown in the above code which I abandoned because I couldn't get the
timestamp formatted the way I want it. You might also notice that I
commented out the part to do with log file collectors because I encountered
an issue with it. I also added my own attributes instead of adding the
common attributes which I didn't all need.

When I implemented the above into an actual application, I split it into 5
source files containing the following:
1. An include file which declares the severity level enumeration, the
operator<< function and the operator>> function. This is included by the
other 2 include files.
2. A source file which defines the operator<< and operator>> functions.
Build and link this with your application.
3. An include file which declares the initialization function. Include this
file in your application source file where you are going to invoke the
initialization function.
4. A source file which defines the initialization function. Build and link
this with your application.
5. An include file which defines your own logging macros and declares the
global logger. Include this file in each of your application source files
where you are going to perform logging.

In my case, I added another 2 source files which declare and define my own
settings class. Couldn't use Boost.Log's settings file for the reason I
already mentioned above. These replace the default settings in my test
program above. If you do this too, you either pass the name of the settings
file to the initialization function or somehow the settings class must know
where to look for the settings file.

I hope this helps.

Kind regards,
Leo


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net