XlogTest.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /*
  2. * Copyright 2017-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <folly/logging/xlog.h>
  17. #include <folly/logging/LogConfigParser.h>
  18. #include <folly/logging/LogHandler.h>
  19. #include <folly/logging/LogMessage.h>
  20. #include <folly/logging/LoggerDB.h>
  21. #include <folly/logging/test/TestLogHandler.h>
  22. #include <folly/logging/test/XlogHeader1.h>
  23. #include <folly/logging/test/XlogHeader2.h>
  24. #include <folly/portability/Constexpr.h>
  25. #include <folly/portability/GMock.h>
  26. #include <folly/portability/GTest.h>
  27. #include <folly/test/TestUtils.h>
  28. #include <chrono>
  29. #include <thread>
  30. using namespace folly;
  31. using std::make_shared;
  32. using testing::ElementsAre;
  33. using testing::ElementsAreArray;
  34. using namespace std::chrono_literals;
  35. XLOG_SET_CATEGORY_NAME("xlog_test.main_file")
  36. namespace {
  37. class XlogTest : public testing::Test {
  38. public:
  39. XlogTest() {
  40. // Note that the XLOG* macros always use the main LoggerDB singleton.
  41. // There is no way to get them to use a test LoggerDB during unit tests.
  42. //
  43. // In order to ensure that changes to the LoggerDB singleton do not persist
  44. // across test functions we reset the configuration to a fixed state before
  45. // each test starts.
  46. auto config =
  47. parseLogConfig(".=WARN:default; default=stream:stream=stderr");
  48. LoggerDB::get().resetConfig(config);
  49. }
  50. };
  51. } // namespace
  52. TEST_F(XlogTest, xlogName) {
  53. EXPECT_EQ("xlog_test.main_file", XLOG_GET_CATEGORY_NAME());
  54. EXPECT_EQ("xlog_test.main_file", XLOG_GET_CATEGORY()->getName());
  55. }
  56. TEST_F(XlogTest, xlogIf) {
  57. auto handler = make_shared<TestLogHandler>();
  58. LoggerDB::get().getCategory("xlog_test")->addHandler(handler);
  59. auto& messages = handler->getMessages();
  60. // info messages are not enabled initially.
  61. EXPECT_FALSE(XLOG_IS_ON(INFO));
  62. EXPECT_TRUE(XLOG_IS_ON(ERR));
  63. XLOG_IF(INFO, false, "testing 1");
  64. EXPECT_EQ(0, messages.size());
  65. messages.clear();
  66. XLOG_IF(INFO, true, "testing 1");
  67. EXPECT_EQ(0, messages.size());
  68. messages.clear();
  69. // Increase the log level, then log a message.
  70. LoggerDB::get().setLevel("xlog_test.main_file", LogLevel::DBG1);
  71. XLOG_IF(DBG1, false, "testing: ", 1, 2, 3);
  72. ASSERT_EQ(0, messages.size());
  73. messages.clear();
  74. XLOG_IF(DBG1, true, "testing: ", 1, 2, 3);
  75. ASSERT_EQ(1, messages.size());
  76. messages.clear();
  77. // more complex conditional expressions
  78. std::array<bool, 2> conds = {{false, true}};
  79. for (unsigned i = 0; i < conds.size(); i++) {
  80. for (unsigned j = 0; j < conds.size(); j++) {
  81. XLOG_IF(DBG1, conds[i] && conds[j], "testing conditional");
  82. EXPECT_EQ((conds[i] && conds[j]) ? 1 : 0, messages.size());
  83. messages.clear();
  84. XLOG_IF(DBG1, conds[i] || conds[j], "testing conditional");
  85. EXPECT_EQ((conds[i] || conds[j]) ? 1 : 0, messages.size());
  86. messages.clear();
  87. }
  88. }
  89. XLOG_IF(DBG1, 0x6 & 0x2, "More conditional 1");
  90. EXPECT_EQ(1, messages.size());
  91. messages.clear();
  92. XLOG_IF(DBG1, 0x6 | 0x2, "More conditional 2");
  93. EXPECT_EQ(1, messages.size());
  94. messages.clear();
  95. XLOG_IF(DBG1, 0x6 | 0x2 ? true : false, "More conditional 3");
  96. EXPECT_EQ(1, messages.size());
  97. messages.clear();
  98. XLOG_IF(DBG1, 0x6 | 0x2 ? true : false, "More conditional 3");
  99. EXPECT_EQ(1, messages.size());
  100. messages.clear();
  101. XLOG_IF(DBG1, 0x3 & 0x4 ? true : false, "More conditional 4");
  102. EXPECT_EQ(0, messages.size());
  103. messages.clear();
  104. XLOG_IF(DBG1, false ? true : false, "More conditional 5");
  105. EXPECT_EQ(0, messages.size());
  106. messages.clear();
  107. XLOGF_IF(DBG1, false, "number: {:>3d}; string: {}", 12, "foo");
  108. ASSERT_EQ(0, messages.size());
  109. messages.clear();
  110. XLOGF_IF(DBG1, true, "number: {:>3d}; string: {}", 12, "foo");
  111. ASSERT_EQ(1, messages.size());
  112. messages.clear();
  113. }
  114. TEST_F(XlogTest, xlog) {
  115. auto handler = make_shared<TestLogHandler>();
  116. LoggerDB::get().getCategory("xlog_test")->addHandler(handler);
  117. auto& messages = handler->getMessages();
  118. // info messages are not enabled initially.
  119. EXPECT_FALSE(XLOG_IS_ON(INFO));
  120. EXPECT_TRUE(XLOG_IS_ON(ERR));
  121. XLOG(INFO, "testing 1");
  122. EXPECT_EQ(0, messages.size());
  123. messages.clear();
  124. // Increase the log level, then log a message.
  125. LoggerDB::get().setLevel("xlog_test.main_file", LogLevel::DBG1);
  126. XLOG(DBG1, "testing: ", 1, 2, 3);
  127. ASSERT_EQ(1, messages.size());
  128. EXPECT_EQ("testing: 123", messages[0].first.getMessage());
  129. EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogTest.cpp"))
  130. << "unexpected file name: " << messages[0].first.getFileName();
  131. EXPECT_EQ(LogLevel::DBG1, messages[0].first.getLevel());
  132. EXPECT_EQ("xlog_test.main_file", messages[0].first.getCategory()->getName());
  133. EXPECT_EQ("xlog_test", messages[0].second->getName());
  134. messages.clear();
  135. XLOGF(WARN, "number: {:>3d}; string: {}", 12, "foo");
  136. ASSERT_EQ(1, messages.size());
  137. EXPECT_EQ("number: 12; string: foo", messages[0].first.getMessage());
  138. EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogTest.cpp"))
  139. << "unexpected file name: " << messages[0].first.getFileName();
  140. EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
  141. EXPECT_EQ("xlog_test.main_file", messages[0].first.getCategory()->getName());
  142. EXPECT_EQ("xlog_test", messages[0].second->getName());
  143. messages.clear();
  144. XLOG(DBG2, "this log check should not pass");
  145. EXPECT_EQ(0, messages.size());
  146. messages.clear();
  147. // Test stream arguments to XLOG()
  148. XLOG(INFO) << "stream test: " << 1 << ", two, " << 3;
  149. ASSERT_EQ(1, messages.size());
  150. EXPECT_EQ("stream test: 1, two, 3", messages[0].first.getMessage());
  151. EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogTest.cpp"))
  152. << "unexpected file name: " << messages[0].first.getFileName();
  153. EXPECT_EQ(LogLevel::INFO, messages[0].first.getLevel());
  154. EXPECT_EQ("xlog_test.main_file", messages[0].first.getCategory()->getName());
  155. EXPECT_EQ("xlog_test", messages[0].second->getName());
  156. messages.clear();
  157. }
  158. TEST_F(XlogTest, perFileCategoryHandling) {
  159. using namespace logging_test;
  160. auto handler = make_shared<TestLogHandler>();
  161. LoggerDB::get().getCategory("folly.logging.test")->addHandler(handler);
  162. LoggerDB::get().setLevel("folly.logging.test", LogLevel::DBG9);
  163. auto& messages = handler->getMessages();
  164. // Use the simple helper function in XlogHeader2
  165. testXlogHdrFunction("factor", 99);
  166. ASSERT_EQ(1, messages.size());
  167. EXPECT_EQ("test: factor=99", messages[0].first.getMessage());
  168. EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogHeader2.h"))
  169. << "unexpected file name: " << messages[0].first.getFileName();
  170. EXPECT_EQ(LogLevel::DBG3, messages[0].first.getLevel());
  171. EXPECT_EQ(
  172. "folly.logging.test.XlogHeader2.h",
  173. messages[0].first.getCategory()->getName());
  174. EXPECT_EQ("folly.logging.test", messages[0].second->getName());
  175. messages.clear();
  176. // Test the loop function from XlogHeader1
  177. testXlogHdrLoop(3, "hello world");
  178. ASSERT_EQ(5, messages.size());
  179. EXPECT_EQ("starting: hello world", messages[0].first.getMessage());
  180. EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogHeader1.h"))
  181. << "unexpected file name: " << messages[0].first.getFileName();
  182. EXPECT_EQ(LogLevel::DBG1, messages[0].first.getLevel());
  183. EXPECT_EQ(
  184. "folly.logging.test.XlogHeader1.h",
  185. messages[0].first.getCategory()->getName());
  186. EXPECT_EQ("folly.logging.test", messages[0].second->getName());
  187. EXPECT_EQ("test: hello world", messages[1].first.getMessage());
  188. EXPECT_EQ("test: hello world", messages[2].first.getMessage());
  189. EXPECT_EQ("test: hello world", messages[3].first.getMessage());
  190. EXPECT_EQ("finished: hello world", messages[4].first.getMessage());
  191. EXPECT_EQ(LogLevel::DBG5, messages[1].first.getLevel());
  192. EXPECT_EQ(LogLevel::DBG5, messages[2].first.getLevel());
  193. EXPECT_EQ(LogLevel::DBG5, messages[3].first.getLevel());
  194. EXPECT_EQ(LogLevel::DBG1, messages[4].first.getLevel());
  195. messages.clear();
  196. // Reduce the log level so that the messages inside the loop
  197. // should not be logged.
  198. LoggerDB::get().setLevel("folly.logging.test", LogLevel::DBG2);
  199. testXlogHdrLoop(300, "hello world");
  200. ASSERT_EQ(2, messages.size());
  201. EXPECT_EQ("starting: hello world", messages[0].first.getMessage());
  202. EXPECT_EQ("finished: hello world", messages[1].first.getMessage());
  203. messages.clear();
  204. // Call the helpers function in XlogFile1.cpp and XlogFile2.cpp and makes
  205. // sure their categories are reported correctly.
  206. testXlogFile1Dbg1("foobar 1234");
  207. ASSERT_EQ(1, messages.size());
  208. EXPECT_EQ("file1: foobar 1234", messages[0].first.getMessage());
  209. EXPECT_EQ(
  210. "folly.logging.test.XlogFile1.cpp",
  211. messages[0].first.getCategory()->getName());
  212. messages.clear();
  213. testXlogFile2Dbg1("hello world");
  214. ASSERT_EQ(1, messages.size());
  215. EXPECT_EQ("file2: hello world", messages[0].first.getMessage());
  216. EXPECT_EQ(
  217. "folly.logging.test.XlogFile2.cpp",
  218. messages[0].first.getCategory()->getName());
  219. messages.clear();
  220. // Adjust the log level and make sure the changes take effect for the .cpp
  221. // file categories
  222. LoggerDB::get().setLevel("folly.logging.test", LogLevel::INFO);
  223. testXlogFile1Dbg1("log check should fail now");
  224. testXlogFile2Dbg1("this should fail too");
  225. EXPECT_EQ(0, messages.size());
  226. messages.clear();
  227. LoggerDB::get().setLevel("folly.logging.test.XlogFile1", LogLevel::DBG1);
  228. testXlogFile1Dbg1("this log check should pass now");
  229. testXlogFile2Dbg1("but this one should still fail");
  230. ASSERT_EQ(1, messages.size());
  231. EXPECT_EQ(
  232. "file1: this log check should pass now", messages[0].first.getMessage());
  233. EXPECT_EQ(
  234. "folly.logging.test.XlogFile1.cpp",
  235. messages[0].first.getCategory()->getName());
  236. messages.clear();
  237. }
  238. TEST_F(XlogTest, rateLimiting) {
  239. auto handler = make_shared<TestLogHandler>();
  240. LoggerDB::get().getCategory("xlog_test")->addHandler(handler);
  241. LoggerDB::get().setLevel("xlog_test", LogLevel::DBG1);
  242. // Test XLOG_EVERY_N
  243. for (size_t n = 0; n < 50; ++n) {
  244. XLOG_EVERY_N(DBG1, 7, "msg ", n);
  245. }
  246. EXPECT_THAT(
  247. handler->getMessageValues(),
  248. ElementsAre(
  249. "msg 0",
  250. "msg 7",
  251. "msg 14",
  252. "msg 21",
  253. "msg 28",
  254. "msg 35",
  255. "msg 42",
  256. "msg 49"));
  257. handler->clearMessages();
  258. // Test XLOG_EVERY_MS and XLOG_N_PER_MS
  259. // We test these together to minimize the number of sleep operations.
  260. for (size_t n = 0; n < 10; ++n) {
  261. // Integer arguments are treated as millisecond
  262. XLOG_EVERY_MS(DBG1, 100, "int arg ", n);
  263. // Other duration arguments also work, as long as they are
  264. // coarser than milliseconds
  265. XLOG_EVERY_MS(DBG1, 100ms, "ms arg ", n);
  266. XLOG_EVERY_MS(DBG1, 1s, "s arg ", n);
  267. // Use XLOG_N_PER_MS() too
  268. XLOG_N_PER_MS(DBG1, 2, 100, "2x int arg ", n);
  269. XLOG_N_PER_MS(DBG1, 1, 100ms, "1x ms arg ", n);
  270. XLOG_N_PER_MS(DBG1, 3, 1s, "3x s arg ", n);
  271. // Sleep for 100ms between iterations 5 and 6
  272. if (n == 5) {
  273. /* sleep override */ std::this_thread::sleep_for(110ms);
  274. }
  275. }
  276. EXPECT_THAT(
  277. handler->getMessageValues(),
  278. ElementsAreArray({
  279. "int arg 0",
  280. "ms arg 0",
  281. "s arg 0",
  282. "2x int arg 0",
  283. "1x ms arg 0",
  284. "3x s arg 0",
  285. "2x int arg 1",
  286. "3x s arg 1",
  287. "3x s arg 2",
  288. "int arg 6",
  289. "ms arg 6",
  290. "2x int arg 6",
  291. "1x ms arg 6",
  292. "2x int arg 7",
  293. }));
  294. handler->clearMessages();
  295. }
  296. TEST_F(XlogTest, getXlogCategoryName) {
  297. EXPECT_EQ("foo.cpp", getXlogCategoryNameForFile("foo.cpp"));
  298. EXPECT_EQ("foo.h", getXlogCategoryNameForFile("foo.h"));
  299. // Directory separators should be translated to "." during LogName
  300. // canonicalization
  301. EXPECT_EQ("src/test/foo.cpp", getXlogCategoryNameForFile("src/test/foo.cpp"));
  302. EXPECT_EQ(
  303. "src.test.foo.cpp",
  304. LogName::canonicalize(getXlogCategoryNameForFile("src/test/foo.cpp")));
  305. EXPECT_EQ("src/test/foo.h", getXlogCategoryNameForFile("src/test/foo.h"));
  306. EXPECT_EQ(
  307. "src.test.foo.h",
  308. LogName::canonicalize(getXlogCategoryNameForFile("src/test/foo.h")));
  309. // Buck's directory prefixes for generated source files
  310. // should be stripped out
  311. EXPECT_EQ(
  312. "myproject.generated_header.h",
  313. LogName::canonicalize(getXlogCategoryNameForFile(
  314. "buck-out/gen/myproject#headers/myproject/generated_header.h")));
  315. EXPECT_EQ(
  316. "foo.bar.test.h",
  317. LogName::canonicalize(getXlogCategoryNameForFile(
  318. "buck-out/gen/foo/bar#header-map,headers/foo/bar/test.h")));
  319. }
  320. TEST(Xlog, xlogStripFilename) {
  321. EXPECT_STREQ("c/d.txt", xlogStripFilename("/a/b/c/d.txt", "/a/b"));
  322. EXPECT_STREQ("c/d.txt", xlogStripFilename("/a/b/c/d.txt", "/a/b/"));
  323. EXPECT_STREQ(
  324. "ships/cruiser.cpp",
  325. xlogStripFilename(
  326. "/home/johndoe/src/spacesim/ships/cruiser.cpp",
  327. "/home/johndoe/src/spacesim"));
  328. EXPECT_STREQ(
  329. "ships/cruiser.cpp",
  330. xlogStripFilename("src/spacesim/ships/cruiser.cpp", "src/spacesim"));
  331. // Test with multiple prefixes
  332. EXPECT_STREQ("c/d.txt", xlogStripFilename("/a/b/c/d.txt", "/x/y:1/2:/a/b"));
  333. EXPECT_STREQ("c/d.txt", xlogStripFilename("/a/b/c/d.txt", "/x/y:/a/b:/1/2"));
  334. EXPECT_STREQ(
  335. "/foobar/src/test.cpp", xlogStripFilename("/foobar/src/test.cpp", "/foo"))
  336. << "should only strip full directory name matches";
  337. EXPECT_STREQ(
  338. "src/test.cpp",
  339. xlogStripFilename("/foobar/src/test.cpp", "/foo:/foobar"));
  340. EXPECT_STREQ(
  341. "/a/b/c/d.txt", xlogStripFilename("/a/b/c/d.txt", "/a/b/c/d.txt"))
  342. << "should not strip if the result will be empty";
  343. EXPECT_STREQ("c/d.txt", xlogStripFilename("/a/b/c/d.txt", ":/x/y::/a/b:"))
  344. << "empty prefixes in the prefix list should be ignored";
  345. EXPECT_STREQ("d.txt", xlogStripFilename("/a/b/c/d.txt", "/a/b/c:/a"))
  346. << "only the first prefix match should be honored";
  347. EXPECT_STREQ("b/c/d.txt", xlogStripFilename("/a/b/c/d.txt", "/a:/a/b/c"))
  348. << "only the first prefix match should be honored";
  349. // xlogStripFilename() should ideally be a purely compile-time evaluation.
  350. // Use a static_assert() to ensure that it can be evaluated at compile time.
  351. // We use EXPECT_STREQ() checks above for most of the testing since it
  352. // produces nicer messages on failure.
  353. static_assert(
  354. constexpr_strcmp(
  355. xlogStripFilename("/my/project/src/test.cpp", "/my/project"),
  356. "src/test.cpp") == 0,
  357. "incorrect xlogStripFilename() behavior");
  358. }