Reputation: 226
My application have several components and I want that each component will write logs to a separate file.
I wanted to use the "Text multi-file backend" but according to the documentation it does not support file rotation.
So my idea was to implement a log class and make an instant for each of the components and store them in a map, that way I can use the map to get the correct logger instance (according to the name) and log to the correct file.
I have done this but this is not working for me, I can see the same messages in all of the files (it seems that this is a global logger).
This is a draft of my code:
logger.h
struct LogInfo{
std::string log_path;
LogLevel log_level;
long log_file_size;
int log_file_amount;
};
LogLevel stringToLogLevel(const std::string &fileType);
class Logger {
public:
Logger(const LogInfo &log_info, const std::string &component_name);
void writeToLog(LogLevel log_level, const std::string &scope, const std::string &message);
private:
void scanForFiles(const std::string &path, const std::string &component_name);
std::string pid;
std::string log_file_name;
boost::log::sources::severity_logger<boost::log::trivial::severity_level> boost_severity_logger;
};
logger.cpp
using namespace boost::log;
using namespace std;
namespace fs = boost::filesystem;
LogLevel stringToLogLevel(const string &fileType) {
if (fileType == "TRACE")
return LOGLEVEL_TRACE;
if (fileType == "DEBUG")
return LOGLEVEL_DEBUG;
if (fileType == "INFO")
return LOGLEVEL_INFO;
if (fileType == "WARNING")
return LOGLEVEL_WARNING;
if (fileType == "ERROR")
return LOGLEVEL_ERROR;
throw invalid_argument("Unknown file type");
}
trivial::severity_level logLevelToBoostLogLevel(const LogLevel log_level) {
if (log_level == LOGLEVEL_TRACE)
return trivial::trace;
if (log_level == LOGLEVEL_DEBUG)
return trivial::debug;
if (log_level == LOGLEVEL_INFO)
return trivial::info;
if (log_level == LOGLEVEL_WARNING)
return trivial::warning;
if (log_level == LOGLEVEL_ERROR)
return trivial::error;
throw invalid_argument("Unknown log type");
}
Logger::Logger(const LogInfo &log_info, const string &component_name) {
scanForFiles(log_info.log_path, component_name);
stringstream s;
s << log_info.log_path << component_name << "_%N.log";
add_file_log(
keywords::file_name = s.str(),
keywords::rotation_size = log_info.log_file_size,
keywords::max_size = log_info.log_file_amount * log_info.log_file_size,
keywords::target = log_info.log_path,
keywords::open_mode = std::ios::out | std::ios::app,
keywords::auto_flush = true,
keywords::format =
expressions::format("[%1%] [%2%] [%3%] [%4%] %5%")
% expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
% expressions::attr<unsigned int>("ThreadID")
% expressions::attr<string>("Scope")
% trivial::severity
% expressions::smessage
);
trivial::severity_level requested_level = logLevelToBoostLogLevel(log_info.log_level);
core::get()->set_filter(
trivial::severity >= requested_level
);
add_common_attributes();
}
void Logger::writeToLog(LogLevel log_level, const std::string &scope, const std::string &message) {
BOOST_LOG_SCOPED_THREAD_ATTR("ThreadID", attributes::constant<unsigned int>(OS::getTid()));
BOOST_LOG_SCOPED_THREAD_ATTR("Scope", attributes::constant<string>(scope));
BOOST_LOG_SEV(this->boost_severity_logger, logLevelToBoostLogLevel(log_level))<< message.c_str();
}
Is it possible to achieve what I want?
Upvotes: 2
Views: 2363
Reputation: 1781
To separate log files you can add filter to the added log file, in that way file rotation will work fine and each of your components will have their own log file:
logging::add_file_log(
keywords::file_name = s.str(),
keywords::rotation_size = log_info.log_file_size,
keywords::max_size = log_info.log_file_amount * log_info.log_file_size,
keywords::filter = expr::attr< std::string >([ATTRIBUTE/TAG]) == [ex. COMPONENT_NAME],
keywords::target = log_info.log_path,
keywords::open_mode = std::ios::out | std::ios::app,
keywords::auto_flush = true,
keywords::format =
expressions::format("[%1%] [%2%] [%3%] [%4%] %5%")
% expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
% expressions::attr<unsigned int>("ThreadID")
% expressions::attr<string>("Scope")
% trivial::severity
% expressions::smessage
);
While logging the attribute should be set and so the message will be written on corresponding log file, for instance:
void Logger::writeToLog(LogLevel log_level, const std::string &scope, const std::string &message) {
...
BOOST_LOG_SCOPED_THREAD_TAG([ATT/TAG], [VALUE]); // Add Component name here
BOOST_LOG_SEV(this->boost_severity_logger, logLevelToBoostLogLevel(log_level))<< message.c_str();
}
Upvotes: 1
Reputation: 10614
See this reply. Among other things, it describes how to use channels and filters to achieve what you want.
Upvotes: 1