I need a Boost logger which logs to console (and later on to a file), with following parameters:
"[%TimeStamp%] [%Severity%] [%File%(%Line%)] %Message%".
I have read the Boost.Log v2 docs and got some "inspiraton" from other places but I can't really get this to work.
// cswlogger.h
#pragma once
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/attributes/mutable_constant.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>
BOOST_LOG_GLOBAL_LOGGER(sysLogger,
boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level>);
class CswLogger
{
public:
/// Init with default info level logging
static void init(boost::log::trivial::severity_level level = boost::log::trivial::info);
/// Disable logging
static void disable();
};
#define LOG_LOG_LOCATION(LOGGER, LEVEL, ARG) \
BOOST_LOG_SEV(LOGGER, boost::log::trivial::LEVEL) \
<< boost::log::add_value("Line", __LINE__) \
<< boost::log::add_value("File", __FILE__) << ARG
/// System Log macros.
/// TRACE < DEBUG < INFO < WARN < ERROR < FATAL
#define LOG_TRACE(ARG) LOG_LOG_LOCATION(sysLogger::get(), trace, ARG);
#define LOG_DEBUG(ARG) LOG_LOG_LOCATION(sysLogger::get(), debug, ARG);
#define LOG_INFO(ARG) LOG_LOG_LOCATION(sysLogger::get(), info, ARG);
#define LOG_WARN(ARG) LOG_LOG_LOCATION(sysLogger::get(), warning, ARG);
#define LOG_ERROR(ARG) LOG_LOG_LOCATION(sysLogger::get(), error, ARG);
#define LOG_FATAL(ARG) LOG_LOG_LOCATION(sysLogger::get(), fatal, ARG);
And source file:
// cswlogger.cpp
#include "cswlogger.h"
#include <boost/log/core.hpp>
#include <boost/log/common.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/settings.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
BOOST_LOG_GLOBAL_LOGGER_DEFAULT(sysLogger,
boost::log::sources::severity_channel_logger_mt<boost::log::trivial::severity_level>);
void CswLogger::init(boost::log::trivial::severity_level level)
{
boost::log::add_console_log
(
std::clog,
boost::log::keywords::format = "[%TimeStamp%] [%Severity%] [%File%(%Line%)] %Message%"
);
boost::log::core::get()->set_filter
(
boost::log::trivial::severity >= level
);
// Indicate start of logging
LOG_INFO("Log Start");
}
void CswLogger::disable()
{
boost::log::core::get()->set_logging_enabled(false);
}
main.cpp
#include "cswlogger.h"
CswLogger::init();
LOG_INFO("This is a info trace");
The output of this will be: "[ ] [ ] main.cpp(3) This is a info trace"
The Timestamp and Severity paramaters are missing.
Is it possible to use the "BOOST_LOG_SEV" log macro and add custom log parameters or do I need to use another approach?
First, the TimeStamp attribute is missing in the output because you haven't added it to the logging core. You can add it as described here:
boost::log::core::get()->add_global_attribute(
"TimeStamp",
boost::log::attributes::local_clock());
Or you could add it as one of the commonly used attributes by calling add_common_attributes.
Next, the Severity attribute is present, it is provided by the logger. However, the value type of that attribute (which is boost::log::trivial::severity_level) is not supported by default for filters and formatters parsed from strings. You can solve this in two ways.
First, you can switch to the manual setup of the logging sinks, which will allow you to set the filter and formatter for the sink as a lambda expression. This way you will be able to provide the attribute type to the formatter. For example:
BOOST_LOG_ATTRIBUTE_KEYWORD(a_timestamp, "TimeStamp", boost::log::attributes::local_clock::value_type)
BOOST_LOG_ATTRIBUTE_KEYWORD(a_severity, "Severity", boost::log::trivial::severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(a_file, "File", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(a_line, "Line", int)
typedef boost::log::sinks::synchronous_sink< boost::log::sinks::text_ostream_backend > sink_t;
auto sink = boost::make_shared< sink_t >();
sink->set_formatter(boost::log::expressions::ostream
<< "[" << a_timestamp << "] "
<< "[" << a_severity << "] "
<< "[" << a_file << "(" << a_line << ")] "
<< boost::log::expressions::message);
boost::log::core::get()->add_sink(sink);
Alternatively, you can register the formatter and filter factories for the Severity attribute so that the formatter and filter parsers are able to associate that attribute with the boost::log::trivial::severity_level type. This is described in detail here. In your case, you can just add this call before any formatters are parsed (i.e. before the add_console_log call):
boost::log::register_simple_formatter_factory<
boost::log::trivial::severity_level, char >("Severity");
Related
Scenario
I have a C++ function which intakes a parameter as std::chrono::milliseconds. It is basically a timeout value. And, it is a default parameter set to some value by default.
Code
#include <iostream>
#include <chrono>
void Fun(const std::chrono::milliseconds someTimeout = std::chrono::milliseconds(100)) {
if (someTimeout > 0) {
std::cout << "someNumberInMillis is: " << someNumberInMillis.count() << std::endl;
}
}
int main() {
unsigned int someValue = 500;
Fun(std::chrono::milliseconds(someValue))
}
Issue
All of above is okay but, when I call Fun with a value then fails to compile and I get the following error:
No viable conversion from 'bool' to 'std::chrono::milliseconds' (aka
'duration >')
Question:
What am I doing wrong here? I want the caller of Fun to be explicitly aware that it is using std::chrono::milliseconds when it invokes Fun. But the compiler doesn't seem to allow using std::chrono::milliseconds as a parameter?
How use std::chrono::milliseconds as a default parameter?
Environment
Compiler used is clang on macOS High Sierra
With the other syntax errors fixed, this compiles without warnings in GCC 9:
#include <iostream>
#include <chrono>
void Fun(const std::chrono::milliseconds someNumberInMillis
= std::chrono::milliseconds(100))
{
if (someNumberInMillis > std::chrono::milliseconds{0}) {
std::cout << "someNumberInMillis is: " << someNumberInMillis.count()
<< std::endl;
}
}
int main()
{
unsigned int someValue = 500;
Fun(std::chrono::milliseconds(someValue));
}
The following simple code, compiled with -lboost_locale generates a bad cast exception. It is the same code in the boost tutorial itself. Any help?
#include <boost/locale.hpp>
#include <iostream>
int main()
{
using namespace boost::locale;
date_time now;
std::cout<<as::date<<now<<std::endl;
}
You need to imbue (global) locales:
Live On Coliru
#include <boost/locale.hpp>
#include <iostream>
int main() {
using namespace boost::locale;
boost::locale::generator gen;
std::locale loc = gen.generate(""); // or "C", "en_US.UTF-8" etc.
std::locale::global(loc);
std::cout.imbue(loc);
date_time_period_set things;
date_time now;
std::cout << as::date << now << std::endl;
}
Prints, e.g. on coliru:
09/17/15
I am trying to create a Global Logger within my entire application so I can use
src::severity_logger_mt< >& lg = my_logger::get();
to get the global logger for different classes (resided in different files) logging.
I try to follow the example listed in boost.org (as listed below). But does not seems to work. Did anyone know any example I can follow or what I need to do make if works. Thanks.
http://www.boost.org/doc/libs/1_54_0/libs/log/doc/html/log/detailed/sources.html
BOOST_LOG_GLOBAL_LOGGER(my_logger, src::severity_logger_mt)
// my_logger.h
// ===========
#include "my_logger.h"
BOOST_LOG_GLOBAL_LOGGER_INIT(my_logger, src::severity_logger_mt)
{
src::severity_logger_mt< > lg;
lg.add_attribute("StopWatch", boost::make_shared< attrs::timer >());
return lg;
}
// my_logger.cpp
// ===========
#include "my_logger.h"
BOOST_LOG_GLOBAL_LOGGER_INIT(my_logger, src::severity_logger_mt)
{
src::severity_logger_mt< > lg;
lg.add_attribute("StopWatch", boost::make_shared< attrs::timer >());
return lg;
}
I've just managed to get this working myself
Logging.h
#pragma once
#include <boost/log/expressions.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup.hpp>
#define INFO BOOST_LOG_SEV(my_logger::get(), boost::log::trivial::info)
#define WARN BOOST_LOG_SEV(my_logger::get(), boost::log::trivial::warning)
#define ERROR BOOST_LOG_SEV(my_logger::get(), boost::log::trivial::error)
#define SYS_LOGFILE "/var/log/example.log"
//Narrow-char thread-safe logger.
typedef boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level> logger_t;
//declares a global logger with a custom initialization
BOOST_LOG_GLOBAL_LOGGER(my_logger, logger_t)
Logging.cpp
#include "Logging.h"
namespace attrs = boost::log::attributes;
namespace expr = boost::log::expressions;
namespace logging = boost::log;
//Defines a global logger initialization routine
BOOST_LOG_GLOBAL_LOGGER_INIT(my_logger, logger_t)
{
logger_t lg;
logging::add_common_attributes();
logging::add_file_log(
boost::log::keywords::file_name = SYS_LOGFILE,
boost::log::keywords::format = (
expr::stream << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
<< " [" << expr::attr< boost::log::trivial::severity_level >("Severity") << "]: "
<< expr::smessage
)
);
logging::add_console_log(
std::cout,
boost::log::keywords::format = (
expr::stream << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
<< " [" << expr::attr< boost::log::trivial::severity_level >("Severity") << "]: "
<< expr::smessage
)
);
logging::core::get()->set_filter
(
logging::trivial::severity >= logging::trivial::info
);
return lg;
}
main.c
#include "Logging.h"
int main(int argc, char **argv)
{
INFO << "Program started";
return 0;
}
My build settings
AM_LDFLAGS += -lboost_system -lboost_thread -lpthread
AM_LDFLAGS += -DBOOST_LOG_DYN_LINK -lboost_log_setup -lboost_log
AM_CXXFLAGS += -std=c++11 -DBOOST_LOG_DYN_LINK
I'm trying to use c_escape_ch_p (see here) from boost::spirit to parse an escaped C++ string. But I'm getting a compiler error. Here is my code:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/home/classic/utility/escape_char.hpp>
#include <boost/spirit/home/classic/utility/confix.hpp>
#include <iostream>
#include <string>
namespace client {
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
template <typename Iterator>
bool parse(Iterator first, Iterator last) {
using qi::char_;
qi::rule< Iterator, std::string(), ascii::space_type > text;
using namespace boost::spirit::classic;
qi::rule<Iterator, std::string()> myword2 =
confix_p('"', *c_escape_ch_p, '"') ; // ERROR!
text = myword2;
bool r = qi::phrase_parse(first, last, text, ascii::space);
if (first != last)
return false;
return r;
}
}
int main () {
std::string s = "\"foo\"";
bool ok = client::parse(s.begin(), s.end());
std::cout << "OK? " << (ok ? "y" : "n") << std::endl;
return 0;
}
The compiler error is a failed static assert instantiated from the line with confix:
// Report invalid expression error as early as possible.
// If you got an error_invalid_expression error message here,
// then the expression (expr) is not a valid spirit qi expression.
BOOST_SPIRIT_ASSERT_MATCH(qi::domain, Expr);
So, it says it's not a valid expression. How is it used correctly?
P.S.: I'm using Boost 1.45.
You are trying to combine classic (old, V1, ...) boost::spirit::classic and (new, V2) boost::spirit::qi.
This is not going to work. The newer stuff is a complete, and incompatible, rewrite. See the 'Porting from Spirit 1.8.x' notes in the documentation.
As for the question on how to parse escaped C/C++ strings using boost::spirit::qi, the following article will be helpful:
Parsing Escaped String Input Using Spirit.Qi
Recently I started to play with boost.log, and bumped into an issue that if an unhanded exception is thrown no log messages are written to the log file. I am using rolling text files and auto-flash option is set on.
Here is the modified source from the samples:
#include <stdexcept>
#include <string>
#include <iostream>
#include <fstream>
#include <functional>
#include <boost/ref.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/barrier.hpp>
#include <boost/log/common.hpp>
#include <boost/log/filters.hpp>
#include <boost/log/formatters.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/utility/empty_deleter.hpp>
#include <boost/log/utility/record_ordering.hpp>
namespace logging = boost::log;
namespace attrs = boost::log::attributes;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace fmt = boost::log::formatters;
namespace keywords = boost::log::keywords;
using boost::shared_ptr;
using namespace boost::gregorian;
enum
{
LOG_RECORDS_TO_WRITE = 100,
LOG_RECORDS_TO_WRITE_BEFORE_EXCEPTION = 10,
THREAD_COUNT = 10
};
BOOST_LOG_DECLARE_GLOBAL_LOGGER(test_lg, src::logger_mt)
//! This function is executed in multiple threads
void thread_fun(boost::barrier& bar)
{
// Wait until all threads are created
bar.wait();
// Here we go. First, identify the thread.
BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::thread::id, boost::this_thread::get_id());
// Now, do some logging
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
{
BOOST_LOG(get_test_lg()) << "Log record " << i;
if(i > LOG_RECORDS_TO_WRITE_BEFORE_EXCEPTION)
{
BOOST_THROW_EXCEPTION(std::exception("unhandled exception"));
}
}
}
int main(int argc, char* argv[])
{
try
{
typedef sinks::synchronous_sink< sinks::text_file_backend > file_sink;
shared_ptr< file_sink > sink(new file_sink(
keywords::file_name = L"%Y%m%d_%H%M%S_%5N.log", // file name pattern
keywords::rotation_size = 10 * 1024 * 1024, // rotation size, in characters
keywords::auto_flush = true // make each log record flushed to the file
));
// Set up where the rotated files will be stored
sink->locked_backend()->set_file_collector(sinks::file::make_collector(
keywords::target = "log" // where to store rotated files
));
// Upon restart, scan the target directory for files matching the file_name pattern
sink->locked_backend()->scan_for_files();
sink->locked_backend()->set_formatter(
fmt::format("%1%: [%2%] [%3%] - %4%")
% fmt::attr< unsigned int >("Line #")
% fmt::date_time< boost::posix_time::ptime >("TimeStamp")
% fmt::attr< boost::thread::id >("ThreadID")
% fmt::message()
);
// Add it to the core
logging::core::get()->add_sink(sink);
// Add some attributes too
shared_ptr< logging::attribute > attr(new attrs::local_clock);
logging::core::get()->add_global_attribute("TimeStamp", attr);
attr.reset(new attrs::counter< unsigned int >);
logging::core::get()->add_global_attribute("Line #", attr);
// Create logging threads
boost::barrier bar(THREAD_COUNT);
boost::thread_group threads;
for (unsigned int i = 0; i < THREAD_COUNT; ++i)
threads.create_thread(boost::bind(&thread_fun, boost::ref(bar)));
// Wait until all action ends
threads.join_all();
return 0;
}
catch (std::exception& e)
{
std::cout << "FAILURE: " << e.what() << std::endl;
return 1;
}
}
Source is compiled under Visual Studio 2008. boost.log compiled for boost 1.40.
Any help is highly appreciated.
Check to see if the log file is in the current working directory of the process, rather than the specified file collector target directory ("log" in your sample code). Additionally, you will probably want to specify a directory for the sink "file_name" pattern.
As "JQ" notes, don't expect to see any logging post-exception.