Boost.Log 更改频道名称未知的每个频道的过滤器
Boost.Log change filter per channel with unknown channel names
在 Boost.Log 我想根据频道设置过滤器。使用此 example 我使用 text_file_backend 实现。但是在我的程序中,频道名称是由用户作为输入参数给出的。所以我决定实现一种为频道设置严重性过滤器的方法。
commons.h
#ifndef COMMONS_H_
#define COMMONS_H_
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/log/sinks/syslog_backend.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/thread/thread.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace expr = boost::log::expressions;
namespace keywords = boost::log::keywords;
enum severity_levels
{
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_levels)
BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)
typedef expr::channel_severity_filter_actor< std::string, severity_levels >
min_severity_filter;
typedef src::severity_channel_logger_mt< severity_levels, std::string >
logger_type_mt;
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, logger_type_mt)
typedef sinks::synchronous_sink< sinks::text_file_backend > File_sink;
#define ADD_LOG(severity, channel, msg, args...) add_log_message(__FILE__, __LINE__, severity, channel, boost::this_thread::get_id(), msg, args)
#define MY_GLOBAL_LOGGER(log_, channel, sv, file, line, thread) BOOST_LOG_CHANNEL_SEV( log_, channel, sv) \
<< boost::log::add_value("Line", line) \
<< boost::log::add_value("File", file) \
<< boost::log::add_value("Thread_id", thread)
std::ostream& operator<< (std::ostream& strm, severity_levels 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;
}
#endif
Logger.h
#ifndef LOGGER_H_
#define LOGGER_H_
#define BOOST_LOG_DYN_LINK 1
#include <string.h>
#include <ctime>
#include <chrono>
#include "commons.h"
class Logger
{
public:
static min_severity_filter min_severity;
static void init_logging()
{
logging::add_common_attributes();
// Create a text file sink
boost::shared_ptr< sinks::text_file_backend> backend(new sinks::text_file_backend());
backend->set_file_name_pattern<std::string>("sample_%N.log");
backend->set_rotation_size(2 * 1024 * 1024);
boost::shared_ptr< File_sink > sink(new File_sink(backend));
// Set up where the rotated files will be stored
init_file_collecting <File_sink>(sink);
sink->set_formatter(&file_log_formatter);
sink->locked_backend()->scan_for_files();
logging::core::get()->add_sink(sink);
logging::core::get()->set_filter(min_severity || severity >= normal);
}
template <typename T>
static void init_file_collecting(boost::shared_ptr< T > sink)
{
sink->locked_backend()->set_file_collector(sinks::file::make_collector(
keywords::target = "logs", /*< the target directory >*/
keywords::max_size = 64 * 1024 * 1024, /*< maximum total size of the stored files, in bytes >*/
keywords::min_free_space = 100 * 1024 * 1024 /*< minimum free space on the drive, in bytes >*/
));
}
static void file_log_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
{
// Get the LineID attribute value and put it into the stream
strm << logging::extract< unsigned int >("LineID", rec) << ": ";
// TimeStamp
strm << "[";
strm << logging::extract<boost::posix_time::ptime>("TimeStamp", rec);
strm << "]";
// thread id
strm << "[" << logging::extract< boost::thread::id >("Thread_id", rec) << "] ";
strm << "[" << rec[channel] << "] ";
strm << "[";
strm << logging::extract< int >("Line", rec) << ", ";
logging::value_ref< std::string > fullpath = logging::extract< std::string >("File", rec);
strm << boost::filesystem::path(fullpath.get()).filename().string() << "] ";
// The same for the severity level.
// The simplified syntax is possible if attribute keywords are used.
strm << "<" << rec[severity] << "> ";
// Finally, put the record message to the stream
strm << rec[expr::smessage];
}
static void set_channel_filter(std::string channel, severity_levels min_level)
{
min_severity[channel] = min_level;
logging::core::get()->set_filter(min_severity);
}
static void add_log_message(const char* file, int line, severity_levels severity,
std::string channel, boost::thread::id thread_id,
const char* message, ...)
{
char buffer[256];
va_list ap;
va_start(ap, message);
vsnprintf(buffer, 256, message, ap);
MY_GLOBAL_LOGGER(test_lg::get(), channel, severity, file, line, thread_id) << buffer;
va_end(ap);
}
};
#endif
min_severity_filter Logger::min_severity = expr::channel_severity_filter(channel, severity);
在程序的第一个调用 init_logging()
将所有通道的过滤器设置为正常。
问题是当我用一些输入(例如 "CHANNEL_1",警告)调用 set_channel_filter()
时,我希望只为 "CHANNEL_1" 设置过滤器,但过滤是为所有可能的通道设置的. (例如 "CHANNEL_2, etc). When I add for example "OTHER_CHANNEL" 手动到 set_channel_filter() 它适用于它。我想让 c++ 映射像数据结构一样保存每个通道的所有严重性过滤器。并且任何时候用户调用以添加一个带有过滤器的新频道或现有频道,它只会更改特定频道的过滤,而不是所有频道。
main.cpp
int main()
{
Logger::init_logging();
Logger::ADD_LOG(severity_levels::normal, "NETWORK", "a warning message with id %d", 34);
Logger::ADD_LOG(severity_levels::notification, "NETWORK", "a warning message with id %d", 65);
Logger::ADD_LOG(severity_levels::notification, "GENERAL", "a notification message with id %d", 12);
Logger::ADD_LOG(severity_levels::warning, "GENERAL", "a warning message with id %d", 13);
// Logs in GENERAL category must have severity level of warning or higher in-order to record.
Logger::set_channel_filter("GENERAL", severity_levels::warning);
Logger::ADD_LOG(severity_levels::notification, "GENERAL", "a notification message with id %d", 14);
for (int i = 0; i < 2; i++)
{
Logger::ADD_LOG(severity_levels::warning, "GENERAL", "a warning message with id %d", 15);
}
Logger::ADD_LOG(severity_levels::normal, "NETWORK", "a warning message with id %d", 34); // Expected to sent to file. but filtered!!
Logger::ADD_LOG(severity_levels::notification, "NETWORK", "a warning message with id %d", 65); //Also filtered !!
}
问题是最初,在 init_logging
中,您按如下方式设置过滤器:
logging::core::get()->set_filter(min_severity || severity >= normal);
此时,min_severity
为空,因为您尚未添加任何 channels/severity 阈值。默认情况下,当在 channel/severity 映射中找不到通道时,min_severity
将 return false
,这意味着您在此处设置的过滤器实际上是 severity >= normal
。
稍后,当您调用 set_channel_filter
时,您将第一个条目添加到 min_severity
映射中,以便它适用于 "GENERAL" 频道,但不适用于任何其他频道。但是,您这次设置了不同的过滤器:
logging::core::get()->set_filter(min_severity);
这一次,如果 min_severity
returns false
日志记录被丢弃,这就是 "NETWORK" 通道中最后两条记录所发生的情况。您需要每次都设置相同的过滤器表达式才能获得一致的行为。
在 Boost.Log 我想根据频道设置过滤器。使用此 example 我使用 text_file_backend 实现。但是在我的程序中,频道名称是由用户作为输入参数给出的。所以我决定实现一种为频道设置严重性过滤器的方法。
commons.h
#ifndef COMMONS_H_
#define COMMONS_H_
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/log/sinks/syslog_backend.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/thread/thread.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace expr = boost::log::expressions;
namespace keywords = boost::log::keywords;
enum severity_levels
{
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_levels)
BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)
typedef expr::channel_severity_filter_actor< std::string, severity_levels >
min_severity_filter;
typedef src::severity_channel_logger_mt< severity_levels, std::string >
logger_type_mt;
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, logger_type_mt)
typedef sinks::synchronous_sink< sinks::text_file_backend > File_sink;
#define ADD_LOG(severity, channel, msg, args...) add_log_message(__FILE__, __LINE__, severity, channel, boost::this_thread::get_id(), msg, args)
#define MY_GLOBAL_LOGGER(log_, channel, sv, file, line, thread) BOOST_LOG_CHANNEL_SEV( log_, channel, sv) \
<< boost::log::add_value("Line", line) \
<< boost::log::add_value("File", file) \
<< boost::log::add_value("Thread_id", thread)
std::ostream& operator<< (std::ostream& strm, severity_levels 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;
}
#endif
Logger.h
#ifndef LOGGER_H_
#define LOGGER_H_
#define BOOST_LOG_DYN_LINK 1
#include <string.h>
#include <ctime>
#include <chrono>
#include "commons.h"
class Logger
{
public:
static min_severity_filter min_severity;
static void init_logging()
{
logging::add_common_attributes();
// Create a text file sink
boost::shared_ptr< sinks::text_file_backend> backend(new sinks::text_file_backend());
backend->set_file_name_pattern<std::string>("sample_%N.log");
backend->set_rotation_size(2 * 1024 * 1024);
boost::shared_ptr< File_sink > sink(new File_sink(backend));
// Set up where the rotated files will be stored
init_file_collecting <File_sink>(sink);
sink->set_formatter(&file_log_formatter);
sink->locked_backend()->scan_for_files();
logging::core::get()->add_sink(sink);
logging::core::get()->set_filter(min_severity || severity >= normal);
}
template <typename T>
static void init_file_collecting(boost::shared_ptr< T > sink)
{
sink->locked_backend()->set_file_collector(sinks::file::make_collector(
keywords::target = "logs", /*< the target directory >*/
keywords::max_size = 64 * 1024 * 1024, /*< maximum total size of the stored files, in bytes >*/
keywords::min_free_space = 100 * 1024 * 1024 /*< minimum free space on the drive, in bytes >*/
));
}
static void file_log_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
{
// Get the LineID attribute value and put it into the stream
strm << logging::extract< unsigned int >("LineID", rec) << ": ";
// TimeStamp
strm << "[";
strm << logging::extract<boost::posix_time::ptime>("TimeStamp", rec);
strm << "]";
// thread id
strm << "[" << logging::extract< boost::thread::id >("Thread_id", rec) << "] ";
strm << "[" << rec[channel] << "] ";
strm << "[";
strm << logging::extract< int >("Line", rec) << ", ";
logging::value_ref< std::string > fullpath = logging::extract< std::string >("File", rec);
strm << boost::filesystem::path(fullpath.get()).filename().string() << "] ";
// The same for the severity level.
// The simplified syntax is possible if attribute keywords are used.
strm << "<" << rec[severity] << "> ";
// Finally, put the record message to the stream
strm << rec[expr::smessage];
}
static void set_channel_filter(std::string channel, severity_levels min_level)
{
min_severity[channel] = min_level;
logging::core::get()->set_filter(min_severity);
}
static void add_log_message(const char* file, int line, severity_levels severity,
std::string channel, boost::thread::id thread_id,
const char* message, ...)
{
char buffer[256];
va_list ap;
va_start(ap, message);
vsnprintf(buffer, 256, message, ap);
MY_GLOBAL_LOGGER(test_lg::get(), channel, severity, file, line, thread_id) << buffer;
va_end(ap);
}
};
#endif
min_severity_filter Logger::min_severity = expr::channel_severity_filter(channel, severity);
在程序的第一个调用 init_logging()
将所有通道的过滤器设置为正常。
问题是当我用一些输入(例如 "CHANNEL_1",警告)调用 set_channel_filter()
时,我希望只为 "CHANNEL_1" 设置过滤器,但过滤是为所有可能的通道设置的. (例如 "CHANNEL_2, etc). When I add for example "OTHER_CHANNEL" 手动到 set_channel_filter() 它适用于它。我想让 c++ 映射像数据结构一样保存每个通道的所有严重性过滤器。并且任何时候用户调用以添加一个带有过滤器的新频道或现有频道,它只会更改特定频道的过滤,而不是所有频道。
main.cpp
int main()
{
Logger::init_logging();
Logger::ADD_LOG(severity_levels::normal, "NETWORK", "a warning message with id %d", 34);
Logger::ADD_LOG(severity_levels::notification, "NETWORK", "a warning message with id %d", 65);
Logger::ADD_LOG(severity_levels::notification, "GENERAL", "a notification message with id %d", 12);
Logger::ADD_LOG(severity_levels::warning, "GENERAL", "a warning message with id %d", 13);
// Logs in GENERAL category must have severity level of warning or higher in-order to record.
Logger::set_channel_filter("GENERAL", severity_levels::warning);
Logger::ADD_LOG(severity_levels::notification, "GENERAL", "a notification message with id %d", 14);
for (int i = 0; i < 2; i++)
{
Logger::ADD_LOG(severity_levels::warning, "GENERAL", "a warning message with id %d", 15);
}
Logger::ADD_LOG(severity_levels::normal, "NETWORK", "a warning message with id %d", 34); // Expected to sent to file. but filtered!!
Logger::ADD_LOG(severity_levels::notification, "NETWORK", "a warning message with id %d", 65); //Also filtered !!
}
问题是最初,在 init_logging
中,您按如下方式设置过滤器:
logging::core::get()->set_filter(min_severity || severity >= normal);
此时,min_severity
为空,因为您尚未添加任何 channels/severity 阈值。默认情况下,当在 channel/severity 映射中找不到通道时,min_severity
将 return false
,这意味着您在此处设置的过滤器实际上是 severity >= normal
。
稍后,当您调用 set_channel_filter
时,您将第一个条目添加到 min_severity
映射中,以便它适用于 "GENERAL" 频道,但不适用于任何其他频道。但是,您这次设置了不同的过滤器:
logging::core::get()->set_filter(min_severity);
这一次,如果 min_severity
returns false
日志记录被丢弃,这就是 "NETWORK" 通道中最后两条记录所发生的情况。您需要每次都设置相同的过滤器表达式才能获得一致的行为。