json_layout_test.cxx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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}, level::warning, {}, "UnitTest",
  34. "This is a test message"});
  35. using testing::Eq;
  36. EXPECT_THAT(ss.str(), Eq(formatted_output));
  37. }
  38. std::string const compact_output = "{\"instant\":{\"epochSecond\":"
  39. "1554401840,\"nanoOfSecond\":123456000},\"level\":\"WARNING\","
  40. "\"loggerName\":\"UnitTest\",\"message\":\"This is a test message\"}";
  41. TEST(JsonLayoutTest, CompactMeansNoWhitespace) {
  42. using namespace logging;
  43. using namespace logging::property;
  44. properties const props{_obj({
  45. {"compact", _v(true)}
  46. })};
  47. auto playout = layouts::instance().get("JsonLayout", props);
  48. std::stringstream ss;
  49. playout->format(ss, {{NOW, 123456}, level::warning, {}, "UnitTest",
  50. "This is a test message"});
  51. using testing::Eq;
  52. EXPECT_THAT(ss.str(), Eq(compact_output));
  53. }
  54. std::string const location_output = R"({
  55. "instant" : {
  56. "epochSecond" : 1554401840,
  57. "nanoOfSecond" : 123456000
  58. },
  59. "level" : "WARNING",
  60. "loggerName" : "UnitTest",
  61. "message" : "This is a test message",
  62. "source" : {
  63. "file" : "json_layout_test.cxx",
  64. "line" : 92,
  65. "method" : "TestBody"
  66. }
  67. })";
  68. TEST(JsonLayoutTest, ShowsLocationInfo) {
  69. using namespace logging;
  70. using namespace logging::property;
  71. properties const props{_obj({
  72. {"locationInfo", _v(true)}
  73. })};
  74. auto playout = layouts::instance().get("JsonLayout", props);
  75. std::stringstream ss;
  76. playout->format(ss, {{NOW, 123456}, level::warning, log_here, "UnitTest",
  77. "This is a test message"});
  78. using testing::Eq;
  79. EXPECT_THAT(ss.str(), Eq(location_output));
  80. }
  81. TEST(JsonLayoutTest, EOLPropertyAppendsNewline) {
  82. using namespace logging;
  83. using namespace logging::property;
  84. properties const props{_obj({
  85. {"eventEol", _v(true)}
  86. })};
  87. auto playout = layouts::instance().get("JsonLayout", props);
  88. std::stringstream ss;
  89. playout->format(ss, {{NOW, 123456}, level::warning, {}, "UnitTest",
  90. "This is a test message"});
  91. using testing::EndsWith;
  92. using testing::StartsWith;
  93. EXPECT_THAT(ss.str(), StartsWith(formatted_output));
  94. EXPECT_THAT(ss.str(), EndsWith(logging::NEWLINE_TOKEN));
  95. }
  96. std::string const struct_output = R"({
  97. "instant" : {
  98. "epochSecond" : 1554401840,
  99. "nanoOfSecond" : 123456000
  100. },
  101. "level" : "WARNING",
  102. "loggerName" : "UnitTest",
  103. "message" : "{225}"
  104. })";
  105. struct example {
  106. int content;
  107. };
  108. std::ostream & operator<<(std::ostream & os, example const & ex) {
  109. return os << '{' << ex.content << '}';
  110. }
  111. std::string const struct_json_output = R"({
  112. "instant" : {
  113. "epochSecond" : 1554401840,
  114. "nanoOfSecond" : 123456000
  115. },
  116. "level" : "WARNING",
  117. "loggerName" : "UnitTest",
  118. "message" : {
  119. "content" : 225
  120. }
  121. })";
  122. struct json_example {
  123. int content;
  124. };
  125. std::ostream & operator<<(std::ostream & os, json_example const & ex) {
  126. return os << '{' << ex.content << '}';
  127. }
  128. Json::Value to_json(json_example const & ex) {
  129. Json::Value json;
  130. json["content"] = ex.content;
  131. return json;
  132. }
  133. TEST(JsonLayoutTest, MessageCanBeLoggedAsJSON) {
  134. using namespace logging;
  135. using namespace logging::property;
  136. properties const props{_obj({
  137. {"objectMessageAsJsonObject", _v(true)}
  138. })};
  139. auto playout = layouts::instance().get("JsonLayout", props);
  140. std::stringstream ss;
  141. json_example ex{225};
  142. playout->format(ss, {{NOW, 123456}, level::warning, {}, "UnitTest",
  143. {"This is a test message", ex}});
  144. using testing::Eq;
  145. EXPECT_THAT(ss.str(), Eq(struct_json_output));
  146. }
  147. TEST(JsonLayoutTest, ObjectLackingJsonDefaultsToString) {
  148. using namespace logging;
  149. using namespace logging::property;
  150. properties const props{_obj({
  151. {"objectMessageAsJsonObject", _v(true)}
  152. })};
  153. auto playout = layouts::instance().get("JsonLayout", props);
  154. std::stringstream ss;
  155. example ex{225};
  156. playout->format(ss, {{NOW, 123456}, level::warning, {}, "UnitTest",
  157. {"This is a test message", ex}});
  158. using testing::Eq;
  159. EXPECT_THAT(ss.str(), Eq(struct_output));
  160. }
  161. #include "header_test_obj.h"
  162. #include "logger/logger.h"
  163. using namespace logging;
  164. using namespace logging::property;
  165. properties const JSON_HEADER_SCHEMA{_obj({
  166. {"configuration", _obj({
  167. {"appenders", _obj({
  168. {"Stub", _obj({
  169. {"JsonLayout", _obj({
  170. {"complete", _v(true)},
  171. {"compact", _v(true)},
  172. {"eventEol", _v(true)}
  173. })}
  174. })}
  175. })},
  176. {"loggers", _obj({
  177. {"root", _obj({{"appenders", _obj({{"ref", _v("Stub")}})}})}
  178. })}
  179. })}
  180. })};
  181. using JsonLayoutHeaderTest = HeaderFooterTest;
  182. TEST_F(JsonLayoutHeaderTest, ProvidesArrayBounds) {
  183. {
  184. manager mgr;
  185. mgr.configure(JSON_HEADER_SCHEMA);
  186. }
  187. using testing::Eq;
  188. EXPECT_THAT(appender->sstream.str(), Eq("[]"));
  189. }
  190. TEST_F(JsonLayoutHeaderTest, SeparatesLogsWithComma) {
  191. using testing::Eq;
  192. using testing::EndsWith;
  193. using testing::StartsWith;
  194. manager mgr;
  195. mgr.configure(JSON_HEADER_SCHEMA);
  196. mgr.get().log(level::error, "HELLO");
  197. // Newline is printed as a part of the message...
  198. EXPECT_THAT(appender->sstream.str(), EndsWith("}\n"));
  199. appender->sstream.str("");
  200. mgr.get().log(level::error, "HELLO");
  201. // So the dividing comma gets attached to the next log
  202. EXPECT_THAT(appender->sstream.str(), StartsWith(",{"));
  203. }