浏览代码

Add support for compact JSON output.

Sam Jaffe 6 年之前
父节点
当前提交
2c52f84da7
共有 2 个文件被更改,包括 77 次插入12 次删除
  1. 54 9
      src/loggers/json_layout.cxx
  2. 23 3
      test/json_layout_test.cxx

+ 54 - 9
src/loggers/json_layout.cxx

@@ -25,6 +25,18 @@ private:
   bool compact_, eol_, log_as_json_;
 };
 
+struct json_writer {
+  json_writer(std::ostream & os, bool compact);
+  void write_indent();
+  void write_key(std::string const & key);
+  void write_timestamp(struct timeval time);
+  
+  std::ostream & os;
+  bool write_whitespace;
+  int indent;
+  std::string div;
+};
+
 using namespace logging::property;
 properties const DEFAULT_JSON_LAYOUT{_obj({
   {"charset", _v("en_US.UTF-8")},
@@ -48,18 +60,51 @@ json_layout::json_layout(properties const & props)
   
 }
 
-static void write_timestamp(std::ostream & os, struct timeval time) {
-  os << "\"instant\": {\n    \"epochSecond\": " << time.tv_sec;
-  os << ",\n    \"nanoOfSecond\": " << (time.tv_usec * 1000) << "\n  }";
+json_writer::json_writer(std::ostream & os, bool compact)
+: os(os), write_whitespace(!compact), indent(1),
+  div(compact ? "" : " ") {
+}
+
+void json_writer::write_indent() {
+  if (!write_whitespace) return;
+  os << NEWLINE_TOKEN;
+  for (int i = 0; i < indent; ++i) {
+    os << "  ";
+  }
+}
+
+void json_writer::write_key(std::string const & key) {
+  write_indent();
+  os << '"' << key << '"' << ':' << div;
+}
+
+void json_writer::write_timestamp(struct timeval time) {
+  write_key("instant");
+  os << '{';
+  ++indent;
+  write_key("epochSecond");
+  os << time.tv_sec << ",";
+  write_key("nanoOfSecond");
+  os << (time.tv_usec * 1000);
+  --indent;
+  write_indent();
+  os << '}';
 }
 
 void json_layout::format(std::ostream & os, logpacket const & pkt) const {
-  os << "{\n  ";
-  write_timestamp(os, pkt.time);
-  os << "\n  \"level\": \"" << pkt.level << "\",";
-  os << "\n  \"loggerName\": \"" << pkt.logger << "\",";
-  os << "\n  \"message\": \"" << pkt.message.str() << "\"";
-  os << "\n}";
+  json_writer writer(os, compact_);
+  os << '{';
+  writer.write_timestamp(pkt.time);
+  os << ',';
+  writer.write_key("level");
+  os << "\"" << pkt.level << "\",";
+  writer.write_key("loggerName");
+  os << "\"" << pkt.logger << "\",";
+  writer.write_key("message");
+  os << "\"" << pkt.message.str() << "\"";
+  --writer.indent;
+  writer.write_indent();
+  os << '}';
   if (eol_) {
     os << NEWLINE_TOKEN;
   }

+ 23 - 3
test/json_layout_test.cxx

@@ -27,20 +27,40 @@ std::string const formatted_output = R"({
   "instant": {
     "epochSecond": 1554401840,
     "nanoOfSecond": 123456000
-  }
+  },
   "level": "warning",
   "loggerName": "UnitTest",
   "message": "This is a test message"
 })";
 
-TEST(JsonLayoutTest, TestInvokesFormatter) {
+std::string const compact_output = "{\"instant\":{\"epochSecond\":"
+  "1554401840,\"nanoOfSecond\":123456000},\"level\":\"warning\","
+  "\"loggerName\":\"UnitTest\",\"message\":\"This is a test message\"}";
+
+TEST(JsonLayoutTest, LogsInformationInJSON) {
   using namespace logging;
   auto playout = layouts::instance().get("JsonLayout", {});
   
   std::stringstream ss;
   playout->format(ss, {{NOW, 123456}, level::warning, {}, "UnitTest",
     "This is a test message"});
-    
+  
   using testing::Eq;
   EXPECT_THAT(ss.str(), Eq(formatted_output));
 }
+
+TEST(JsonLayoutTest, CompactMeansNoWhitespace) {
+  using namespace logging;
+  using namespace logging::property;
+  properties const props{_obj({
+    {"compact", _v(true)}
+  })};
+  auto playout = layouts::instance().get("JsonLayout", props);
+  
+  std::stringstream ss;
+  playout->format(ss, {{NOW, 123456}, level::warning, {}, "UnitTest",
+    "This is a test message"});
+  
+  using testing::Eq;
+  EXPECT_THAT(ss.str(), Eq(compact_output));
+}