json_layout_test.cxx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. //
  2. // json_layout_test.cxx
  3. // logger_test
  4. //
  5. // Created by Sam Jaffe on 4/13/19.
  6. //
  7. #include <gmock/gmock.h>
  8. #include "resource_factory/prototype_factory.hpp"
  9. #include "logger/detail/layout.h"
  10. #include "logger/log_manager.h"
  11. #include "logger/logpacket.h"
  12. #include "logger/properties.h"
  13. TEST(JsonLayoutTest, CanConstructWithNoConfig) {
  14. EXPECT_NO_THROW(logging::layouts::instance().get("JsonLayout", {}));
  15. }
  16. // Thursday, April 4, 2019 6:17:20 PM GMT
  17. namespace {
  18. constexpr const int NOW = 1554401840;
  19. }
  20. std::string const formatted_output = R"({
  21. "instant" : {
  22. "epochSecond" : 1554401840,
  23. "nanoOfSecond" : 123456000
  24. },
  25. "level" : "WARNING",
  26. "loggerName" : "UnitTest",
  27. "message" : "This is a test message"
  28. })";
  29. TEST(JsonLayoutTest, LogsInformationInJSON) {
  30. using namespace logging;
  31. auto playout = layouts::instance().get("JsonLayout", {});
  32. std::stringstream ss;
  33. playout->format(ss, {{NOW, 123456},
  34. level::warning,
  35. {},
  36. "UnitTest",
  37. "This is a test message"});
  38. using testing::Eq;
  39. EXPECT_THAT(ss.str(), Eq(formatted_output));
  40. }
  41. std::string const compact_output =
  42. "{\"instant\":{\"epochSecond\":"
  43. "1554401840,\"nanoOfSecond\":123456000},\"level\":\"WARNING\","
  44. "\"loggerName\":\"UnitTest\",\"message\":\"This is a test message\"}";
  45. TEST(JsonLayoutTest, CompactMeansNoWhitespace) {
  46. using namespace logging;
  47. using namespace logging::property;
  48. properties const props{_obj({{"compact", _v(true)}})};
  49. auto playout = layouts::instance().get("JsonLayout", props);
  50. std::stringstream ss;
  51. playout->format(ss, {{NOW, 123456},
  52. level::warning,
  53. {},
  54. "UnitTest",
  55. "This is a test message"});
  56. using testing::Eq;
  57. EXPECT_THAT(ss.str(), Eq(compact_output));
  58. }
  59. std::string const location_output = R"({
  60. "instant" : {
  61. "epochSecond" : 1554401840,
  62. "nanoOfSecond" : 123456000
  63. },
  64. "level" : "WARNING",
  65. "loggerName" : "UnitTest",
  66. "message" : "This is a test message",
  67. "source" : {
  68. "file" : "json_layout_test.cxx",
  69. "line" : 97,
  70. "method" : "TestBody"
  71. }
  72. })";
  73. TEST(JsonLayoutTest, ShowsLocationInfo) {
  74. using namespace logging;
  75. using namespace logging::property;
  76. properties const props{_obj({{"locationInfo", _v(true)}})};
  77. auto playout = layouts::instance().get("JsonLayout", props);
  78. std::stringstream ss;
  79. playout->format(ss, {{NOW, 123456},
  80. level::warning,
  81. log_here,
  82. "UnitTest",
  83. "This is a test message"});
  84. using testing::Eq;
  85. EXPECT_THAT(ss.str(), Eq(location_output));
  86. }
  87. TEST(JsonLayoutTest, EOLPropertyAppendsNewline) {
  88. using namespace logging;
  89. using namespace logging::property;
  90. properties const props{_obj({{"eventEol", _v(true)}})};
  91. auto playout = layouts::instance().get("JsonLayout", props);
  92. std::stringstream ss;
  93. playout->format(ss, {{NOW, 123456},
  94. level::warning,
  95. {},
  96. "UnitTest",
  97. "This is a test message"});
  98. using testing::EndsWith;
  99. using testing::StartsWith;
  100. EXPECT_THAT(ss.str(), StartsWith(formatted_output));
  101. EXPECT_THAT(ss.str(), EndsWith(logging::NEWLINE_TOKEN));
  102. }
  103. std::string const struct_output = R"({
  104. "instant" : {
  105. "epochSecond" : 1554401840,
  106. "nanoOfSecond" : 123456000
  107. },
  108. "level" : "WARNING",
  109. "loggerName" : "UnitTest",
  110. "message" : "{225}"
  111. })";
  112. struct example {
  113. int content;
  114. };
  115. std::ostream & operator<<(std::ostream & os, example const & ex) {
  116. return os << '{' << ex.content << '}';
  117. }
  118. std::string const struct_json_output = R"({
  119. "instant" : {
  120. "epochSecond" : 1554401840,
  121. "nanoOfSecond" : 123456000
  122. },
  123. "level" : "WARNING",
  124. "loggerName" : "UnitTest",
  125. "message" : {
  126. "content" : 225
  127. }
  128. })";
  129. struct json_example {
  130. int content;
  131. };
  132. std::ostream & operator<<(std::ostream & os, json_example const & ex) {
  133. return os << '{' << ex.content << '}';
  134. }
  135. Json::Value to_json(json_example const & ex) {
  136. Json::Value json;
  137. json["content"] = ex.content;
  138. return json;
  139. }
  140. TEST(JsonLayoutTest, MessageCanBeLoggedAsJSON) {
  141. using namespace logging;
  142. using namespace logging::property;
  143. properties const props{_obj({{"objectMessageAsJsonObject", _v(true)}})};
  144. auto playout = layouts::instance().get("JsonLayout", props);
  145. std::stringstream ss;
  146. json_example ex{225};
  147. playout->format(ss, {{NOW, 123456},
  148. level::warning,
  149. {},
  150. "UnitTest",
  151. {"This is a test message", ex}});
  152. using testing::Eq;
  153. EXPECT_THAT(ss.str(), Eq(struct_json_output));
  154. }
  155. TEST(JsonLayoutTest, ObjectLackingJsonDefaultsToString) {
  156. using namespace logging;
  157. using namespace logging::property;
  158. properties const props{_obj({{"objectMessageAsJsonObject", _v(true)}})};
  159. auto playout = layouts::instance().get("JsonLayout", props);
  160. std::stringstream ss;
  161. example ex{225};
  162. playout->format(ss, {{NOW, 123456},
  163. level::warning,
  164. {},
  165. "UnitTest",
  166. {"This is a test message", ex}});
  167. using testing::Eq;
  168. EXPECT_THAT(ss.str(), Eq(struct_output));
  169. }
  170. #include "header_test_obj.h"
  171. #include "logger/logger.h"
  172. using namespace logging;
  173. using namespace logging::property;
  174. // clang-format off
  175. properties const JSON_HEADER_SCHEMA{_obj({
  176. {"configuration", _obj({
  177. {"appenders", _obj({
  178. {"Stub", _obj({
  179. {"JsonLayout", _obj({
  180. {"complete", _v(true)},
  181. {"compact", _v(true)},
  182. {"eventEol", _v(true)}
  183. })}
  184. })}
  185. })},
  186. {"loggers", _obj({
  187. {"root", _obj({{"appenders", _obj({{"ref", _v("Stub")}})}})}
  188. })}
  189. })}
  190. })};
  191. // clang-format on
  192. using JsonLayoutHeaderTest = HeaderFooterTest;
  193. TEST_F(JsonLayoutHeaderTest, ProvidesArrayBounds) {
  194. {
  195. manager mgr;
  196. mgr.configure(JSON_HEADER_SCHEMA);
  197. }
  198. using testing::Eq;
  199. EXPECT_THAT(appender->sstream.str(), Eq("[]"));
  200. }
  201. TEST_F(JsonLayoutHeaderTest, SeparatesLogsWithComma) {
  202. using testing::EndsWith;
  203. using testing::StartsWith;
  204. manager mgr;
  205. mgr.configure(JSON_HEADER_SCHEMA);
  206. mgr.get().log(level::error, "HELLO");
  207. // Newline is printed as a part of the message...
  208. EXPECT_THAT(appender->sstream.str(), EndsWith("}\n"));
  209. appender->sstream.str("");
  210. mgr.get().log(level::error, "HELLO");
  211. // So the dividing comma gets attached to the next log
  212. EXPECT_THAT(appender->sstream.str(), StartsWith(",{"));
  213. }
  214. struct JsonLayoutIncompleteTest : public JsonLayoutHeaderTest {
  215. properties OVERRIDE{
  216. _obj({{"configuration",
  217. _obj({{"appenders",
  218. _obj({{"Stub",
  219. _obj({{"JsonLayout",
  220. _obj({{"complete", _v(false)}})}})}})}})}})};
  221. };
  222. TEST_F(JsonLayoutIncompleteTest, NoLogsMeansNoOutput) {
  223. {
  224. manager mgr;
  225. mgr.configure(JSON_HEADER_SCHEMA.mergedWith(OVERRIDE));
  226. }
  227. using testing::Eq;
  228. EXPECT_THAT(appender->sstream.str(), Eq(""));
  229. }
  230. TEST_F(JsonLayoutIncompleteTest, NonCompleteLogProducesJsonPerLine) {
  231. using testing::EndsWith;
  232. using testing::StartsWith;
  233. manager mgr;
  234. mgr.configure(JSON_HEADER_SCHEMA.mergedWith(OVERRIDE));
  235. mgr.get().log(level::error, "HELLO");
  236. // Newline is printed as a part of the message...
  237. EXPECT_THAT(appender->sstream.str(), EndsWith("}\n"));
  238. appender->sstream.str("");
  239. mgr.get().log(level::error, "HELLO");
  240. // So the dividing comma gets attached to the next log
  241. EXPECT_THAT(appender->sstream.str(), StartsWith("{"));
  242. }