I'm using a sink to log information and a file with information on the different levels I want for each tag I created like so:
sink->set_filter(logging::trivial::severity >= logging::trivial::warning && expr::attr<std::string>("Tag") == tag);
[...]
sink->set_filter(logging::trivial::severity >= logging::trivial::warning && expr::attr<std::string>("Tag") == tag1);
with both tag and tag1 different tags
I also tried to apply the documentation of boost::phoenix to my problem but I can't figure out how to implement it.
Right now, I have this code, but it just overrides the filter each time I get into a leaf.
void setSinks()
{
logging::core::get()->add_global_attribute("Tag", attrs::mutable_constant<std::string>(""));
std::string path = "..../log_config.json";
[def of sink...]
pt::ptree root;
pt::read_json(path, root);
std::function<void(pt::ptree, std::string)> parse_tree;
auto setFilter = [](std::string tag, std::string logLevel) {
logging::trivial::severity_level level;
if (logLevel == "TRACE") level = logging::trivial::trace;
else if (logLevel == "DEBUG") level = logging::trivial::debug;
else if (logLevel == "INFO") level = logging::trivial::info;
else if (logLevel == "WARNING") level = logging::trivial::warning;
else if (logLevel == "ERROR") level = logging::trivial::error;
else if (logLevel == "FATAL") level = logging::trivial::fatal;
else level = logging::trivial::debug;
return logging::trivial::severity >= level && expr::attr<std::string>("Tag") == tag;
};
parse_tree = [&sink, &setFilter, &parse_tree](pt::ptree tree, std::string tag)
{
for (const auto& v : tree)
{
std::string name = v.first;
pt::ptree value = v.second;
if (value.empty())
{
sink->set_filter(setFilter(tag + "." + name, value.data()));
}
else
{
parse_tree(value, (tag.empty() ? name : tag + "." + name));
}
}
};
parse_tree(root, "");
}
If your list of tags is known and fixed at compile time, you can compose a filter using template expressions like this:
sink->set_filter
(
(expr::attr<std::string>("Tag") == tag1 && logging::trivial::severity >= severity1) ||
(expr::attr<std::string>("Tag") == tag2 && logging::trivial::severity >= severity2) ||
...
);
Here, tag1
, tag2
, severity1
and severity2
may be constants or dynamic values, e.g. obtained from a file.
If the list of tags is more dynamic, you could use channel_severity_filter
. It is used to map severity level thresholds to different channels, and in your case your "Tag" attribute plays the role of a channel name.
// Create a threshold table. If not using keywords, you have to explicitly
// specify the value types of Tag and Severity attributes.
auto min_severity = expr::channel_severity_filter<
std::string, logging::trivial::severity_level>("Tag", "Severity");
// Populate the table. You may do this dynamically, in a loop.
min_severity[tag] = severity;
sink->set_filter(min_severity);
You may also combine this filter with other template expressions, as shown in the docs.
Lastly, you can implement whatever filtering logic you want by writing your own filtering function.
typedef std::map<std::string, logging::trivial::severity_level> severity_table_t;
bool my_filter(
severity_table_t const& table,
logging::value_ref<std::string> tag,
logging::value_ref<logging::trivial::severity_level> severity)
{
// Check if Tag and Severity attributes were present in the log record
if (!tag || !severity)
return false;
// Check if the Tag is present in the table
auto it = table.find(*tag);
if (it == table.end())
return false;
// Check if Severity satisfies the threshold
return *severity >= it->second;
}
// Populate the table, e.g. from a file
severity_table_t table;
sink->set_filter(boost::phoenix::bind(
&my_filter,
table,
expr::attr<std::string>("Tag").or_none(),
expr::attr<logging::trivial::severity_level>("Severity").or_none()));
You can also do away with Boost.Phoenix and implement attribute value extraction yourself.
typedef std::map<std::string, logging::trivial::severity_level> severity_table_t;
// Populate the table, e.g. from a file
severity_table_t table;
sink->set_filter
(
[table](logging::attribute_value_set const& record)
{
// Check if Tag and Severity attributes were present in the log record
auto tag = record["Tag"].extract<std::string>();
if (!tag)
return false;
// You can use keywords to reference attribute values
auto severity = record[logging::trivial::severity];
if (!severity)
return false;
// Check if the Tag is present in the table
auto it = table.find(*tag);
if (it == table.end())
return false;
// Check if Severity satisfies the threshold
return *severity >= it->second;
}
);