proxygen
LoggerTest.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 <folly/logging/Logger.h>
20 #include <folly/logging/LoggerDB.h>
24 #include <folly/test/TestUtils.h>
25 
26 using namespace folly;
27 using std::make_shared;
29 
30 class LoggerTest : public ::testing::Test {
31  protected:
32  void SetUp() override {
33  auto* category = logger_.getCategory();
34 
35  handler_ = make_shared<TestLogHandler>();
36  category->addHandler(handler_);
37  category->setLevel(LogLevel::DBG, true);
38  }
39 
41  auto idx = path.rfind('/');
42  if (idx == StringPiece::npos) {
43  return path.str();
44  }
45  return path.subpiece(idx + 1);
46  }
47 
49  Logger logger_{&db_, "test"};
50  std::shared_ptr<TestLogHandler> handler_;
51 };
52 
54  // Simple log message
55  auto expectedLine = __LINE__ + 1;
56  FB_LOG(logger_, WARN, "hello world");
57 
58  auto& messages = handler_->getMessages();
59  ASSERT_EQ(1, messages.size());
60  EXPECT_EQ("hello world", messages[0].first.getMessage());
61  EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
62  EXPECT_EQ(expectedLine, messages[0].first.getLineNumber());
63  EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
64  EXPECT_FALSE(messages[0].first.containsNewlines());
65  EXPECT_EQ(logger_.getCategory(), messages[0].first.getCategory());
66  EXPECT_EQ(logger_.getCategory(), messages[0].second);
67 }
68 
69 TEST_F(LoggerTest, subCategory) {
70  // Log from a sub-category.
71  Logger subLogger{&db_, "test.foo.bar"};
72  auto expectedLine = __LINE__ + 1;
73  FB_LOG(subLogger, ERR, "sub-category\nlog message");
74 
75  auto& messages = handler_->getMessages();
76  ASSERT_EQ(1, messages.size());
77  EXPECT_EQ("sub-category\nlog message", messages[0].first.getMessage());
78  EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
79  EXPECT_EQ(expectedLine, messages[0].first.getLineNumber());
80  EXPECT_EQ(LogLevel::ERR, messages[0].first.getLevel());
81  EXPECT_TRUE(messages[0].first.containsNewlines());
82  EXPECT_EQ(subLogger.getCategory(), messages[0].first.getCategory());
83  EXPECT_EQ(logger_.getCategory(), messages[0].second);
84 }
85 
86 TEST_F(LoggerTest, formatMessage) {
87  auto expectedLine = __LINE__ + 1;
88  FB_LOGF(logger_, WARN, "num events: {:06d}, duration: {:6.3f}", 1234, 5.6789);
89 
90  auto& messages = handler_->getMessages();
91  ASSERT_EQ(1, messages.size());
92  EXPECT_EQ(
93  "num events: 001234, duration: 5.679", messages[0].first.getMessage());
94  EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
95  EXPECT_EQ(expectedLine, messages[0].first.getLineNumber());
96  EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
97  EXPECT_FALSE(messages[0].first.containsNewlines());
98  EXPECT_EQ(logger_.getCategory(), messages[0].first.getCategory());
99  EXPECT_EQ(logger_.getCategory(), messages[0].second);
100 }
101 
102 TEST_F(LoggerTest, follyFormatError) {
103  // If we pass in a bogus format string, logf() should not throw.
104  // It should instead log a message, just complaining about the format error.
105  FB_LOGF(
106  logger_, WARN, "param1: {:06d}, param2: {:6.3f}", 1234, "hello world!");
107 
108  auto& messages = handler_->getMessages();
109  ASSERT_EQ(1, messages.size());
110  // Use a regex match here, since the type IDs are reported slightly
111  // differently on different platforms.
112  EXPECT_THAT(
113  messages[0].first.getMessage(),
114  MatchesRegex(
115  R"(error formatting log message: )"
116  R"(invalid format argument \{:6.3f\}: invalid specifier 'f'; )"
117  R"(format string: "param1: \{:06d\}, param2: \{:6.3f\}", )"
118  R"(arguments: \((.*: )?1234\), \((.*: )?hello world\!\))"));
119  EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
120  EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
121  EXPECT_FALSE(messages[0].first.containsNewlines());
122  EXPECT_EQ(logger_.getCategory(), messages[0].first.getCategory());
123  EXPECT_EQ(logger_.getCategory(), messages[0].second);
124 }
125 
127  // Use the log API that calls folly::to<string>
128  auto expectedLine = __LINE__ + 1;
129  FB_LOG(logger_, DBG5, "status=", 5, " name=", "foobar");
130 
131  auto& messages = handler_->getMessages();
132  ASSERT_EQ(1, messages.size());
133  EXPECT_EQ("status=5 name=foobar", messages[0].first.getMessage());
134  EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
135  EXPECT_EQ(expectedLine, messages[0].first.getLineNumber());
136  EXPECT_EQ(LogLevel::DBG5, messages[0].first.getLevel());
137  EXPECT_FALSE(messages[0].first.containsNewlines());
138  EXPECT_EQ(logger_.getCategory(), messages[0].first.getCategory());
139  EXPECT_EQ(logger_.getCategory(), messages[0].second);
140 }
141 
144 
145 // clang-format off
146 [[noreturn]] void toAppend(
147  const ToStringFailure& /* arg */,
148  std::string* /* result */) {
149  throw std::runtime_error(
150  "error converting ToStringFailure object to a string");
151 }
152 
153 namespace folly {
154 template <>
156  public:
158 
159  template <class FormatCallback>
160  void format(FormatArg& arg, FormatCallback& cb) const {
161  FormatValue<std::string>("ToStringFailure").format(arg, cb);
162  }
163 };
164 
165 template <>
167  public:
169 
170  template <class FormatCallback>
171  void format(FormatArg&, FormatCallback&) const {
172  throw std::runtime_error("test");
173  }
174 };
175 } // namespace folly
176 // clang-format on
177 
178 TEST_F(LoggerTest, toStringError) {
179  // Use the folly::to<string> log API, with an object that will throw
180  // an exception when we try to convert it to a string.
181  //
182  // The logging code should not throw, but should instead log a message
183  // with some detail about the failure.
184  ToStringFailure obj;
185  auto expectedLine = __LINE__ + 1;
186  FB_LOG(logger_, DBG1, "status=", obj, " name=", "foobar");
187 
188  auto& messages = handler_->getMessages();
189  ASSERT_EQ(1, messages.size());
190  EXPECT_EQ(
191  "error constructing log message: "
192  "error converting ToStringFailure object to a string",
193  messages[0].first.getMessage());
194  EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
195  EXPECT_EQ(expectedLine, messages[0].first.getLineNumber());
196  EXPECT_EQ(LogLevel::DBG1, messages[0].first.getLevel());
197  EXPECT_FALSE(messages[0].first.containsNewlines());
198  EXPECT_EQ(logger_.getCategory(), messages[0].first.getCategory());
199  EXPECT_EQ(logger_.getCategory(), messages[0].second);
200 }
201 
202 TEST_F(LoggerTest, formatFallbackError) {
203  // Check the behavior if logf() fails, and toAppend() also fails.
204  ToStringFailure obj;
205  FB_LOGF(logger_, WARN, "param1: {}, param2: {}, {}", 1234, obj);
206 
207  auto& messages = handler_->getMessages();
208  ASSERT_EQ(1, messages.size());
209  EXPECT_THAT(
210  messages[0].first.getMessage(),
211  MatchesRegex(
212  R"(error formatting log message: invalid format argument \{\}: )"
213  R"(argument index out of range, max=2; )"
214  R"(format string: "param1: \{\}, param2: \{\}, \{\}", )"
215  R"(arguments: \((.*: )?1234\), )"
216  R"(\((.*ToStringFailure.*: )?<error_converting_to_string>\))"));
217  EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
218  EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
219  EXPECT_FALSE(messages[0].first.containsNewlines());
220  EXPECT_EQ(logger_.getCategory(), messages[0].first.getCategory());
221  EXPECT_EQ(logger_.getCategory(), messages[0].second);
222 }
223 
224 TEST_F(LoggerTest, formatFallbackUnsupported) {
225  // Check the behavior if logf() fails, and toAppend() also fails.
227  FB_LOGF(logger_, WARN, "param1: {}, param2: {}", 1234, obj);
228 
229  auto& messages = handler_->getMessages();
230  ASSERT_EQ(1, messages.size());
231  EXPECT_THAT(
232  messages[0].first.getMessage(),
233  MatchesRegex(
234  R"(error formatting log message: test; )"
235  R"(format string: "param1: \{\}, param2: \{\}", )"
236  R"(arguments: \((.*: )?1234\), )"
237  R"(\((.*FormattableButNoToString.*: )?<no_string_conversion>\))"));
238  EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
239  EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
240  EXPECT_FALSE(messages[0].first.containsNewlines());
241  EXPECT_EQ(logger_.getCategory(), messages[0].first.getCategory());
242  EXPECT_EQ(logger_.getCategory(), messages[0].second);
243 }
244 
245 TEST_F(LoggerTest, streamingArgs) {
246  auto& messages = handler_->getMessages();
247 
248  // Test with only streaming arguments
249  std::string foo = "bar";
250  FB_LOG(logger_, WARN) << "foo=" << foo << ", test=0x" << std::hex << 35;
251  ASSERT_EQ(1, messages.size());
252  EXPECT_EQ("foo=bar, test=0x23", messages[0].first.getMessage());
253  EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
254  EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
255  EXPECT_FALSE(messages[0].first.containsNewlines());
256  EXPECT_EQ(logger_.getCategory(), messages[0].first.getCategory());
257  EXPECT_EQ(logger_.getCategory(), messages[0].second);
258  messages.clear();
259 
260  // Test with both function-style and streaming arguments
261  FB_LOG(logger_, WARN, "foo=", foo) << " hello, "
262  << "world: " << 34;
263  ASSERT_EQ(1, messages.size());
264  EXPECT_EQ("foo=bar hello, world: 34", messages[0].first.getMessage());
265  EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
266  EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
267  EXPECT_FALSE(messages[0].first.containsNewlines());
268  EXPECT_EQ(logger_.getCategory(), messages[0].first.getCategory());
269  EXPECT_EQ(logger_.getCategory(), messages[0].second);
270  messages.clear();
271 
272  // Test with format-style and streaming arguments
273  FB_LOGF(logger_, WARN, "foo={}, x={}", foo, 34) << ", also " << 12;
274  ASSERT_EQ(1, messages.size());
275  EXPECT_EQ("foo=bar, x=34, also 12", messages[0].first.getMessage());
276  EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
277  EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
278  EXPECT_FALSE(messages[0].first.containsNewlines());
279  EXPECT_EQ(logger_.getCategory(), messages[0].first.getCategory());
280  EXPECT_EQ(logger_.getCategory(), messages[0].second);
281  messages.clear();
282 }
283 
284 TEST_F(LoggerTest, escapeSequences) {
285  // Escape characters (and any other unprintable characters) in the log
286  // message should be escaped when logged.
287  auto expectedLine = __LINE__ + 1;
288  FB_LOG(logger_, WARN, "hello \033[34mworld\033[0m!");
289 
290  auto& messages = handler_->getMessages();
291  ASSERT_EQ(1, messages.size());
292  EXPECT_EQ("hello \\x1b[34mworld\\x1b[0m!", messages[0].first.getMessage());
293  EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
294  EXPECT_EQ(expectedLine, messages[0].first.getLineNumber());
295  EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
296  EXPECT_FALSE(messages[0].first.containsNewlines());
297  EXPECT_EQ(logger_.getCategory(), messages[0].first.getCategory());
298  EXPECT_EQ(logger_.getCategory(), messages[0].second);
299 }
300 
301 TEST_F(LoggerTest, logMacros) {
302  Logger foo{&db_, "test.foo.bar"};
303  Logger foobar{&db_, "test.foo.bar"};
304  Logger footest{&db_, "test.foo.test"};
305  Logger footest1234{&db_, "test.foo.test.1234"};
306  Logger other{&db_, "test.other"};
307  db_.setLevel("test", LogLevel::ERR);
308  db_.setLevel("test.foo", LogLevel::DBG2);
309  db_.setLevel("test.foo.test", LogLevel::DBG7);
310 
311  auto& messages = handler_->getMessages();
312 
313  // test.other's effective level should be INFO, so a DBG0
314  // message to it should be discarded
315  FB_LOG(other, DBG0, "this should be discarded");
316  ASSERT_EQ(0, messages.size());
317 
318  // Disabled log messages should not evaluate their arguments
319  bool argumentEvaluated = false;
320  auto getValue = [&] {
321  argumentEvaluated = true;
322  return 5;
323  };
324  FB_LOG(foobar, DBG3, "discarded message: ", getValue());
325  EXPECT_FALSE(argumentEvaluated);
326 
327  FB_LOG(foobar, DBG1, "this message should pass: ", getValue());
328  ASSERT_EQ(1, messages.size());
329  EXPECT_EQ("this message should pass: 5", messages[0].first.getMessage());
330  EXPECT_TRUE(argumentEvaluated);
331  messages.clear();
332 
333  // Similar checks with FB_LOGF()
334  argumentEvaluated = false;
335  FB_LOGF(footest1234, DBG9, "failing log check: {}", getValue());
336  EXPECT_FALSE(argumentEvaluated);
337 
338  FB_LOGF(footest1234, DBG5, "passing log: {:03}", getValue());
339  ASSERT_EQ(1, messages.size());
340  EXPECT_EQ("passing log: 005", messages[0].first.getMessage());
341  EXPECT_TRUE(argumentEvaluated);
342  messages.clear();
343 
344  // Bad format arguments should not throw
345  FB_LOGF(footest1234, ERR, "whoops: {}, {}", getValue());
346  ASSERT_EQ(1, messages.size());
347  EXPECT_THAT(
348  messages[0].first.getMessage(),
349  MatchesRegex(
350  R"(error formatting log message: invalid format argument \{\}: )"
351  R"(argument index out of range, max=1; )"
352  R"(format string: "whoops: \{\}, \{\}", arguments: \((.*: )?5\))"));
353  messages.clear();
354 }
355 
356 TEST_F(LoggerTest, logRawMacros) {
357  Logger foobar{&db_, "test.foo.bar"};
358  db_.setLevel("test.foo", LogLevel::DBG2);
359 
360  auto& messages = handler_->getMessages();
361 
362  FB_LOG_RAW(
363  foobar,
365  "src/some/file.c",
366  1234,
367  "testFunction",
368  "hello",
369  ' ',
370  1)
371  << " world";
372  ASSERT_EQ(1, messages.size());
373  EXPECT_EQ("hello 1 world", messages[0].first.getMessage());
374  EXPECT_EQ("src/some/file.c", messages[0].first.getFileName());
375  EXPECT_EQ("file.c", messages[0].first.getFileBaseName());
376  EXPECT_EQ(1234, messages[0].first.getLineNumber());
377  messages.clear();
378 
379  auto level = LogLevel::DBG1;
380  FB_LOGF_RAW(
381  foobar,
382  level,
383  "test/mytest.c",
384  99,
385  "testFunction",
386  "{}: num={}",
387  "test",
388  42)
389  << " plus extra stuff";
390  ASSERT_EQ(1, messages.size());
391  EXPECT_EQ("test: num=42 plus extra stuff", messages[0].first.getMessage());
392  EXPECT_EQ("test/mytest.c", messages[0].first.getFileName());
393  EXPECT_EQ("mytest.c", messages[0].first.getFileBaseName());
394  EXPECT_EQ(99, messages[0].first.getLineNumber());
395  messages.clear();
396 }
static struct message messages[5]
Definition: test.c:75
PolymorphicMatcher< internal::MatchesRegexMatcher > MatchesRegex(const internal::RE *regex)
folly::StringPiece toString(StateEnum state)
Definition: State.cpp:16
std::string str() const
Definition: Range.h:591
#define ASSERT_EQ(val1, val2)
Definition: gtest.h:1956
static StringPiece pathBasename(StringPiece path)
Definition: LoggerTest.cpp:40
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
void format(FormatArg &arg, FormatCallback &cb) const
Definition: LoggerTest.cpp:160
void SetUp() override
Definition: LoggerTest.cpp:32
static void basic()
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
#define FB_LOG(logger, level,...)
Definition: Logger.h:34
#define FB_LOG_RAW(logger, level, filename, linenumber, functionName,...)
Definition: Logger.h:75
#define FB_LOGF_RAW(logger, level, filename, linenumber, functionName, fmt, arg1,...)
Definition: Logger.h:89
std::unique_ptr< LoggerDB > db_
Definition: LoggerDB.cpp:108
size_type rfind(value_type c) const
Definition: Range.h:761
Range subpiece(size_type first, size_type length=npos) const
Definition: Range.h:686
#define FB_LOGF(logger, level, fmt, arg1,...)
Definition: Logger.h:53
void toAppend(char value, Tgt *result)
Definition: Conv.h:406
TEST_F(AsyncSSLSocketWriteTest, write_coalescing1)
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
#define EXPECT_THAT(value, matcher)
std::shared_ptr< TestLogHandler > handler_
Definition: LoggerTest.cpp:50
static const size_type npos
Definition: Range.h:197
const char * string
Definition: Conv.cpp:212
Formatter< false, Args... > format(StringPiece fmt, Args &&...args)
Definition: Format.h:271
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
void format(FormatArg &, FormatCallback &) const
Definition: LoggerTest.cpp:171
constexpr detail::First first
Definition: Base-inl.h:2553