boost logging to file

Thanks to everyone who's replied to my previous boost logging question -
still having some issues I'm afraid :S
I've been struggling with boost log for a while now - I got their simple
example writing to a log file
(http://boost-log.sourceforge.net/libs/log/example/doc/tutorial_file.cpp).
However, when I try to copy that code into my own 'Logger' class, I
can't get it to write to the log file. I can see the file `default.log`
get created, but there is nothing in it.
I'm on debian 7 64bit. Everything compiles fine - compile line is:
g++ -o build/Logger.o -c -std=c++11 -Wall -g -O0
-DBOOST_LOG_DYN_LINK -DDEBUG src/Logger.cpp
g++ -o build/logtest build/Logger.o -lboost_log -lboost_log_setup
-lboost_date_time -lboost_thread -lboost_wave -lboost_regex
-lboost_program_options
Here's my code:
`Logger.cpp`
#include "Logger.h"
namespace logging = boost::log;
namespace sinks = boost::log::sinks;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace attrs = boost::log::attributes;
namespace keywords = boost::log::keywords;
namespace dhlogging {
Logger::Logger(std::string fileName)
{
initialize(fileName);
}
Logger::Logger(Logger const&)
{
}
Logger::~Logger()
{
}
Logger* Logger::logger_ = nullptr;
Logger* Logger::getInstance(std::string logFile)
{
if ( Logger::logger_ == nullptr ) {
logging::add_file_log( logFile );
logging::core::get()->set_filter
(
logging::trivial::severity >= logging::trivial::info
);
logging::add_common_attributes();
Logger::logger_ = new Logger(logFile);
}
return Logger::logger_;
}
void Logger::initialize(std::string fileName)
{
BOOST_LOG(log_) << "Hello, World!";
BOOST_LOG_SEV(log_, info) << "Hello, World2!";
}
void Logger::logInfo(std::string message)
{
BOOST_LOG_SEV(log_, info) << message;
}
void Logger::logDebug(std::string message)
{
BOOST_LOG_SEV(log_, debug) << message;
}
void Logger::logWarn(std::string message)
{
BOOST_LOG_SEV(log_, warning) << message;
}
void Logger::logError(std::string message)
{
BOOST_LOG_SEV(log_, error) << message;
}
void Logger::logFatal(std::string message)
{
BOOST_LOG_SEV(log_, fatal) << message;
}
}
int main(int, char*[])
{
logging::add_common_attributes();
using namespace logging::trivial;
dhlogging::Logger::getInstance()->logInfo("himom");
return 0;
}
`Logger.h`
#ifndef LOGGER_H_
#define LOGGER_H_
#include

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

Thanks Leo - I didn't think about doing my own severity stuff, very interesting. It turns out, in my example, I needed to send the ' keywords::auto_flush = true' parameter into the 'logging::add_file_log(..)' method. After that, it worked fine - very weird. Thanks for sending your example, I may pick some stuff out of it :) Cheers Jarrett On 03/08/13 07:01 PM, Leo Carreon wrote:
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
#include #include #include #include #include #include #include #include #include #include #include #include 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
(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
("Severity"); logging::register_simple_filter_factory ("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_timeboost::posix_time::ptime("TimeStamp", "%Y-%m-%d %H:%M:%S.%f") << " [" << expr::attr<ESeverityLevel>("Severity") << "] " << expr::attr<string>("Process") << ":" << expr::attrlogging::process_id("ProcessID") << ":" << expr::attrlogging::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 mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

Hi Jarrett, Two additional pieces of information for you: 1. You may uncomment the log collector code in my example. I have discovered the cause of my issue in the log collector code and it has nothing to do with the code in my example. The code I have will work as long as you change the directory in vFileName to be different that of vTarget. Note that the value of vTarget is the name of the directory where the collected log files will be stored. Having the collector code means you can restrict the total size of the collected log files to the value of vMaxSize. Older logs will be discarded when required. 2. I was originally using a C++11 compiler thus I had auto on vRotationSize, vMaxSize, vSeverity, pCore and rLogger. I’m just not sure if I didn’t make a mistake in specifying the right types for C++03. I hope this info is any use to you. Regards, Leo

Hi Jarrett, I forgot to mention that I also took on board the setting of auto_flush because this means log records are immediately flushed to the sink instead of being buffered. This is important if you are logging from multiple threads to reduce the possibility of log records being out of sequence in the log files although according to the Boost.Log documentation this cannot be 100% avoided with a multi-threaded application. Cheers again, Leo
participants (2)
-
Jarrett Chisholm
-
Leo Carreon