proxygen
CustomLogFormatterTest.cpp
Go to the documentation of this file.
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 <cstdlib>
17 
18 #include <folly/init/Init.h>
21 #include <folly/logging/Logger.h>
22 #include <folly/logging/LoggerDB.h>
25 
26 using namespace folly;
27 
28 namespace {
29 
30 const std::string kGlogFormatString =
31  "{L}{m:02d}{D:02d} {H:2d}:{M:02d}:{S:02d}.{USECS:06d} "
32  "{THREAD:5d} {FILE}:{LINE}]";
33 
34 const std::string kGlogFormatStringWithFunctionName =
35  "{L}{m:02d}{D:02d} {H:2d}:{M:02d}:{S:02d}.{USECS:06d} "
36  "{THREAD:5d} {FILE}:{LINE} {FUN}()]";
37 
48 std::string formatMsg(
50  LogLevel level,
51  StringPiece msg,
52  StringPiece filename,
53  StringPiece functionName,
54  unsigned int lineNumber,
55  bool colored = false,
56  // Default timestamp: 2017-04-17 13:45:56.123456 UTC
57  uint64_t timestampNS = 1492436756123456789ULL) {
59  auto* category = db.getCategory("test");
60  CustomLogFormatter formatter{formatString, colored};
61 
62  std::chrono::system_clock::time_point logTimePoint{
63  std::chrono::duration_cast<std::chrono::system_clock::duration>(
64  std::chrono::nanoseconds{timestampNS})};
65  LogMessage logMessage{category,
66  level,
67  logTimePoint,
68  filename,
69  lineNumber,
70  functionName,
71  msg.str()};
72 
73  return formatter.formatMessage(logMessage, category);
74 }
75 } // namespace
76 
78  auto tid = getOSThreadID();
79 
80  // Test a very simple single-line log message
81  auto expected = folly::sformat(
82  "W0417 13:45:56.123456 {:5d} myfile.cpp:1234] hello world\n", tid);
83  EXPECT_EQ(
84  expected,
85  formatMsg(
86  kGlogFormatString,
88  "hello world",
89  "myfile.cpp",
90  "testFunction",
91  1234));
92 }
93 
95  auto tid = getOSThreadID();
96 
97  // Make sure only the file basename gets logged
98  auto expected = folly::sformat(
99  "W0417 13:45:56.123456 {:5d} myfile.cpp:1234] hello world\n", tid);
100  EXPECT_EQ(
101  expected,
102  formatMsg(
103  kGlogFormatString,
105  "hello world",
106  "src/test/logging/code/myfile.cpp",
107  "testFunction",
108  1234));
109 
110  // Log a message with a very long file name.
111  expected = folly::sformat(
112  "W0417 13:45:56.123456 {:5d} "
113  "this_is_a_really_long_file_name_that_will_probably_exceed_"
114  "our_buffer_allocation_guess.cpp:123456789] oh noes\n",
115  tid);
116  EXPECT_EQ(
117  expected,
118  formatMsg(
119  kGlogFormatString,
121  "oh noes",
122  "this_is_a_really_long_file_name_that_will_probably_exceed_"
123  "our_buffer_allocation_guess.cpp",
124  "testFunction",
125  123456789));
126 }
127 
128 TEST(CustomLogFormatter, functionName) {
129  auto tid = getOSThreadID();
130 
131  // Make sure the function name gets logged
132  auto expected = folly::sformat(
133  "W0417 13:45:56.123456 {:5d} myfile.cpp:1234 testFunction()] "
134  "hello world\n",
135  tid);
136  EXPECT_EQ(
137  expected,
138  formatMsg(
139  kGlogFormatStringWithFunctionName,
141  "hello world",
142  "src/test/logging/code/myfile.cpp",
143  "testFunction",
144  1234));
145 }
146 
148  auto tid = getOSThreadID();
149  std::map<std::string, std::string> formatMap{
150  {"tid", folly::to<std::string>(tid)}};
151 
152  // Log a multi-line message
153  auto expected = folly::svformat(
154  "V0417 13:45:56.123456 {tid:>5s} rodent.cpp:777] Eeek, a mouse!\n"
155  "V0417 13:45:56.123456 {tid:>5s} rodent.cpp:777] . .\n"
156  "V0417 13:45:56.123456 {tid:>5s} rodent.cpp:777] ( ).( )\n"
157  "V0417 13:45:56.123456 {tid:>5s} rodent.cpp:777] (o o) .-._.'\n"
158  "V0417 13:45:56.123456 {tid:>5s} rodent.cpp:777] ( - )\n"
159  "V0417 13:45:56.123456 {tid:>5s} rodent.cpp:777] mm mm\n"
160  "V0417 13:45:56.123456 {tid:>5s} rodent.cpp:777] \n"
161  "V0417 13:45:56.123456 {tid:>5s} rodent.cpp:777] =============\n",
162  formatMap);
163  EXPECT_EQ(
164  expected,
165  formatMsg(
166  kGlogFormatString,
168  "Eeek, a mouse!\n"
169  " . .\n"
170  " ( ).( )\n"
171  " (o o) .-._.'\n"
172  " ( - )\n"
173  " mm mm\n"
174  "\n"
175  "=============",
176  "src/rodent.cpp",
177  "testFunction",
178  777));
179 }
180 
181 TEST(CustomLogFormatter, singleNewline) {
182  auto tid = getOSThreadID();
183  std::map<std::string, std::string> formatMap{
184  {"tid", folly::to<std::string>(tid)}};
185 
186  // Logging a single newline is basically two empty strings.
187  auto expected = folly::svformat(
188  "V0417 13:45:56.123456 {tid:>5s} foo.txt:123] \n"
189  "V0417 13:45:56.123456 {tid:>5s} foo.txt:123] \n",
190  formatMap);
191  EXPECT_EQ(
192  expected,
193  formatMsg(
194  kGlogFormatString,
196  "\n",
197  "foo.txt",
198  "testFunction",
199  123));
200 }
201 
203  auto tid = getOSThreadID();
204  std::map<std::string, std::string> formatMap{
205  {"tid", folly::to<std::string>(tid)}};
206 
207  { // Logging a DBG9 message should log in grey color (\033[1;30m) and should
208  // call reset color at the end of the message (\033[0m)
209  auto expected = folly::svformat(
210  "\033[1;30mV0417 13:45:56.123456 {tid:>5s} foo.txt:123] DBG9\033[0m\n",
211  formatMap);
212  EXPECT_EQ(
213  expected,
214  formatMsg(
215  kGlogFormatString,
217  "DBG9",
218  "foo.txt",
219  "testFunction",
220  123,
221  true));
222  }
223  { // Logging an INFO message when coloring enabled is displayed as regular
224  // message. i.e. no color or reset sequence
225  auto expected = folly::svformat(
226  "I0417 13:45:56.123456 {tid:>5s} foo.txt:123] INFO\n", formatMap);
227  EXPECT_EQ(
228  expected,
229  formatMsg(
230  kGlogFormatString,
232  "INFO",
233  "foo.txt",
234  "testFunction",
235  123,
236  true));
237  }
238  { // Logging a WARN message should log in yellow color (\033[33m) and should
239  // call reset color at the end of the message (\033[0m)
240  auto expected = folly::svformat(
241  "\033[33mW0417 13:45:56.123456 {tid:>5s} foo.txt:123] WARN\033[0m\n",
242  formatMap);
243  EXPECT_EQ(
244  expected,
245  formatMsg(
246  kGlogFormatString,
248  "WARN",
249  "foo.txt",
250  "testFunction",
251  123,
252  true));
253  }
254  { // Logging a ERR message should log in red color (\033[31m) and should
255  // call reset color at the end of the message (\033[0m)
256  auto expected = folly::svformat(
257  "\033[31mE0417 13:45:56.123456 {tid:>5s} foo.txt:123] ERR\033[0m\n",
258  formatMap);
259  EXPECT_EQ(
260  expected,
261  formatMsg(
262  kGlogFormatString,
264  "ERR",
265  "foo.txt",
266  "testFunction",
267  123,
268  true));
269  }
270  { // Logging a CRITICAL message should log bold in red background (\033[1;41m)
271  // and should call reset color at the end of the message (\033[0m)
272  auto expected = folly::svformat(
273  "\033[1;41mC0417 13:45:56.123456 {tid:>5s} foo.txt:123] "
274  "CRITICAL\033[0m\n",
275  formatMap);
276  EXPECT_EQ(
277  expected,
278  formatMsg(
279  kGlogFormatString,
281  "CRITICAL",
282  "foo.txt",
283  "testFunction",
284  123,
285  true));
286  }
287  { // Logging a FATAL message should log bold in red background (\033[1;41m)
288  // and should call reset color at the end of the message (\033[0m)
289  auto expected = folly::svformat(
290  "\033[1;41mF0417 13:45:56.123456 {tid:>5s} foo.txt:123] "
291  "FATAL\033[0m\n",
292  formatMap);
293  EXPECT_EQ(
294  expected,
295  formatMsg(
296  kGlogFormatString,
298  "FATAL",
299  "foo.txt",
300  "testFunction",
301  123,
302  true));
303  }
304 }
305 
306 TEST(CustomLogFormatter, unprintableChars) {
307  auto tid = getOSThreadID();
308 
309  // Unprintable characters should be backslash escaped, as should backslashes.
310  auto expected = folly::sformat(
311  "E0417 13:45:56.123456 {:5d} escapes.cpp:97] foo\\x07bar\\x1btest\n",
312  tid);
313  EXPECT_EQ(
314  expected,
315  formatMsg(
316  kGlogFormatString,
318  "foo\abar\x1btest",
319  "escapes.cpp",
320  "testFunction",
321  97));
322  expected = folly::sformat(
323  "I0417 13:45:56.123456 {:5d} escapes.cpp:98] foo\\\\bar\"test\n", tid);
324  EXPECT_EQ(
325  expected,
326  formatMsg(
327  kGlogFormatString,
329  "foo\\bar\"test",
330  "escapes.cpp",
331  "testFunction",
332  98));
333  expected = folly::sformat(
334  "C0417 13:45:56.123456 {:5d} escapes.cpp:99] nul\\x00byte\n", tid);
335  EXPECT_EQ(
336  expected,
337  formatMsg(
338  kGlogFormatString,
340  std::string("nul\0byte", 8),
341  "escapes.cpp",
342  "testFunction",
343  99));
344 }
345 
346 TEST(CustomLogFormatter, invalidFormatStrings) {
347  EXPECT_THROW(CustomLogFormatter("{", false), std::runtime_error);
348  EXPECT_THROW(CustomLogFormatter("{}", false), std::runtime_error);
349  EXPECT_THROW(CustomLogFormatter("{0}", false), std::runtime_error);
350  EXPECT_THROW(CustomLogFormatter("{WRONG}", false), std::runtime_error);
351  EXPECT_THROW(CustomLogFormatter("{L} {FORMAT}", false), std::runtime_error);
352  EXPECT_THROW(CustomLogFormatter("{L}{m:1} {KEY}", false), std::runtime_error);
353  EXPECT_THROW(CustomLogFormatter("{L", false), std::runtime_error);
354 }
355 
356 TEST(CustomLogFormatter, validFormatStringsEdgeCases) {
357  EXPECT_EQ("msg\n", formatMsg("", LogLevel::INFO, "msg", "file", "fun", 99));
358  EXPECT_EQ(
359  "I msg\n", formatMsg("{L}", LogLevel::INFO, "msg", "file", "fun", 99));
360  EXPECT_EQ(
361  "{L} msg\n",
362  formatMsg("{{L}}", LogLevel::INFO, "msg", "file", "fun", 99));
363  EXPECT_EQ(
364  "E:099 msg\n",
365  formatMsg("{L}:{LINE:03}", LogLevel::ERR, "msg", "file", "fun", 99));
366  EXPECT_EQ(
367  "{L}:099 msg\n",
368  formatMsg("{{L}}:{LINE:03}", LogLevel::ERR, "msg", "file", "fun", 99));
369  EXPECT_EQ(
370  "E:099{ msg\n",
371  formatMsg("{L}:{LINE:03}{{", LogLevel::ERR, "msg", "file", "fun", 99));
372  EXPECT_EQ(
373  "E:099} msg\n",
374  formatMsg("{L}:{LINE:03}}}", LogLevel::ERR, "msg", "file", "fun", 99));
375  EXPECT_EQ(
376  "{E} msg\n",
377  formatMsg("{{{L}}}", LogLevel::ERR, "msg", "file", "fun", 99));
378 }
379 
380 int main(int argc, char* argv[]) {
381  testing::InitGoogleTest(&argc, argv);
382  folly::init(&argc, &argv);
383 
384  // Some of our tests check timestamps emitted by the formatter.
385  // Set the timezone to a consistent value so that the tests are not
386  // affected by the local time of the user running the test.
387  //
388  // UTC is the only timezone that we can really rely on to work consistently.
389  // This will work even in the absence of a proper tzdata installation on the
390  // local system.
391  setenv("TZ", "UTC", 1);
392 
393  return RUN_ALL_TESTS();
394 }
std::string str() const
Definition: Range.h:591
#define EXPECT_THROW(statement, expected_exception)
Definition: gtest.h:1843
int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_
Definition: gtest.h:2232
std::string sformat(StringPiece fmt, Args &&...args)
Definition: Format.h:280
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
int main(int argc, char *argv[])
uint64_t getOSThreadID()
Definition: ThreadId.h:80
void formatString(StringPiece val, FormatArg &arg, FormatCallback &cb)
Definition: Format-inl.h:298
void init(int *argc, char ***argv, bool removeFlags)
Definition: Init.cpp:34
char ** argv
LogLevel
Definition: LogLevel.h:38
std::string svformat(StringPiece fmt, Container &&container)
Definition: Format.h:307
const char * string
Definition: Conv.cpp:212
GTEST_API_ void InitGoogleTest(int *argc, char **argv)
Definition: gtest.cc:5370
TEST(SequencedExecutor, CPUThreadPoolExecutor)