diff --git a/src/blackhole/formatter/json.hpp b/src/blackhole/formatter/json.hpp index 278fdee7..8d2ff91d 100644 --- a/src/blackhole/formatter/json.hpp +++ b/src/blackhole/formatter/json.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -172,10 +173,29 @@ class json_t : public base_t { root.SetObject(); json_visitor_t visitor(&root, config, mapper); - for (auto it = record.attributes().begin(); it != record.attributes().end(); ++it) { - const std::string& name = it->first; - const attribute_t& attribute = it->second; - aux::apply_visitor(visitor, name, attribute.value); + + const auto& attributes = record.attributes().partial(); + + if (config.filter) { + std::unordered_set duplicates; + + for (auto it = attributes.rbegin(); it != attributes.rend(); ++it) { + const std::string& name = it->first; + const attribute_t& attribute = it->second; + + if (!duplicates.insert(name).second) { + // Filter duplicates. + continue; + } + + aux::apply_visitor(visitor, name, attribute.value); + } + } else { + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + const std::string& name = it->first; + const attribute_t& attribute = it->second; + aux::apply_visitor(visitor, name, attribute.value); + } } rapidjson::GenericStringBuffer> buffer; @@ -200,6 +220,12 @@ struct factory_traits { static void map_config(const aux::extractor& ex, config_type& config) { + try { + ex["filter"].to(config.filter); + } catch (const error_t&) { + config.filter = false; + } + ex["newline"].to(config.newline); auto mapping = ex["mapping"].get(); diff --git a/src/blackhole/formatter/json/config.hpp b/src/blackhole/formatter/json/config.hpp index 47ee5037..192c376e 100644 --- a/src/blackhole/formatter/json/config.hpp +++ b/src/blackhole/formatter/json/config.hpp @@ -26,11 +26,13 @@ struct routing_t { } // namespace map struct config_t { + bool filter; bool newline; map::mapping_t naming; map::routing_t routing; config_t() : + filter(false), newline(false) {} }; diff --git a/src/tests/test_JsonFormatter.cpp b/src/tests/test_JsonFormatter.cpp index 74df7931..8c69a13d 100644 --- a/src/tests/test_JsonFormatter.cpp +++ b/src/tests/test_JsonFormatter.cpp @@ -240,3 +240,26 @@ TEST(json_t, AttributeMappingIsDeterminedByItsBaseNames) { ASSERT_TRUE(doc["@secret"].IsString()); EXPECT_STREQ("(42)", doc["@secret"].GetString()); } + +TEST(json_t, AllowDuplicateAttributes) { + record_t record; + record.insert(keyword::message() = "le message1"); + record.insert(keyword::message() = "le message2"); + + formatter::json_t fmt; + std::string expected = "{\"message\":\"le message1\",\"message\":\"le message2\"}"; + EXPECT_EQ(expected, fmt.format(record)); +} + +TEST(json_t, DisallowDuplicateAttributesIfNeeded) { + record_t record; + record.insert(keyword::message() = "le message1"); + record.insert(keyword::message() = "le message2"); + + formatter::json::config_t config; + config.filter = true; + + formatter::json_t fmt(config); + std::string expected = "{\"message\":\"le message2\"}"; + EXPECT_EQ(expected, fmt.format(record)); +}