I am trying to do a log for my application. I want to add an attribute so I would know in what class is the log. I have starting a test to see if it works:
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/common.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/syslog_backend.hpp>
enum severity_levels
{
debug,
info,
warning,
error
};
typedef boost::log::sinks::synchronous_sink< boost::log::sinks::syslog_backend > SinkSysLogBackEnd;
typedef boost::log::sources::severity_logger< severity_levels > BoostLogger;
std::ostream& operator<< (std::ostream& strm, severity_levels level)
{
static const char* strings[] =
{
"debug",
"info",
"warning",
"error"
};
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
strm << strings[level];
else
strm << static_cast< int >(level);
return strm;
}
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_levels)
BOOST_LOG_ATTRIBUTE_KEYWORD(executable, "Executable", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(className, "Class name", std::string)
void init_syslog()
{
// Create a backend
boost::shared_ptr< SinkSysLogBackEnd > sink(new SinkSysLogBackEnd());
// We'll have to map our custom levels to the syslog levels
boost::log::sinks::syslog::custom_severity_mapping< severity_levels > mapping("Severity");
mapping[info] = boost::log::sinks::syslog::info;
mapping[warning] = boost::log::sinks::syslog::warning;
mapping[error] = boost::log::sinks::syslog::error;
sink->set_formatter(
boost::log::expressions::stream
// line id will be written in hex, 8-digits, zero-filled
<< executable << " <" << severity
<< "> : " << boost::log::expressions::smessage);
sink->locked_backend()->set_severity_mapper(mapping);
// Set the remote address to sent syslog messages to
sink->locked_backend()->set_target_address("localhost");
// Wrap it into the frontend and register in the core.
// The backend requires synchronization in the frontend.
boost::log::core::get()->add_sink(sink);
}
class Cls1
{
BoostLogger m_lg;
public:
Cls1()
{
// set the class name to Cls1
}
void foo()
{
// print log that has "Class Name" attribute set to "Cls1"
}
};
class Cls2
{
BoostLogger m_lg;
public:
Cls2()
{
// set the class name to Cls2
}
void foo()
{
// print log that has "Class Name" attribute set to "Cls2"
}
};
int main(int argc, char** argv)
{
init_syslog();
Cls1 o1;
o1.foo();
Cls2 o2;
o2.foo();
return 0;
}
Now I am stuck...
Thanks for any advice. I have seen the boost log attributes tutorial (link1, link2, link3), but I have not found something that would help...
Boost. Log, part of collection of the Boost C++ Libraries, provides tools for adding logging to libraries and applications.
The get function itself is thread-safe, so there is no need in additional synchronization around it.
Below a sample that I programed:
Logger.hpp :
#ifndef LOGGER_HPP
#define LOGGER_HPP
#include <ostream>
#include <fstream>
#include <boost/shared_ptr.hpp>
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sinks/async_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/utility/empty_deleter.hpp>
class Logger
{
public:
typedef boost::log::sinks::asynchronous_sink<boost::log::sinks::text_ostream_backend> text_sink;
Logger();
~Logger();
void Initialize();
void Finalize();
void addStream(boost::shared_ptr<std::ostream>& stream);
template< typename T >
Logger& operator<<(const T& value)
{
BOOST_LOG(my_logger::get()) << value;
return *this;
}
typedef Logger& (*LoggerManipulator)(Logger&);
Logger& operator<<(LoggerManipulator manip)
{
return manip(*this);
}
static Logger& endl(Logger& stream)
{
std::cout << std::endl;
return stream;
}
typedef std::basic_ostream<char, std::char_traits<char>> CoutType;
typedef CoutType& (*StandardEndLine)(CoutType&);
Logger& operator<<(StandardEndLine manip)
{
manip(std::cout);
return *this;
}
private:
boost::log::sources::logger_mt m_lg;
boost::shared_ptr<text_sink> m_sink;
};
#endif LOGGER_HPP
Logger.cpp:
#include <boost/date_time/posix_time/posix_time.hpp>
#include "Logger.hpp"
Logger::Logger()
:
m_sink(new text_sink),
m_lg()
{
}
Logger::~Logger()
{
}
void Logger::Initialize()
{
m_sink->locked_backend()->auto_flush(true);
m_sink->set_formatter
(
boost::log::expressions::format("[%1%] - %2%")
% boost::log::expressions::attr< boost::posix_time::ptime >("TimeStamp")
% boost::log::expressions::smessage
);
boost::log::core::get()->add_sink(m_sink);
boost::log::core::get()->add_global_attribute("TimeStamp",boost::log::attributes::local_clock());
}
void Logger::Finalize()
{
}
void Logger::addStream(boost::shared_ptr<std::ostream>& stream)
{
m_sink->locked_backend()->add_stream(stream);
}
main.cpp:
int main
{
boost::shared_ptr<std::ostream> stream_out(&std::clog, boost::empty_deleter());
boost::shared_ptr<std::ostream> stream_file(new std::ofstream(ProgramName + ".log", std::ostream::app));
Logger logger;
logger.addStream(stream_out);
logger.addStream(stream_file);
logger.Initialize();
logger << "Sample";
return 0;
}
I have solved the problem using static loggers for each class and using a method for initializing them. Something like this:
BoostLogger setClassNameAttribute(const std::string& classNameIn)
{
BoostLogger lg;
lg.add_attribute("ClassName", boost::log::attributes::constant<std::string>(classNameIn));
return lg;
}
class Cls1
{
static BoostLogger sm_lg;
public:
Cls1()
{
// set the class name to Cls1
}
void foo()
{
// print log that has "Class Name" attribute set to "Cls1"
}
};
BoostLogger Cls1::sm_lg = setClassNameAttribute("Cls1");
class Cls2
{
static BoostLogger sm_lg;
public:
Cls2()
{
// set the class name to Cls2
}
void foo()
{
// print log that has "Class Name" attribute set to "Cls2"
}
};
BoostLogger Cls2::sm_lg = setClassNameAttribute("Cls2");
I've adapted the code from the documentation on attributes and formatting to achieve a solution to this. I extracted all the relevant bits from my program to create the sample code below; It doesn't compile, but should give you a clear indication on what to do.
Rough explanation:
expressions::attr<string>("ClassName")
in the formatter somewhereseverity_logger<severity_level>
logger.add_attribute("ClassName", attrs::constant<string>("Foo"));
BOOST_LOG_SEV(logger, trace) << "Hello there";
I've left out a lot of the includes and 'using namespace's - let me know if you can't find where something comes from.
// I have this in my stdafx.h for convenience
typedef boost::log::sources::severity_logger<boost::log::trivial::severity_level> Logger;
// foo.h
class Foo
{
private:
static Logger logger;
static class _Init
{
public:
_Init()
{
logger.add_attribute("ClassName", boost::log::attributes::constant<std::string>("Foo"));
}
} _initializer;
public:
Foo();
}
// foo.cpp
Logger Foo::logger; // initialize the static variables
Foo::_Init Foo::_initializer;
Foo::Foo()
{
BOOST_LOG_SEV(logger, trace) << "Hello there";
}
// main.cpp
int main(int argc, char *argv[])
{
using namespace boost::log;
// Set up our sink and formatting
boost::log::add_file_log(
boost::log::keywords::file_name = "MyApp_%3N.log",
boost::log::keywords::rotation_size = 1 * 1024 * 1024,
boost::log::keywords::max_size = 20 * 1024 * 1024,
boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
boost::log::keywords::format =
(
expressions::stream
<< expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d %H:%M:%S,%f") << "|"
<< expressions::attr<std::string>("ClassName") << "|"
<< trivial::severity << "|"
<< expressions::message
),
boost::log::keywords::auto_flush = true
);
boost::log::add_common_attributes();
boost::log::core::get()->set_filter(
boost::log::trivial::severity >= boost::log::trivial::trace
);
Foo* foo = new Foo();
}
The output will look something like
2014-08-26 19:16:19,006726|Foo|trace|Hello there
(I've formatted it like that because the log viewer I use likes it that way)
Now you should be able to sort your log messages by class name. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With