I have written C++ code for capturing the various severity levels of messages. I have used https://github.com/gklingler/simpleLogger for this.
File simpleLogger.cpp
#include "simpleLogger.h"
#include <boost/log/core/core.hpp>
#include <boost/log/expressions/formatters/date_time.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/trivial.hpp>
#include <boost/core/null_deleter.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <fstream>
#include <ostream>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace attrs = boost::log::attributes;
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", logging::trivial::severity_level)
BOOST_LOG_GLOBAL_LOGGER_INIT(logger, src::severity_logger_mt) {
src::severity_logger_mt<boost::log::trivial::severity_level> logger;
// add attributes
logger.add_attribute("LineID", attrs::counter<unsigned int>(1)); // lines are sequentially numbered
logger.add_attribute("TimeStamp", attrs::local_clock()); // each log line gets a timestamp
// add a text sink
typedef sinks::synchronous_sink<sinks::text_ostream_backend> text_sink;
boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
// add a logfile stream to our sink
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(LOGFILE));
// add "console" output stream to our sink
sink->locked_backend()->add_stream(boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter()));
// specify the format of the log message
logging::formatter formatter = expr::stream
<< std::setw(7) << std::setfill('0') << line_id << std::setfill(' ') << " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - " << expr::smessage;
sink->set_formatter(formatter);
// only messages with severity >= SEVERITY_THRESHOLD are written
sink->set_filter(severity >= SEVERITY_THRESHOLD);
// "register" our sink
logging::core::get()->add_sink(sink);
return logger;
}
File simpleLogger.h
#ifndef simpleLogger_h__
#define simpleLogger_h__
#define BOOST_LOG_DYN_LINK // necessary when linking the boost_log library dynamically
#include <boost/log/trivial.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
// the logs are also written to LOGFILE
#define LOGFILE "logfile.log"
// just log messages with severity >= SEVERITY_THRESHOLD are written
#define SEVERITY_THRESHOLD logging::trivial::warning
// register a global logger
BOOST_LOG_GLOBAL_LOGGER(logger, boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level>)
// just a helper macro used by the macros below - don't use it in your code
#define LOG(severity) BOOST_LOG_SEV(logger::get(),boost::log::trivial::severity)
// ===== log macros =====
#define LOG_TRACE LOG(trace)
#define LOG_DEBUG LOG(debug)
#define LOG_INFO LOG(info)
#define LOG_WARNING LOG(warning)
#define LOG_ERROR LOG(error)
#define LOG_FATAL LOG(fatal)
#endif
File app.cpp
#include "simpleLogger.h"
int main() {
LOG_TRACE << "this is a trace message";
LOG_DEBUG << "this is a debug message";
LOG_WARNING << "this is a warning message";
LOG_ERROR << "this is an error message";
LOG_FATAL << "this is a fatal error message";
return 0;
}
So I would send my message as
LOG_INFO << "This is info message"
But this log message I need to send it to some other function as an argument. In that function I will be doing some other changes on the log message i.e "This is info message".
How to send the boost log message as an argument to function?
I didn't find relevant source for this.
Thanks in advance
You can define a "lazy actor" that you can put into a wrapped formatter expression. This is pretty much the rocket science of Boost Log, but perhaps this simple example will help you:
auto reverse_expr = [](auto fmt) {
return expr::wrap_formatter([fmt](logging::record_view const& rec, logging::formatting_ostream& strm) {
logging::formatting_ostream tmp;
std::string text;
tmp.attach(text);
fmt(rec, tmp);
std::reverse(text.begin(), text.end());
strm << text;
});
};
// specify the format of the log message
logging::formatter formatter = expr::stream
<< reverse_expr(expr::stream << std::setw(7) << std::setfill('0') << line_id)
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - "
<< reverse_expr(expr::stream << expr::smessage);
sink->set_formatter(formatter);
Results in:
3000000 | 2020-04-28, 16:15:15.779204 [warning] - egassem gninraw a si siht
4000000 | 2020-04-28, 16:15:15.779308 [error] - egassem rorre na si siht
5000000 | 2020-04-28, 16:15:15.779324 [fatal] - egassem rorre lataf a si siht
Notes: it will not be overly efficient because it involves "double buffering" with a temporary stream, but it is highly flexible, as you can see.
UPDATE Improved method below (see section BONUS)
A slightly more generic implementation might look like:
template <typename F> struct Xfrm {
Xfrm(F f) : _f(f) {}
F _f;
template <typename E> auto operator[](E fmt) const {
return expr::wrap_formatter(
[f=_f,fmt](logging::record_view const& rec, logging::formatting_ostream& strm) {
logging::formatting_ostream tmp;
std::string text;
tmp.attach(text);
fmt(rec, tmp);
strm << f(text);
});
}
};
So you can actually use it for other functions:
std::string reversed(std::string v) {
std::reverse(v.begin(), v.end());
return v;
}
std::string to_uppercase(std::string v) {
for (auto& ch : v) ch = std::toupper(ch);
return v;
}
Which you could then use like:
logging::formatter formatter = expr::stream
<< Xfrm(reversed)[expr::stream << std::setw(7) << std::setfill('0') << line_id]
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - "
<< Xfrm(to_uppercase)[ expr::stream << expr::smessage ];
Or put the manipulators in your header file:
static inline constexpr Xfrm UC{to_uppercase};
static inline constexpr Xfrm REV{reversed};
So you can use it pre-fab:
logging::formatter formatter = expr::stream
<< REV[ expr::stream << std::setw(7) << std::setfill('0') << line_id ]
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - "
<< UC[ expr::stream << expr::smessage ];
Output
3000000 | 2020-04-28, 16:36:46.184958 [warning] - THIS IS A WARNING MESSAGE
4000000 | 2020-04-28, 16:36:46.185034 [error] - THIS IS AN ERROR MESSAGE
5000000 | 2020-04-28, 16:36:46.185045 [fatal] - THIS IS A FATAL ERROR MESSAGE
Live Demo
Live On Wandbox
File simpleLogger.h
#ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_SIMPLELOGGER_H
#define _HOME_SEHE_PROJECTS_STACKOVERFLOW_SIMPLELOGGER_H
#pragma once
#define BOOST_LOG_DYN_LINK \
1 // necessary when linking the boost_log library dynamically
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/trivial.hpp>
// the logs are also written to LOGFILE
#define LOGFILE "logfile.log"
// just log messages with severity >= SEVERITY_THRESHOLD are written
#define SEVERITY_THRESHOLD logging::trivial::warning
// register a global logger
BOOST_LOG_GLOBAL_LOGGER(logger, boost::log::sources::severity_logger_mt<
boost::log::trivial::severity_level>)
// just a helper macro used by the macros below - don't use it in your code
#define LOG(severity) \
BOOST_LOG_SEV(logger::get(), boost::log::trivial::severity)
// ===== log macros =====
#define LOG_TRACE LOG(trace)
#define LOG_DEBUG LOG(debug)
#define LOG_INFO LOG(info)
#define LOG_WARNING LOG(warning)
#define LOG_ERROR LOG(error)
#define LOG_FATAL LOG(fatal)
#endif
File simpleLogger.cpp
#include "simpleLogger.h"
#include <boost/core/null_deleter.hpp>
#include <boost/log/core/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/expressions/formatters/char_decorator.hpp>
#include <boost/log/expressions/formatters/date_time.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/make_shared.hpp>
#include <boost/phoenix.hpp>
#include <boost/phoenix/function.hpp>
#include <boost/shared_ptr.hpp>
#include <fstream>
#include <ostream>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace attrs = boost::log::attributes;
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity",
logging::trivial::severity_level)
namespace /*extend locally*/ {
template <typename F> struct Xfrm {
constexpr Xfrm(F f) : _f(f) {}
F _f;
template <typename E> auto operator[](E fmt) const {
return expr::wrap_formatter(
[f = _f, fmt](logging::record_view const& rec,
logging::formatting_ostream& strm) {
logging::formatting_ostream tmp;
std::string text;
tmp.attach(text);
fmt(rec, tmp);
strm << f(text);
});
}
};
std::string reversed(std::string v) {
std::reverse(v.begin(), v.end());
return v;
}
std::string to_uppercase(std::string v) {
for (auto& ch : v) {
ch = std::toupper(ch);
}
return v;
}
inline constexpr Xfrm UC{ to_uppercase };
inline constexpr Xfrm REV{ reversed };
} // namespace
BOOST_LOG_GLOBAL_LOGGER_INIT(logger, src::severity_logger_mt) {
src::severity_logger_mt<boost::log::trivial::severity_level> logger;
// add attributes
logger.add_attribute("LineID", attrs::counter<unsigned int>(
1)); // lines are sequentially numbered
logger.add_attribute(
"TimeStamp", attrs::local_clock()); // each log line gets a timestamp
// add a text sink
using text_sink = sinks::synchronous_sink<sinks::text_ostream_backend>;
boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
// add a logfile stream to our sink
sink->locked_backend()->add_stream(
boost::make_shared<std::ofstream>(LOGFILE));
// add "console" output stream to our sink
sink->locked_backend()->add_stream(
boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter()));
// specify the format of the log message
logging::formatter formatter =
expr::stream
<< REV[expr::stream << std::setw(7) << std::setfill('0') << line_id]
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - " << UC[expr::stream << expr::smessage];
sink->set_formatter(formatter);
// only messages with severity >= SEVERITY_THRESHOLD are written
sink->set_filter(severity >= SEVERITY_THRESHOLD);
// "register" our sink
logging::core::get()->add_sink(sink);
return logger;
}
File test.cpp
#include "simpleLogger.h"
int main() {
LOG_TRACE << "this is a trace message";
LOG_DEBUG << "this is a debug message";
LOG_WARNING << "this is a warning message";
LOG_ERROR << "this is an error message";
LOG_FATAL << "this is a fatal error message";
return 0;
}
BONUS: More Efficient
To avoid double-buffering, we can avoid using the original formatter. This works best for known attributes:
struct OddEvenId {
void operator()(logging::record_view const& rec, logging::formatting_ostream& strm) const {
auto vr = line_id.or_throw()(rec);
if (!vr.empty()) {
strm << std::setw(4) << (vr.get<uint32_t>()%2? "Odd":"Even");
}
}
};
struct QuotedMessage {
void operator()(logging::record_view const& rec, logging::formatting_ostream& strm) const {
auto vr = expr::smessage.or_throw()(rec);
if (!vr.empty())
strm << std::quoted(vr.get<std::string>());
}
};
static inline auto oddeven_id = expr::wrap_formatter(OddEvenId{});
static inline auto qmessage = expr::wrap_formatter(QuotedMessage{});
Now we can simply say qmessage instead of expr::smessage to get the quoted message value:
Live On Wandbox
logging::formatter formatter = expr::stream
<< oddeven_id
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - " << qmessage;
Prints
Odd | 2020-04-28, 17:21:12.619565 [warning] - "this is a warning message"
Even | 2020-04-28, 17:21:12.619666 [error] - "this is an error message"
Odd | 2020-04-28, 17:21:12.619684 [fatal] - "this is a fatal error message"
Related
I have a code similar to this:
const auto jsonFormatter = boost::log::expressions::stream << boost::log::expressions::smessage;
I want to escape the message using nlohmann::json, something like:
nlohmann::json json{boost::log::expressions::smessage};
I can do following to convert boost::log::expressions::smessage to std::string:
std::stringstream ss;
ss << boost::log::expressions::smessage;
std::string message = ss.str();
nlohmann::json json{message};
, but I need to put it inside the formatter, because the
const auto jsonFormatter = boost::log::expressions::stream << nlohmann::json{boost::log::expressions::smessage};
can't convert the boost::log::expressions::smessage argument to any nlohmann::json constructor.
Any suggestions how to make it work?
Log formatters look like normal C++, but they are expression templates that compose deferred calleable that do the corresponding action.
Here's how you can make a wrapper expression that knows how to do this:
namespace {
struct as_json_t {
template <typename E> auto operator[](E fmt) const {
return expr::wrap_formatter(
[fmt](logging::record_view const& rec,
logging::formatting_ostream& strm) {
logging::formatting_ostream tmp;
std::string text;
tmp.attach(text);
fmt(rec, tmp);
strm << nlohmann::json{text};
});
}
};
inline constexpr as_json_t as_json;
} // namespace
Now you can make your formatter e.g.
logging::formatter formatter = expr::stream
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< logging::trivial::severity
<< " - " << as_json[expr::stream << expr::smessage]
;
ANd the result is e.g.
2021-01-15, 23:34:08.489173 error - ["this is an error message"]
Live Demo
Live On Wandbox
File simpleLogger.h
#ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_SIMPLELOGGER_H
#define _HOME_SEHE_PROJECTS_STACKOVERFLOW_SIMPLELOGGER_H
#pragma once
#define BOOST_LOG_DYN_LINK \
1 // necessary when linking the boost_log library dynamically
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/trivial.hpp>
// the logs are also written to LOGFILE
#define LOGFILE "logfile.log"
// just log messages with severity >= SEVERITY_THRESHOLD are written
#define SEVERITY_THRESHOLD logging::trivial::warning
// register a global logger
BOOST_LOG_GLOBAL_LOGGER(logger, boost::log::sources::severity_logger_mt<
boost::log::trivial::severity_level>)
// just a helper macro used by the macros below - don't use it in your code
#define LOG(severity) \
BOOST_LOG_SEV(logger::get(), boost::log::trivial::severity)
// ===== log macros =====
#define LOG_TRACE LOG(trace)
#define LOG_DEBUG LOG(debug)
#define LOG_INFO LOG(info)
#define LOG_WARNING LOG(warning)
#define LOG_ERROR LOG(error)
#define LOG_FATAL LOG(fatal)
#endif
File simpleLogger.cpp
#include "simpleLogger.h"
#include <boost/core/null_deleter.hpp>
#include <boost/log/core/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/expressions/formatters/char_decorator.hpp>
#include <boost/log/expressions/formatters/date_time.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <nlohmann/json.hpp>
#include <fstream>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace attrs = boost::log::attributes;
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", logging::trivial::severity_level)
namespace {
struct as_json_t {
template <typename E> auto operator[](E fmt) const {
return expr::wrap_formatter(
[fmt](logging::record_view const& rec,
logging::formatting_ostream& strm) {
logging::formatting_ostream tmp;
std::string text;
tmp.attach(text);
fmt(rec, tmp);
strm << nlohmann::json{text};
});
}
};
inline constexpr as_json_t as_json;
} // namespace
BOOST_LOG_GLOBAL_LOGGER_INIT(logger, src::severity_logger_mt) {
src::severity_logger_mt<boost::log::trivial::severity_level> logger;
// add attributes
logger.add_attribute("TimeStamp", attrs::local_clock()); // each log line gets a timestamp
// add a text sink
using text_sink = sinks::synchronous_sink<sinks::text_ostream_backend>;
boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
// add a logfile stream to our sink
sink->locked_backend()->add_stream(
boost::make_shared<std::ofstream>(LOGFILE));
// add "console" output stream to our sink
sink->locked_backend()->add_stream(
boost::shared_ptr<std::ostream>(&std::clog, boost::null_deleter()));
// specify the format of the log message
logging::formatter formatter = expr::stream
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< logging::trivial::severity
<< " - " << as_json[expr::stream << expr::smessage]
;
sink->set_formatter(formatter);
// only messages with severity >= SEVERITY_THRESHOLD are written
sink->set_filter(severity >= SEVERITY_THRESHOLD);
// "register" our sink
logging::core::get()->add_sink(sink);
return logger;
}
File test.cpp
#include "simpleLogger.h"
int main() {
LOG_TRACE << "this is a trace message";
LOG_DEBUG << "this is a debug message";
LOG_WARNING << "this is a warning message";
LOG_ERROR << "this is an error message";
LOG_FATAL << "this is a fatal error message";
return 0;
}
Prints
2021-01-15, 23:50:03.130250 warning - ["this is a warning message"]
2021-01-15, 23:50:03.130327 error - ["this is an error message"]
2021-01-15, 23:50:03.130354 fatal - ["this is a fatal error message"]
In addition to sehe's answer, you could achieve the JSON-like format using Boost.Log components. The essential part is the c_decor character decorator, which ensures that its output can be used as a C-style string literal.
namespace expr = boost::log::expressions;
const auto jsonFormatter =
expr::stream << "[\""
<< expr::c_decor[ expr::stream << expr::smessage ]
<< "\"]";
First, c_decor will escape any control characters in the messages to C-style escape sequences, like \n, \t. It will also escape double quote characters. Then, the surrounding brackets and double quotes are added to make the output compatible with JSON format.
If you have non-ASCII characters in your log messages and you want the formatted log records to be strictly ASCII, you can use c_ascii_decor instead of c_decor. In addition to what c_decor does, it will also escape any bytes greater than 127 to their hex escape sequences, e.g. \x8c.
I really appreciate help and the other answers, which may be better in other cases, but long story short, I tried a lot of solutions and went with this one in the end (I can update it if it can be improved):
#include <boost/phoenix/bind/bind_function.hpp>
..
nlohmann::json EscapeMessage(
boost::log::value_ref<std::string, boost::log::expressions::tag::smessage> const& message)
{
return message ? nlohmann::json(message.get()) : nlohmann::json();
}
..
const auto jsonFormatter = boost::log::expressions::stream << boost::phoenix::bind(&EscapeMessage, boost::log::expressions::smessage.or_none())
boost::log::add_console_log(std::cout, boost::log::keywords::format = jsonFormatter);
Assume there is only a single sink (for example a file) and couple of threads are writing their logs on that.
I've searched a lot but unfortunately could not find a good example.
I've tried multiple channels per threads and got crash.
#include <iostream>
#include <boost/log/expressions.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/sinks.hpp>
#include <thread>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace keywords = boost::log::keywords;
namespace sinks = boost::log::sinks;
// We define our own severity levels
enum severity_level
{
normal,
notification,
warning,
error,
critical
};
// Define the attribute keywords
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)
std::ostream& operator<< (std::ostream& strm, severity_level level)
{
static const char* strings[] =
{
"normal",
"notification",
"warning",
"error",
"critical"
};
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
strm << strings[level];
else
strm << static_cast< int >(level);
return strm;
}
void init()
{
// Create a minimal severity table filter
typedef expr::channel_severity_filter_actor< std::string, severity_level > min_severity_filter;
min_severity_filter min_severity = expr::channel_severity_filter(channel, severity);
// Set up the minimum severity levels for different channels
min_severity["general"] = notification;
min_severity["network"] = warning;
min_severity["gui"] = error;
logging::add_file_log
(
keywords::file_name = "sample_%N.log",
keywords::rotation_size = 1000 * 1024 * 1024,
keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
keywords::filter = min_severity || severity >= critical,
keywords::format =
(
expr::stream
<< line_id
<< ": <" << severity
<< "> [" << channel << "] "
<< expr::smessage
)
);
logging::add_console_log
(
std::clog,
keywords::filter = min_severity || severity >= critical,
keywords::format =
(
expr::stream
<< line_id
<< ": <" << severity
<< "> [" << channel << "] "
<< expr::smessage
)
);
// Define our logger type
typedef src::severity_channel_logger< severity_level, std::string > logger_type;
void test_logging(logger_type lg, std::string const channel_name)
{
for(int i=0; i<1000; i++) {
BOOST_LOG_CHANNEL_SEV(lg, channel_name, normal) << "A normal severity level message";
BOOST_LOG_CHANNEL_SEV(lg, channel_name, notification) << "A notification severity level message";
BOOST_LOG_CHANNEL_SEV(lg, channel_name, warning) << "A warning severity level message";
BOOST_LOG_CHANNEL_SEV(lg, channel_name, error) << "An error severity level message";
BOOST_LOG_CHANNEL_SEV(lg, channel_name, critical) << "A critical severity level message";
}
}
int main(int, char*[])
{
init();
logging::add_common_attributes();
logger_type lg;
std::thread t1(test_logging, lg, "general");
std::thread t2(test_logging, lg, "network");
std::thread t3(test_logging, lg, "gui");
std::thread t4(test_logging, lg, "filesystem");
getchar();
return 0;
}
I try to set dynamic date for my boost multi-file logger to separate log files for everyday in different files like this :'log___2018-07-10__172.17.18.199.log'.
I don't want to set the date in each functions in my code a BOOST_LOG_SCOPED_THREAD_TAG!
I want the boost core logger to do that and I just want to set local clock in global attributes, but I do NOT know how to do that.
Here is me code:
#include <boost/shared_ptr.hpp>
#include <boost/thread/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/common.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_multifile_backend.hpp>
#include <boost/log/support/date_time.hpp>
enum SeverityLevel {
TRACE,
DEBUG,
WARNING,
INFO,
ERROR,
FATAL
};
enum
{
THREAD_COUNT = 5,
LOG_RECORDS_TO_WRITE = 10
};
boost::log::sources::severity_logger_mt< SeverityLevel > myLogger;
#define LOG_ERROR BOOST_LOG_SEV(myLogger, ERROR)
#define LOG_FATAL BOOST_LOG_SEV(myLogger, FATAL)
#define LOG_INFO BOOST_LOG_SEV(myLogger, INFO)
#define LOG_WARNING BOOST_LOG_SEV(myLogger, WARNING)
#define LOG_DEBUG BOOST_LOG_SEV(myLogger, DEBUG)
#define LOG_TRACE BOOST_LOG_SEV(myLogger, TRACE)
// This function is executed in a separate thread
void thread_fooMain()
{
BOOST_LOG_NAMED_SCOPE("thread_fooMain");
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
{
LOG_ERROR << "Log record " << i;
}
}
void thread_foo2()
{
BOOST_LOG_SCOPED_THREAD_TAG("TerminalIP", "172.17.18.102");
BOOST_LOG_NAMED_SCOPE("thread_fooMain");
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
{
LOG_TRACE << "Log record " << i;
}
}
void thread_foo3()
{
BOOST_LOG_SCOPED_THREAD_TAG("TerminalIP", "172.17.18.103");
BOOST_LOG_NAMED_SCOPE("thread_fooMain");
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
{
LOG_INFO << "Log record " << i;
}
}
void thread_foo4()
{
BOOST_LOG_SCOPED_THREAD_TAG("TerminalIP", "172.17.18.104");
BOOST_LOG_NAMED_SCOPE("thread_fooMain");
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
{
LOG_INFO << "Log record " << i;
}
}
void thread_foo5()
{
BOOST_LOG_SCOPED_THREAD_TAG("TerminalIP", "172.17.18.105");
BOOST_LOG_NAMED_SCOPE("thread_fooMain");
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
{
LOG_INFO << "Log record " << i;
}
}
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", SeverityLevel)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "Timestamp", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(dateStream, "DateStream", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(thread_id, "ThreadID", boost::log::attributes::current_thread_id::value_type)
// The operator puts a human-friendly representation of the severity level to the stream
std::ostream & operator<< (std::ostream & strm, SeverityLevel level)
{
static const char* strings[] =
{
"TRACE",
"DEBUG",
"WARNING",
"INFO",
"ERROR",
"FATAL"
};
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
strm << strings[level];
else
strm << static_cast< int >(level);
return strm;
}
int main(int argc, char* argv[])
{
try
{
// Create a text file sink
typedef boost::log::sinks::synchronous_sink< boost::log::sinks::text_multifile_backend > file_sink;
boost::shared_ptr< file_sink > sink(new file_sink);
// Set up how the file names will be generated
sink->locked_backend()->set_file_name_composer(
boost::log::sinks::file::as_file_name_composer(
boost::log::expressions::stream <<
"log/log_" <<
boost::log::expressions::format_date_time(dateStream, "__%Y_%m_%d__") <<
boost::log::expressions::attr< std::string >("TerminalIP") <<
".log"
)
);
sink->set_formatter
(
boost::log::expressions::stream <<
boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "[%Y/%m/%d %H:%M:%S.%f]") <<
"<" << severity << ">" <<
"[" << thread_id << "]:" <<
"<" << boost::log::expressions::format_named_scope("Scope", boost::log::keywords::format = "%n") << "> " <<
boost::log::expressions::smessage
);
// Add it to the core
boost::log::core::get()->add_sink(sink);
boost::log::core::get()->set_filter
(
severity >= WARNING
);
// Add some attributes too
boost::log::core::get()->add_global_attribute("TimeStamp", boost::log::attributes::local_clock());
boost::log::core::get()->add_global_attribute("DateStream", boost::log::attributes::local_clock());
boost::log::core::get()->add_global_attribute("Scope", boost::log::attributes::named_scope());
boost::log::core::get()->add_global_attribute("ThreadID", boost::log::attributes::current_thread_id());
// Create threads and make some logs
boost::thread_group threads;
threads.create_thread(&thread_fooMain);
threads.create_thread(&thread_foo2);
threads.create_thread(&thread_foo3);
threads.create_thread(&thread_foo4);
threads.create_thread(&thread_foo5);
threads.join_all();
return 0;
}
catch (std::exception& e)
{
std::cout << "FAILURE: " << e.what() << std::endl;
return 1;
}
}
but got this linker error:
ErrorC2664 'boost::log::v2s_mt_nt6::aux::light_function<void (boost::log::v2s_mt_nt6::basic_formatting_ostream<char,std::char_traits<char>,std::allocator<char>> &,const boost::posix_time::ptime &)>::result_type boost::log::v2s_mt_nt6::aux::light_function<void (boost::log::v2s_mt_nt6::basic_formatting_ostream<char,std::char_traits<char>,std::allocator<char>> &,const boost::posix_time::ptime &)>::operator ()(boost::log::v2s_mt_nt6::basic_formatting_ostream<char,std::char_traits<char>,std::allocator<char>> &,const boost::posix_time::ptime &) const':
cannot convert argument 1 from 'boost::log::v2s_mt_nt6::basic_formatting_ostream<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>' to 'boost::log::v2s_mt_nt6::basic_formatting_ostream<char,std::char_traits<char>,std::allocator<char>> &' sample.boost.asio e:\programming\c++\boost libraries\boost_1_66_0\boost\log\utility\functional\bind.hpp 93
Can anyone explain to me why this happened?
In Windows
boost::log::expressions::format_date_time<boost::posix_time::ptime,wchar_t>("TimeStamp",L"__%Y_%m_%d__")
In Unix
boost::log::expressions::format_date_time<boost::posix_time::ptime>("TimeStamp","__%Y_%m_%d__")
See text_multifile_backend.hpp
The essential is typedef filesystem::path result_type;
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I have written an application in c++ with OpenCV and boost and it is running well and correct, but before exiting it crashes (if I debug step by step, it crashes when return 0; at the end of main) saying:
in kdevelop:
*** Error in `/home/xxx/git_repos/my_proj/build/my_proj': free(): invalid pointer: 0x00000000008939c0 ***
*** Crashed with return code: 0 ***
in command line:
*** Error in `./my_proj': free(): invalid pointer: 0x00000000008939c0 ***
Aborted (core dumped)
if I use gdb:
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
*** Error in `/home/xxx/git_repos/my_proj/build/plate_info_extractor': free(): invalid pointer: 0x00000000008939c0 ***
Program received signal SIGABRT, Aborted.
0x00007ffff5b83cc9 in __GI_raise (sig=sig#entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
56 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
Any help please? I cannot figure out what is wrong.
I have commented some code parts and it seems that the problem is in the code that I use to log the errors/info... So:
#include "CLogger.hpp"
static void help(const std::string& appNameIn)
{
std::cerr << "Usage: " << appNameIn << " <option(s)> SOURCES" << std::endl
<< "Options:" << std::endl
<< " -h,--help Show this help message" << std::endl
<< " -c,--config CONFIG FILE Specify the name and path to the config file" << std::endl
<< std::endl
<< std::endl;
}
enum RetVals
{
NO_ERRORS,
NOT_ENOUGH_PARAMETERS,
UNKNOWN_OPTION,
SHOW_USAGE,
UNKNOWN_EXCEPTION,
OTHER_EXCEPTION,
};
int main(int argc, char **argv)
{
Logger::initFilesList();
Logger::initLogging();
BoostLogger lg = setClassNameAttribute("main");
if (argc == 3)
{
std::string opt = argv[1];
if (opt == "-c" || opt == "--config")
{
try
{
std::cout << "running" << std::endl;
}
catch (std::exception& e)
{
BOOST_LOG_SEV(lg, Logger::SeverityLevels::error) << "Other exception: " << e.what();
return OTHER_EXCEPTION;
}
catch (...)
{
BOOST_LOG_SEV(lg, Logger::SeverityLevels::error) << "Unkown exception ...";
return UNKNOWN_EXCEPTION;
}
}
else
{
help(argv[0]);
BOOST_LOG_SEV(lg, Logger::SeverityLevels::debug) << "The options are -c or --config, -h or --help";
return UNKNOWN_OPTION;
}
}
else if (argc == 2 && (std::string(argv[1]) == "-h" || std::string(argv[1]) == "--help"))
{
help(argv[0]);
BOOST_LOG_SEV(lg, Logger::SeverityLevels::debug) << "Call help function";
return SHOW_USAGE;
}
else
{
help(argv[0]);
BOOST_LOG_SEV(lg, Logger::SeverityLevels::debug) << "The number of input parameters is wrong";
return NOT_ENOUGH_PARAMETERS;
}
return NO_ERRORS;
}
and the CLogger.hpp:
#pragma once
#include <list>
#include <string>
#include <boost/log/common.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/concept_check.hpp>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
class Logger
{
private:
static std::list< std::string > sm_logFilesList;
static const std::string sm_folderOfLogFiles;
/** the function adds the input log file to the list
*
* #param logFilePathIn : input log file path
*/
static void addNewLogFileToList(const std::string& logFilePathIn, bool sorting = false);
/** The function adds new logs to new log file and remove the oltest ones to have always 2 log files */
static void addNewRemoveOldLogs(bool sorting = false);
/** #returns : if the input path is valid or not */
static bool checkPath(const boost::filesystem::path& pathIn);
/** #returns if the file with the first input path was more recent accesed than the file with the second input path
*/
static bool compareAccessTime(const std::string& path1In, const std::string& path2In);
/** The function remove the old log files and keeps just the most recent two */
static void removeOldLogFiles();
/** The function is calles at the end of each file and removes the old files keeping just the last two */
static void openingHandler(boost::log::sinks::text_file_backend::stream_type& file);
public:
enum SeverityLevels
{
debug,
info,
warning,
error
};
/** The function sets the sm_logFilesList to contain the files if there are log files from earlier runs */
static void initFilesList();
/** The fuction is initializing the sinks and the formatter of the logger */
static void initLogging();
};
typedef boost::log::sources::severity_logger< Logger::SeverityLevels > BoostLogger;
/** the overloaded << operator of the BoostLogger */
std::ostream& operator<< (std::ostream& strm, Logger::SeverityLevels level);
/** The function sets the logger attribute ClassName to the specified string and returns the initialized logger
*
* #param classNameIn : input string name of the class
*
* #returns : initialized BoostLogger
*/
BoostLogger setClassNameAttribute(const std::string& classNameIn);
and the implementation:
#include "CLogger.hpp"
#include <boost/log/attributes.hpp>
#include <boost/log/attributes/current_process_id.hpp>
#include <boost/log/attributes/current_process_name.hpp>
#include <boost/log/core/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/utility/setup/file.hpp>
typedef boost::log::sinks::synchronous_sink< boost::log::sinks::text_file_backend > SinkTextFileBakend;
BOOST_LOG_ATTRIBUTE_KEYWORD(correlid, "CorrelationID", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", Logger::SeverityLevels)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(threadid, "ThreadID", boost::log::attributes::current_thread_id)
BOOST_LOG_ATTRIBUTE_KEYWORD(classname, "ClassName", std::string)
std::list< std::string > Logger::sm_logFilesList;
const std::string Logger::sm_folderOfLogFiles = "../logs/";
std::ostream& operator<<(std::ostream& strm, Logger::SeverityLevels level)
{
static const char* strings[] =
{
"DEBUG",
"INFO",
"WARN",
"ERROR"
};
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
strm << strings[level];
else
strm << static_cast< int >(level);
return strm;
}
BoostLogger setClassNameAttribute(const std::string& classNameIn)
{
BoostLogger lg;
lg.add_attribute("ClassName", boost::log::attributes::constant<std::string>(classNameIn));
return lg;
}
void Logger::removeOldLogFiles()
{
while (sm_logFilesList.size() > 2)
{
std::string fileToDelete = sm_logFilesList.front();
if (fs::exists(fs::path(fileToDelete)))
{
fs::remove(fs::path(fileToDelete));
sm_logFilesList.pop_front();
}
}
}
bool Logger::compareAccessTime(const std::string& path1In, const std::string& path2In)
{
return (fs::last_write_time(fs::path(path1In)) < fs::last_write_time(fs::path(path2In)));
}
void Logger::addNewLogFileToList(const std::string& logFilePathIn, bool sorting)
{
if (std::find(sm_logFilesList.begin(), sm_logFilesList.end(), logFilePathIn) == sm_logFilesList.end())
{
sm_logFilesList.push_back(logFilePathIn);
if (sorting)
{
sm_logFilesList.sort(compareAccessTime);
}
removeOldLogFiles();
}
}
void Logger::addNewRemoveOldLogs(bool sorting)
{
fs::path path(sm_folderOfLogFiles);
fs::directory_iterator endDir;
if (checkPath(path))
{
for (fs::directory_iterator it(path); it != endDir; it++)
{
if (fs::is_regular_file(it->status()))
{
if (fs::extension(*it) == ".log")
{
std::string fileToPush = it->path().string();
addNewLogFileToList(fileToPush, sorting);
}
}
}
}
}
bool Logger::checkPath(const boost::filesystem::path& pathIn)
{
if (!fs::exists(pathIn))
{
return false;
}
if (!fs::is_directory(pathIn))
{
return false;
}
return true;
}
void Logger::openingHandler(boost::log::sinks::text_file_backend::stream_type& file)
{
addNewRemoveOldLogs();
}
void Logger::initFilesList()
{
addNewRemoveOldLogs(true);
}
void Logger::initLogging()
{
// Create a backend
boost::shared_ptr< SinkTextFileBakend > sink = boost::log::add_file_log(
sm_folderOfLogFiles + "plate_info_extractor_%Y-%m-%d_%H-%M-%S.%N.log",
boost::log::keywords::format = boost::log::expressions::stream
<< boost::log::expressions::attr< boost::log::attributes::current_process_name::value_type >("Executable") << "("
<< boost::log::expressions::attr< boost::log::attributes::current_process_id::value_type >("ExeUID") << ") CID("
<< correlid << ") " << severity << "["
<< boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
<< "] [" << boost::log::expressions::attr< boost::log::attributes::current_thread_id::value_type >("ThreadID")
<< "] " << classname << " - " << boost::log::expressions::smessage,
boost::log::keywords::open_mode = (std::ios::out | std::ios::app),
boost::log::keywords::rotation_size = 2 * 1024 * 1024,
boost::log::keywords::auto_flush = true
);
sink->locked_backend()->set_open_handler(&openingHandler);
boost::log::core::get()->set_filter(severity >= SeverityLevels::info);
boost::log::core::get()->add_sink(sink);
#ifdef DEBUGGING
boost::shared_ptr< boost::log::sinks::text_ostream_backend > backend =
boost::make_shared< boost::log::sinks::text_ostream_backend >();
backend->add_stream(
boost::shared_ptr< std::ostream >(&std::cout));
// Enable auto-flushing after each log record written
backend->auto_flush(true);
// Wrap it into the frontend and register in the core.
// The backend requires synchronization in the frontend.
// for testing and printing log in sysout
boost::shared_ptr< boost::log::sinks::synchronous_sink< boost::log::sinks::text_ostream_backend > > backend_sink(
new boost::log::sinks::synchronous_sink< boost::log::sinks::text_ostream_backend >(backend));
backend_sink->set_formatter(
boost::log::expressions::stream
// line id will be written in hex, 8-digits, zero-filled
<< boost::log::expressions::attr< boost::log::attributes::current_process_name::value_type >("Executable")
<< "(" << boost::log::expressions::attr< boost::log::attributes::current_process_name::value_type >("ExeUID")
<< ") CID(" << correlid << ") " << severity << " ["
<< boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
<< "] [" << boost::log::expressions::attr< boost::log::attributes::current_thread_id::value_type >("ThreadID")
<< "] " << classname << " - " << boost::log::expressions::smessage);
boost::log::core::get()->add_sink(backend_sink); // for testing and printing log in sysout
#endif
}
I hope that now there is enough information :)
This line:
backend->add_stream(boost::shared_ptr< std::ostream >(&std::cout));
looks pretty disastrous me. Your program will attempt to delete std::cout when exiting. I'm not familiar with Boost Log, so I don't know how to properly set up a sink for std::cout.
As Arpegius pointed out you should use the null_deleter to avoid deletion. Something along the lines of this:
backend->add_stream(boost::shared_ptr< std::ostream>(&std::cout, boost::null_deleter()));
I'm using Boost(1.55.0) Logging in my C++ application.
I have been able to generate log of this format
[2014-Jul-15 10:47:26.137959]: <debug> A regular message
I want to be able to add source file name and line number where
the log is generated.
[2014-Jul-15 10:47:26.137959]: <debug> [filename:line_no] A regular message
example:
[2014-Jul-15 10:47:26.137959]: <debug> [helloworld.cpp : 12] A regular message
Source Code:
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/attributes/attribute.hpp>
#include <boost/log/attributes/attribute_cast.hpp>
#include <boost/log/attributes/attribute_value.hpp>
#include <boost/make_shared.hpp>
#include <boost/property_tree/ptree.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;
void init()
{
logging::add_file_log
(
keywords::file_name = "sample_%N.log", /*< file name pattern >*/
keywords::rotation_size = 10*1024*1204, /*< rotate files every 10 MiB... >*/
keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0), /*< ...or at midnight >*/
keywords::format =
(
boost::log::expressions::stream
<< boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d_%H:%M:%S.%f")
<< ": <" << boost::log::trivial::severity << "> "
<< boost::log::expressions::smessage
)
);
}
int main(int, char*[])
{
init();
logging::add_common_attributes();
using namespace logging::trivial;
src::severity_logger< severity_level > lg;
BOOST_LOG_SEV(lg, debug) << "A regular message";
return 0;
}
As Horus pointed out, you can use attributes to log file and line numbers. However it is best to avoid using multi-statements macros to avoid problems with expressions like this:
if (something)
LOG_FILE_LINE(debug) << "It's true"; // Only the first statement is conditional!
You can do better creating a macro that leverages the underlying behavior of the Boost Log library. For example, BOOST_LOG_SEV is:
#define BOOST_LOG_SEV(logger, lvl) BOOST_LOG_STREAM_SEV(logger, lvl)
#define BOOST_LOG_STREAM_SEV(logger, lvl)\
BOOST_LOG_STREAM_WITH_PARAMS((logger), (::boost::log::keywords::severity = (lvl)))
Using BOOST_LOG_STREAM_WITH_PARAMS you can set and get more attributes, like this:
// New macro that includes severity, filename and line number
#define CUSTOM_LOG(logger, sev) \
BOOST_LOG_STREAM_WITH_PARAMS( \
(logger), \
(set_get_attrib("File", path_to_filename(__FILE__))) \
(set_get_attrib("Line", __LINE__)) \
(::boost::log::keywords::severity = (boost::log::trivial::sev)) \
)
// Set attribute and return the new value
template<typename ValueType>
ValueType set_get_attrib(const char* name, ValueType value) {
auto attr = logging::attribute_cast<attrs::mutable_constant<ValueType>>(logging::core::get()->get_thread_attributes()[name]);
attr.set(value);
return attr.get();
}
// Convert file path to only the filename
std::string path_to_filename(std::string path) {
return path.substr(path.find_last_of("/\\")+1);
}
The complete source code is:
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/attributes/mutable_constant.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/attributes/mutable_constant.hpp>
namespace logging = boost::log;
namespace attrs = boost::log::attributes;
namespace expr = boost::log::expressions;
namespace src = boost::log::sources;
namespace keywords = boost::log::keywords;
// New macro that includes severity, filename and line number
#define CUSTOM_LOG(logger, sev) \
BOOST_LOG_STREAM_WITH_PARAMS( \
(logger), \
(set_get_attrib("File", path_to_filename(__FILE__))) \
(set_get_attrib("Line", __LINE__)) \
(::boost::log::keywords::severity = (boost::log::trivial::sev)) \
)
// Set attribute and return the new value
template<typename ValueType>
ValueType set_get_attrib(const char* name, ValueType value) {
auto attr = logging::attribute_cast<attrs::mutable_constant<ValueType>>(logging::core::get()->get_thread_attributes()[name]);
attr.set(value);
return attr.get();
}
// Convert file path to only the filename
std::string path_to_filename(std::string path) {
return path.substr(path.find_last_of("/\\")+1);
}
void init() {
// New attributes that hold filename and line number
logging::core::get()->add_thread_attribute("File", attrs::mutable_constant<std::string>(""));
logging::core::get()->add_thread_attribute("Line", attrs::mutable_constant<int>(0));
logging::add_file_log (
keywords::file_name = "sample.log",
keywords::format = (
expr::stream
<< expr::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d_%H:%M:%S.%f")
<< ": <" << boost::log::trivial::severity << "> "
<< '[' << expr::attr<std::string>("File")
<< ':' << expr::attr<int>("Line") << "] "
<< expr::smessage
)
);
logging::add_common_attributes();
}
int main(int argc, char* argv[]) {
init();
src::severity_logger<logging::trivial::severity_level> lg;
CUSTOM_LOG(lg, debug) << "A regular message";
return 0;
}
This generate a log like this:
2015-10-15_15:25:12.743153: <debug> [main.cpp:61] A regular message
As user2943014 pointed out, using scopes prints the line number where you opened that scope, not the line number where you emitted a log message using BOOST_LOG_SEV.
You can use attributes to log the line numbers etc at the place you actually logged.
Register global attributes in your logging initialization function:
using namespace boost::log;
core::get()->add_global_attribute("Line", attributes::mutable_constant<int>(5));
core::get()->add_global_attribute("File", attributes::mutable_constant<std::string>(""));
core::get()->add_global_attribute("Function", attributes::mutable_constant<std::string>(""));
Setting these attributes in your logging macro:
#define logInfo(methodname, message) \
LOG_LOCATION; \
BOOST_LOG_SEV(_log, boost::log::trivial::severity_level::trace) << message
#define LOG_LOCATION \
boost::log::attribute_cast<boost::log::attributes::mutable_constant<int>>(boost::log::core::get()->get_global_attributes()["Line"]).set(__LINE__); \
boost::log::attribute_cast<boost::log::attributes::mutable_constant<std::string>>(boost::log::core::get()->get_global_attributes()["File"]).set(__FILE__); \
boost::log::attribute_cast<boost::log::attributes::mutable_constant<std::string>>(boost::log::core::get()->get_global_attributes()["Function"]).set(__func__);
Not exactly beautiful, but it works and it was a long way for me. It's a pity boost doesn't offer this feature out of the box.
I would suggest to use the boost::log::add_value() function.
Define:
#define LOG_LOCATION(LEVEL, MSG) \
BOOST_LOG_SEV(logger::get(), LEVEL) \
<< boost::log::add_value("Line", __LINE__) \
<< boost::log::add_value("File", __FILE__) \
<< boost::log::add_value("Function", __FUNCTION__) << MSG
And then you can format it as follows:
boost::log::add_common_attributes();
boost::log::register_simple_filter_factory<boost::log::trivial::severity_level, char>("Severity");
boost::log::register_simple_formatter_factory<boost::log::trivial::severity_level, char>("Severity");
auto syslog_format(
boost::log::expressions::stream <<
"[" << boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S") <<
"] [" << boost::log::expressions::attr<boost::log::attributes::current_thread_id::value_type>("ThreadID") <<
"] [" << std::left << std::setw(7) << std::setfill(' ') << boost::log::trivial::severity <<
"] " << boost::log::expressions::smessage <<
" (" << boost::log::expressions::attr<std::string>("Filename") <<
":" << boost::log::expressions::attr<int>("Line") <<
":" << boost::log::expressions::attr<std::string>("Function") <<
")"
);
boost::log::add_file_log(
boost::log::keywords::file_name = "sys_%d_%m_%Y.%N.log",
boost::log::keywords::format = syslog_format
);
No need to add global attributes, and you can format it easily as seen above. I find this is a good compromise between other's solutions and the raw __FILE__ __LINE__ approach.
Full example here.
Define
namespace attrs = boost::logging::attributes;
namespace expr = boost::logging::expressions;
Add
<< expr::format_named_scope("Scope", keywords::format = "[%f:%l]")
to your keywords::format = (...) in init.
Then add
logging::core::get()->add_global_attribute("Scope", attrs::named_scope());
after add_common_attributes() in main.
Then just before the BOOST_LOG_SEV line add BOOST_LOG_NAMED_SCOPE("whatever").
BOOST_LOG_NAMED_SCOPE("whatever") creates a "scope" named "whatever". The scope is implemented by a unused variable that contains the scope name and the file and the line where the scope was defined.
The format_named_scope line specifies how a scope should be formatted in the log line. %f is the file, %l is the line and %n is the scope name.
Note that the file line that appears in the log record is the line where the macro BOOST_LOG_NAMED_SCOPE appears and not the line of the BOOST_LOG_SEV macro.
I am not aware of simple method to record the file and line without using the BOOST_LOG_NAMED_SCOPE.