I trying to use Boost.log library from Andrey Semashev
I have some problem with multithreaded-logging with settings file.
Code of main program:
namespace logging = boost::log;
namespace attrs = boost::log::attributes;
namespace src = boost::log::sources;
using boost::shared_ptr;
// Here we define our application severity levels.
enum severity_level
{
normal,
notification,
warning,
error,
critical
};
BOOST_LOG_DECLARE_GLOBAL_LOGGER(test_lg, src::severity_logger_mt<>)
void try_logging( std::string c )
{
BOOST_LOG_SCOPED_THREAD_TAG("Tag", std::string, c.c_str());
src::severity_logger_mt<>& lg = get_test_lg();
BOOST_LOG_SEV(lg, normal) << "This is a normal severity record";
BOOST_LOG_SEV(lg, notification) << "This is a notification severity record";
BOOST_LOG_SEV(lg, warning) << "This is a warning severity record";
BOOST_LOG_SEV(lg, error) << "This is a error severity record";
BOOST_LOG_SEV(lg, critical) << "This is a critical severity record";
}
int main(int argc, char* argv[])
{
try
{
// Open the file
std::ifstream settings("settings.txt");
if (!settings.is_open())
{
std::cout << "Could not open settings.txt file" << std::endl;
return 1;
}
// Read the settings and initialize logging library
logging::init_from_stream(settings);
shared_ptr< logging::attribute > attr(new attrs::local_clock);
logging::core::get()->add_global_attribute("TimeStamp", attr);
boost::thread_group g;
for (char i = 'a'; i < 'z'; i++)
g.create_thread(boost::bind(&try_logging, std::string("aaa") + i));
g.join_all();
system("PAUSE");
return 0;
}
catch (std::exception& e)
{
std::cout << "FAILURE: " << e.what() << std::endl;
return 1;
}
}
Settings file:
[Core]
Filter="%Severity% >= 2"
[Sink:2]
Destination=TextFile
FileName=test.log
AutoFlush=true
Format="[%TimeStamp%] %Tag% %_%"
Asynchronous=1
Filter="%Tag% = aaab"
[Sink:3]
Filter="%Tag% = aaaa"
Asynchronous=1
Destination=TextFile
FileName=test.log
AutoFlush=true
Format="%Tag% %_%"
As you see, i setup filtering: i want to output just messages from aaab, aaaa threads.
But result file is very bad:
[2011-Jul-18 11:32:58.078192] aaab This aaaa This is a error severity [2011-Juaaaa This is a critical severity record
r severity record
[2011-Jul-18 11:32:58.437581] aaab This is a critical severity record
One message override other! As I understood - there's no synchronyzing between multiple threads.
If i remove filtering - result is all rigth.
How i can fix it? If it's possible - with config file, not code.
Thank you.
Related
I want to setup only one Boost Log V2 sink (Boost 1.75.0) to filter out few lines from the log records producing the application.
The goal would be to have a log file containing only these lines (e.g.: users who logged in), while another sink logs out every records
I have tried to configure the filter property of the sink in the configuration file to match to a tag presence, like this:
[Sinks.FileSink]
Destination=TextFile
Filter="%USER%"
FileName="log.log"
MaxSize=10000000
Format="[%TimeStamp%] - %Message%"
Asynchronous=false
AutoFlush=true
Where the %USER% tag would be the filter criteria, if it is present, log the line, otherwise not...
I tried to add as value attribute:
... << boost::log::add_value("USER", true) << "message..."
and also register it before logger from file:
boost::log::core::get()->add_global_attribute("USER", boost::log::attributes::mutable_constant(true));
boost::log::init_from_stream(config);
None of them work
For me, the strange behavioral was that filter pattern works if I filter for severity, like this: Filter="%Severity%"
Can anybody help me how to filter only really few lines from application and forward them into file?
Thank you
Update:
a minimal example that wants to demonstrator what I want to achieve:
#include <boost/log/utility/setup/from_stream.hpp>
#include <boost/log/attributes/mutable_constant.hpp>
int main(int argc, char *argv_char[]) {
std::stringstream config;
config << R"([Core])" << std::endl;
config << R"(DisableLogging=false)" << std::endl;
config << R"(Filter="%Severity% >= trace")" << std::endl;
config << R"([Sinks.Sink])" << std::endl;
config << R"(Destination=Console)" << std::endl;
//config << R"(Filter="%CMD% = \"YES\"")" << std::endl;
config << R"(Filter="%CMD% = \"NO\"")" << std::endl;
config << R"(FileName="commands.log")" << std::endl;
config << R"(Format="%Message%")" << std::endl;
config << R"(Asynchronous=false)" << std::endl;
config << R"(AutoFlush=true)" << std::endl;
boost::log::core::get()->add_global_attribute("CMD", boost::log::attributes::mutable_constant<std::string>("NO"));
boost::log::init_from_stream(config);
BOOST_LOG_TRIVIAL(debug) << "Non-visible message";
BOOST_LOG_TRIVIAL(debug) << boost::log::add_value("CMD", "YES") << "Visible message";
return 0;
}
If I replace the filter condition to %CMD% = "YES", the desired log line is missing on the output
Update 2:
// Codes above didn't changed
boost::log::init_from_stream(config);
boost::log::sources::logger logger;
logger.add_attribute("CMD", boost::log::attributes::constant<std::string>("YES"));
BOOST_LOG(logger) << "A log message from logger";
boost::log::sources::logger loggerScoped;
BOOST_LOG_SCOPED_LOGGER_ATTR(loggerScoped, "CMD", boost::log::attributes::constant<std::string>("YES"));
BOOST_LOG(loggerScoped) << "A log message from loggerScoped";
BOOST_LOG_TRIVIAL(debug) << "Non-visible message";
BOOST_LOG_TRIVIAL(debug) << boost::log::add_value("CMD", "YES") << "Visible message";
return 0;
As stated in the documentation, add_value manipulator has no effect on filtering as it is being executed after filtering is done. In your code sample, you're setting a global string attribute "CMD" with a value of "NO", and that is what is being tested by the filters for both log records. When you change the filter to require the value of "YES", both log records get suppressed.
If you want to selectively disable some log records, you should use other means for registering the attribute, so that it is in effect at the point of filtering. For example, you could add the attribute to a logger (by calling add_attribute on the logger) and use that logger to emit log records you want to suppress. Or you could use scoped attributes to mark log records that are being emitted in a given region of code. Below is an updated code example:
#include <string>
#include <sstream>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/from_stream.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>
#include <boost/log/attributes/mutable_constant.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
#include <boost/log/sources/logger.hpp>
int main(int argc, char *argv_char[]) {
std::stringstream config;
config << R"([Core])" << std::endl;
config << R"(DisableLogging=false)" << std::endl;
//config << R"(Filter="%Severity% >= trace")" << std::endl;
config << R"([Sinks.Sink])" << std::endl;
config << R"(Destination=Console)" << std::endl;
config << R"(Filter="%CMD% = \"YES\"")" << std::endl;
config << R"(Format="%Message%")" << std::endl;
config << R"(Asynchronous=false)" << std::endl;
config << R"(AutoFlush=true)" << std::endl;
boost::log::core::get()->add_global_attribute("CMD", boost::log::attributes::mutable_constant<std::string>("NO"));
boost::log::init_from_stream(config);
boost::log::sources::logger logger;
logger.add_attribute("CMD", boost::log::attributes::constant<std::string>("YES"));
BOOST_LOG(logger) << "A log message from logger";
{
boost::log::sources::logger loggerScoped;
BOOST_LOG_SCOPED_LOGGER_ATTR(loggerScoped, "CMD", boost::log::attributes::constant<std::string>("YES"));
BOOST_LOG(loggerScoped) << "A log message from loggerScoped";
}
BOOST_LOG_TRIVIAL(debug) << "Non-visible message";
BOOST_LOG_TRIVIAL(debug) << boost::log::add_value("CMD", "YES") << "Still non-visible message";
return 0;
}
I use this function to init logging with boost and it works well
void initBoostLog(std::string logfile)
{
boost::log::add_file_log
(
boost::log::keywords::file_name = logfile,
boost::log::keywords::auto_flush = true,
boost::log::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
)
);
boost::log::add_common_attributes();
boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::info);
}
It gives log like this as output
[2020-07-29 16:07:20.726137] <info> My log starts
[2020-07-29 16:07:20.726619] <info> Next log line
But I want the clock to be the monotonic clock similar to this from standard C (what I rightly or wrongly call the system uptime)
struct timespec monotime;
clock_gettime(CLOCK_MONOTONIC, &monotime);
return (float)(monotime.tv_sec + monotime.tv_nsec/1.0e9);
So that log would log something like this
[1130.726137] <info> My log starts
[1130.726619] <info> Next log line
Boost offers a monotonic clock
boost::log::detail::get_timestamp()
But I don't know now how to use it in the init of the boost log?
How can it be used or can be solved in some other way?
My system is Ubuntu 16.04 arm64
Update after #sehe answer
sehes' answer works like clockwork (pun intended!) the only thing I had to add was to set the precision in the stream. Otherwise I would only get 2 or 3 decimals (same code different Ubuntu 16.04 installations) like this
boost::log::expressions::stream
<< "[" << std::fixed << std::setprecision(6) << boost::log::expressions::attr<double>("monoclock")
<< "] <" << boost::log::trivial::severity
<< "> " << boost::log::expressions::smessage
Boost offers a monotonic clock boost::log::detail::get_timestamp()
It's a bit misleading to say it "offers" that. It's expressly in a detail namespace so you cannot rely on it.
Is it monotonic?
It turns out that on POSIX the implementation is switched anyways:
# if defined(_POSIX_MONOTONIC_CLOCK)
//! \c get_timestamp implementation based on POSIX monotonic clock
timestamp get_timestamp_monotonic_clock() {
// ...
}
# define BOOST_LOG_DEFAULT_GET_TIMESTAMP get_timestamp_monotonic_clock
# else // if defined(_POSIX_MONOTONIC_CLOCK)
# define BOOST_LOG_DEFAULT_GET_TIMESTAMP get_timestamp_realtime_clock
# endif // if defined(_POSIX_MONOTONIC_CLOCK)
The switch only depends on _POSIX_MONOTONIC_CLOCK which is defined in unistd.h https://linux.die.net/man/3/clock_gettime
Availability
On POSIX systems on which these functions are available, the symbol _POSIX_TIMERS is defined in <unistd.h> to a value greater than 0. The symbols _POSIX_MONOTONIC_CLOCK, _POSIX_CPUTIME, _POSIX_THREAD_CPUTIME indicate that CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID are available. (See also sysconf(3).)
So, yes it does use monotonic clock if available. (It does fall back to realtime if monotonic fails at runtime, which makes sense with ported binaries).
Is it used?
Yes. It's used in queued sinks, to dequeue new messages as per a "ordering window": That's not your format attribute, though.
How to make it work
Define your own attribute! We can use an adhoc formatting expression:
auto adhoc =
expr::wrap_formatter([](boost::log::record_view const&, boost::log::formatting_ostream& strm) {
struct ::timespec monotime;
::clock_gettime(CLOCK_MONOTONIC, &monotime);
strm << std::fixed << std::setprecision(6) << (monotime.tv_sec + monotime.tv_nsec/1.0e9);
});
When used:
boost::log::keywords::format = (boost::log::expressions::stream
<< "[" << adhoc << "] "
<< "<" << boost::log::trivial::severity << "> "
<< boost::log::expressions::smessage));
We get:
[768386.969232] <info> An informational severity message
[768386.969656] <warning> A warning severity message
[768386.969793] <error> An error severity message
[768386.969887] <fatal> A fatal severity message
See it Live On Coliru
Improvement suggestions
I called that ad-hoc, because actually it doesn't log the timestamp of the meesage, but rather the timestamp of formatting, which might significantly differ. In that respect, you may want to actually define a custom attribute which will be stored in the record, so you can format that instead.
Custom Attribute: monoclock
It turned out to be pretty simple: function objects as attributes.
static inline double get_monoclock() {
struct ::timespec monotime;
::clock_gettime(CLOCK_MONOTONIC, &monotime);
return monotime.tv_sec + monotime.tv_nsec/1.0e9;
}
All that's required to add the attribte to all records:
boost::log::core::get()->add_global_attribute("monoclock", attrs::make_function(&get_monoclock));
You're then free to use it in your formatters:
boost::log::keywords::format = (boost::log::expressions::stream
<< "[" << expr::attr<double>("monoclock") << "] "
<< "<" << boost::log::trivial::severity << "> "
<< boost::log::expressions::smessage));
Again, Live On Coliru
[504214.461181] <info> An informational severity message
[504214.462371] <warning> A warning severity message
[504214.462405] <error> An error severity message
[504214.462433] <fatal> A fatal severity message
Full Listing
Again, Live On Coliru
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/sources/record_ostream.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/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
static inline double get_monoclock() {
struct ::timespec monotime;
::clock_gettime(CLOCK_MONOTONIC, &monotime);
return monotime.tv_sec + monotime.tv_nsec/1.0e9;
}
void initBoostLog(std::string logfile) {
namespace attrs = boost::log::attributes;
namespace expr = boost::log::expressions;
boost::log::add_file_log(
boost::log::keywords::file_name = logfile,
boost::log::keywords::auto_flush = true,
boost::log::keywords::format = (boost::log::expressions::stream
<< "[" << std::fixed << expr::attr<double>("monoclock") << "] "
<< "<" << boost::log::trivial::severity << "> "
<< boost::log::expressions::smessage));
boost::log::add_common_attributes();
boost::log::core::get()->add_global_attribute("monoclock", attrs::make_function(&get_monoclock));
boost::log::core::get()->set_filter(boost::log::trivial::severity >=
boost::log::trivial::info);
}
int main() {
initBoostLog("test.log");
using namespace boost::log::trivial;
boost::log::sources::severity_logger< severity_level > lg;
BOOST_LOG_SEV(lg, trace) << "A trace severity message";
BOOST_LOG_SEV(lg, debug) << "A debug severity message";
BOOST_LOG_SEV(lg, info) << "An informational severity message";
BOOST_LOG_SEV(lg, warning) << "A warning severity message";
BOOST_LOG_SEV(lg, error) << "An error severity message";
BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
}
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;
}
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 am trying to understand the boost log tutorial, and I have written the following code:
#include <iostream>
#include <boost/log/keywords/severity.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/sources/severity_feature.hpp>
#include <boost/log/sources/severity_logger.hpp>
namespace keywords = boost::log::keywords;
namespace src = boost::log::sources;
// We define our own severity levels
enum severity_level
{
normal = 0,
notification,
warning,
error,
critical
};
void logging_function()
{
// The logger implicitly adds a source-specific attribute 'Severity'
// of type 'severity_level' on construction
src::severity_logger< severity_level > slg;
BOOST_LOG_SEV(slg, normal) << "A regular message";
BOOST_LOG_SEV(slg, warning) << "Something bad is going on but I can handle it";
BOOST_LOG_SEV(slg, critical) << "Everything crumbles, shoot me now!";
}
void default_severity()
{
// The default severity can be specified in constructor.
src::severity_logger< severity_level > error_lg(keywords::severity = error);
BOOST_LOG(error_lg) << "An error level log record (by default)";
// The explicitly specified level overrides the default
BOOST_LOG_SEV(error_lg, warning) << "A warning level log record (overrode the default)";
}
int main(int argc, char **argv) {
logging_function();
default_severity();
std::cout << "Hello, world!" << std::endl;
return 0;
}
But it is always printing the logs with the info severity level, like this:
[2014-11-12 14:38:23.476358] [0xbedaf780] [info] A regular message
[2014-11-12 14:38:23.476967] [0xbedaf780] [info] Something bad is going on but I can handle it
[2014-11-12 14:38:23.476978] [0xbedaf780] [info] Everything crumbles, shoot me now!
[2014-11-12 14:38:23.476991] [0xbedaf780] [info] An error level log record (by default)
[2014-11-12 14:38:23.477002] [0xbedaf780] [info] A warning level log record (overrode the default)
Hello, world!
Why is that? Is it normal to print in standard out? And why isn't it printing the wanted severity level?
Even if I have added this:
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;
}
The output remains the same. What else have I forgotten?