Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Development/cmake/NmosCppTest.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ set(NMOS_CPP_TEST_NMOS_TEST_SOURCES
nmos/test/json_validator_test.cpp
nmos/test/jwt_generator_test.cpp
nmos/test/jwt_validation_test.cpp
nmos/test/log_gate_test.cpp
nmos/test/mdns_test.cpp
nmos/test/node_interfaces_test.cpp
nmos/test/paging_utils_test.cpp
Expand Down
26 changes: 24 additions & 2 deletions Development/nmos/log_gate.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,36 @@ namespace nmos

const auto& pertinent_categories = nmos::fields::logging_categories(model.settings);

// categories prefixed with '!' specify messages that should not be logged;
// a negative match takes precedence over any positive match, and when only
// negative categories are specified, all other messages are pertinent
const auto is_negative = [](const web::json::value& category)
{
const auto& s = category.as_string();
return !s.empty() && U('!') == s.front();
};
const bool default_pertinent = 0 != pertinent_categories.size()
&& pertinent_categories.end() == boost::range::find_if(pertinent_categories, [&](const web::json::value& category)
{
return !is_negative(category);
});

if (categories.empty())
{
static const auto no_category = web::json::value::string(utility::string_t());
return pertinent_categories.end() != boost::range::find(pertinent_categories, no_category);
return default_pertinent || pertinent_categories.end() != boost::range::find(pertinent_categories, no_category);
}

// this could be made more efficient if there may be many pertinent categories
return categories.end() != boost::range::find_if(categories, [&](const nmos::category& c)
if (categories.end() != boost::range::find_if(categories, [&](const nmos::category& c)
{
return pertinent_categories.end() != boost::range::find(pertinent_categories, web::json::value::string(utility::s2us("!" + c)));
}))
{
return false;
}

return default_pertinent || categories.end() != boost::range::find_if(categories, [&](const nmos::category& c)
{
return pertinent_categories.end() != boost::range::find(pertinent_categories, web::json::value::string(utility::s2us(c)));
});
Expand Down
2 changes: 2 additions & 0 deletions Development/nmos/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ namespace nmos
const web::json::field_as_integer_or logging_level{ U("logging_level"), 0 }; // 0, rather than slog::severities::info or slog::nil_severity, just to avoid a #include

// logging_categories [registry, node]: array of logging categories to be included in the error log
// categories prefixed with '!' are excluded, even if another category matches positively;
// when only excluded categories are specified, all other log messages are included
const web::json::field_as_array logging_categories{ U("logging_categories") }; // when omitted, all log messages are included

// Configuration settings and defaults for the NMOS APIs
Expand Down
69 changes: 69 additions & 0 deletions Development/nmos/test/log_gate_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// The first "test" is of course whether the header compiles standalone
#include "nmos/log_gate.h"

#include <sstream>
#include "bst/test/test.h"

namespace
{
struct test_gate : nmos::experimental::log_gate
{
test_gate(std::ostream& error_log, std::ostream& access_log, nmos::experimental::log_model& model)
: nmos::experimental::log_gate(error_log, access_log, model) {}
using nmos::experimental::log_gate::pertinent;
};
}

////////////////////////////////////////////////////////////////////////////////////////////
BST_TEST_CASE(testLogGatePertinentCategories)
{
using web::json::value_of;

std::ostringstream error_log;
std::ostringstream access_log;
nmos::experimental::log_model model;
test_gate gate(error_log, access_log, model);

const std::list<nmos::category> no_categories;
const std::list<nmos::category> access{ "access" };
const std::list<nmos::category> send_query_ws_events{ "send_query_ws_events" };
const std::list<nmos::category> both{ "send_query_ws_events", "access" };

// when logging_categories is omitted, all messages are pertinent
BST_REQUIRE(gate.pertinent(no_categories));
BST_REQUIRE(gate.pertinent(access));
BST_REQUIRE(gate.pertinent(both));

// when logging_categories is empty, no messages are pertinent
model.settings[nmos::fields::logging_categories] = web::json::value::array();
BST_REQUIRE(!gate.pertinent(no_categories));
BST_REQUIRE(!gate.pertinent(access));
BST_REQUIRE(!gate.pertinent(both));

// positive categories select the messages to be logged
model.settings[nmos::fields::logging_categories] = value_of({ U("send_query_ws_events") });
BST_REQUIRE(!gate.pertinent(no_categories));
BST_REQUIRE(gate.pertinent(send_query_ws_events));
BST_REQUIRE(!gate.pertinent(access));
BST_REQUIRE(gate.pertinent(both));

// the empty string selects messages with no category
model.settings[nmos::fields::logging_categories] = value_of({ U("") });
BST_REQUIRE(gate.pertinent(no_categories));
BST_REQUIRE(!gate.pertinent(access));

// a category prefixed with '!' excludes matching messages, even if another
// category matches positively
model.settings[nmos::fields::logging_categories] = value_of({ U("send_query_ws_events"), U("!access") });
BST_REQUIRE(gate.pertinent(send_query_ws_events));
BST_REQUIRE(!gate.pertinent(access));
BST_REQUIRE(!gate.pertinent(both));
BST_REQUIRE(!gate.pertinent(no_categories));

// when only excluded categories are specified, all other messages are pertinent
model.settings[nmos::fields::logging_categories] = value_of({ U("!access") });
BST_REQUIRE(gate.pertinent(no_categories));
BST_REQUIRE(gate.pertinent(send_query_ws_events));
BST_REQUIRE(!gate.pertinent(access));
BST_REQUIRE(!gate.pertinent(both));
}
Loading