proxygen
xlog.h
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 #pragma once
17 
18 #include <folly/Likely.h>
19 #include <folly/Portability.h>
20 #include <folly/Range.h>
22 #include <folly/logging/Logger.h>
23 #include <folly/logging/LoggerDB.h>
25 #include <cstdlib>
26 
27 /*
28  * This file contains the XLOG() and XLOGF() macros.
29  *
30  * These macros make it easy to use the logging library without having to
31  * manually pick log category names. All XLOG() and XLOGF() statements in a
32  * given file automatically use a LogCategory based on the current file name.
33  *
34  * For instance, in src/foo/bar.cpp, the default log category name will be
35  * "src.foo.bar"
36  *
37  * If desired, the log category name used by XLOG() in a .cpp file may be
38  * overridden using XLOG_SET_CATEGORY_NAME() macro.
39  */
40 
57 #define XLOG(level, ...) \
58  XLOG_IMPL( \
59  ::folly::LogLevel::level, \
60  ::folly::LogStreamProcessor::APPEND, \
61  ##__VA_ARGS__)
62 
68 #define XLOG_IF(level, cond, ...) \
69  XLOG_IF_IMPL( \
70  ::folly::LogLevel::level, \
71  cond, \
72  ::folly::LogStreamProcessor::APPEND, \
73  ##__VA_ARGS__)
74 
77 #define XLOGF(level, fmt, arg1, ...) \
78  XLOG_IMPL( \
79  ::folly::LogLevel::level, \
80  ::folly::LogStreamProcessor::FORMAT, \
81  fmt, \
82  arg1, \
83  ##__VA_ARGS__)
84 
90 #define XLOGF_IF(level, cond, fmt, arg1, ...) \
91  XLOG_IF_IMPL( \
92  ::folly::LogLevel::level, \
93  cond, \
94  ::folly::LogStreamProcessor::FORMAT, \
95  fmt, \
96  arg1, \
97  ##__VA_ARGS__)
98 
105 #define XLOG_EVERY_MS(level, ms, ...) \
106  XLOG_IF( \
107  level, \
108  [] { \
109  static ::folly::logging::IntervalRateLimiter \
110  folly_detail_xlog_limiter(1, std::chrono::milliseconds(ms)); \
111  return folly_detail_xlog_limiter.check(); \
112  }(), \
113  ##__VA_ARGS__)
114 
121 #define XLOG_EVERY_N(level, n, ...) \
122  XLOG_IF( \
123  level, \
124  [] { \
125  static std::atomic<size_t> folly_detail_xlog_count{0}; \
126  return FOLLY_UNLIKELY( \
127  (folly_detail_xlog_count.fetch_add(1, std::memory_order_relaxed) % \
128  (n)) == 0); \
129  }(), \
130  ##__VA_ARGS__)
131 
138 #define XLOG_N_PER_MS(level, count, ms, ...) \
139  XLOG_IF( \
140  level, \
141  [] { \
142  static ::folly::logging::IntervalRateLimiter \
143  folly_detail_xlog_limiter((count), std::chrono::milliseconds(ms)); \
144  return folly_detail_xlog_limiter.check(); \
145  }(), \
146  ##__VA_ARGS__)
147 
158 #ifdef FOLLY_XLOG_STRIP_PREFIXES
159 #define XLOG_FILENAME \
160  folly::xlogStripFilename(__FILE__, FOLLY_XLOG_STRIP_PREFIXES)
161 #else
162 #define XLOG_FILENAME __FILE__
163 #endif
164 
165 #define XLOG_IMPL(level, type, ...) \
166  XLOG_ACTUAL_IMPL( \
167  level, true, ::folly::isLogLevelFatal(level), type, ##__VA_ARGS__)
168 
169 #define XLOG_IF_IMPL(level, cond, type, ...) \
170  XLOG_ACTUAL_IMPL(level, cond, false, type, ##__VA_ARGS__)
171 
215 #define XLOG_ACTUAL_IMPL(level, cond, always_fatal, type, ...) \
216  (!XLOG_IS_ON_IMPL(level) || !(cond)) \
217  ? ::folly::logDisabledHelper(::folly::bool_constant<always_fatal>{}) \
218  : ::folly::LogStreamVoidify<::folly::isLogLevelFatal(level)>{} & \
219  ::folly::LogStreamProcessor( \
220  [] { \
221  static ::folly::XlogCategoryInfo<XLOG_IS_IN_HEADER_FILE> \
222  folly_detail_xlog_category; \
223  return folly_detail_xlog_category.getInfo( \
224  &xlog_detail::xlogFileScopeInfo); \
225  }(), \
226  (level), \
227  xlog_detail::getXlogCategoryName(XLOG_FILENAME, 0), \
228  xlog_detail::isXlogCategoryOverridden(0), \
229  XLOG_FILENAME, \
230  __LINE__, \
231  __func__, \
232  (type), \
233  ##__VA_ARGS__) \
234  .stream()
235 
241 #define XLOG_IS_ON(level) XLOG_IS_ON_IMPL(::folly::LogLevel::level)
242 
258 #define XLOG_IS_ON_IMPL(level) \
259  ([] { \
260  static ::folly::XlogLevelInfo<XLOG_IS_IN_HEADER_FILE> \
261  folly_detail_xlog_level; \
262  return folly_detail_xlog_level.check( \
263  (level), \
264  xlog_detail::getXlogCategoryName(XLOG_FILENAME, 0), \
265  xlog_detail::isXlogCategoryOverridden(0), \
266  &xlog_detail::xlogFileScopeInfo); \
267  }())
268 
273 #define XLOG_GET_CATEGORY_NAME() \
274  (xlog_detail::isXlogCategoryOverridden(0) \
275  ? xlog_detail::getXlogCategoryName(XLOG_FILENAME, 0) \
276  : ::folly::getXlogCategoryNameForFile(XLOG_FILENAME))
277 
286 #define XLOG_GET_CATEGORY() \
287  folly::LoggerDB::get().getCategory(XLOG_GET_CATEGORY_NAME())
288 
301 #ifdef __INCLUDE_LEVEL__
302 #define XLOG_SET_CATEGORY_CHECK \
303  static_assert( \
304  __INCLUDE_LEVEL__ == 0, \
305  "XLOG_SET_CATEGORY_NAME() should not be used in header files");
306 #else
307 #define XLOG_SET_CATEGORY_CHECK
308 #endif
309 
310 #define XLOG_SET_CATEGORY_NAME(category) \
311  namespace { \
312  namespace xlog_detail { \
313  XLOG_SET_CATEGORY_CHECK \
314  constexpr inline folly::StringPiece getXlogCategoryName( \
315  folly::StringPiece, \
316  int) { \
317  return category; \
318  } \
319  constexpr inline bool isXlogCategoryOverridden(int) { \
320  return true; \
321  } \
322  } \
323  }
324 
332 #define XCHECK(cond, ...) \
333  XLOG_IF(FATAL, UNLIKELY(!(cond)), "Check failed: " #cond " ", ##__VA_ARGS__)
334 
346 #define XDCHECK(cond, ...) \
347  (!::folly::kIsDebug) ? static_cast<void>(0) : XCHECK(cond, ##__VA_ARGS__)
348 
353 #ifdef __INCLUDE_LEVEL__
354 #define XLOG_IS_IN_HEADER_FILE bool(__INCLUDE_LEVEL__ > 0)
355 #else
356 // Without __INCLUDE_LEVEL__ we canot tell if we are in a header file or not,
357 // and must pessimstically assume we are always in a header file.
358 #define XLOG_IS_IN_HEADER_FILE true
359 #endif
360 
361 namespace folly {
362 
364  public:
365 #ifdef __INCLUDE_LEVEL__
366  std::atomic<::folly::LogLevel> level;
367  ::folly::LogCategory* category;
368 #endif
369 };
370 
380 template <bool IsInHeaderFile>
382  public:
383  bool check(
384  LogLevel levelToCheck,
385  folly::StringPiece categoryName,
386  bool isOverridden,
388  // Do an initial relaxed check. If this fails we know the category level
389  // is initialized and the log admittance check failed.
390  // Use LIKELY() to optimize for the case of disabled debug statements:
391  // we disabled debug statements to be cheap. If the log message is
392  // enabled then this check will still be minimal perf overhead compared to
393  // the overall cost of logging it.
394  if (LIKELY(levelToCheck < level_.load(std::memory_order_relaxed))) {
395  return false;
396  }
397 
398  // If we are still here, then either:
399  // - The log level check actually passed, or
400  // - level_ has not been initialized yet, and we have to initialize it and
401  // then re-perform the check.
402  //
403  // Do this work in a separate helper method. It is intentionally defined
404  // in the xlog.cpp file to avoid inlining, to reduce the amount of code
405  // emitted for each XLOG() statement.
406  auto currentLevel = loadLevelFull(categoryName, isOverridden);
407  return levelToCheck >= currentLevel;
408  }
409 
410  private:
411  LogLevel loadLevelFull(folly::StringPiece categoryName, bool isOverridden);
412 
413  // XlogLevelInfo objects are always defined with static storage.
414  // This member will always be zero-initialized on program start.
415  std::atomic<LogLevel> level_;
416 };
417 
418 template <bool IsInHeaderFile>
419 class XlogCategoryInfo {
420  public:
421  bool isInitialized() const {
422  return isInitialized_.load(std::memory_order_acquire);
423  }
424 
425  LogCategory* init(folly::StringPiece categoryName, bool isOverridden);
426 
428  return category_;
429  }
430 
436  return this;
437  }
438 
439  private:
440  // These variables will always be zero-initialized on program start.
441  std::atomic<bool> isInitialized_;
443 };
444 
445 #ifdef __INCLUDE_LEVEL__
446 
451 template <>
452 class XlogLevelInfo<false> {
453  public:
454  static bool check(
455  LogLevel levelToCheck,
456  folly::StringPiece categoryName,
457  bool isOverridden,
458  XlogFileScopeInfo* fileScopeInfo) {
459  // As above in the non-specialized XlogFileScopeInfo code, do a simple
460  // relaxed check first.
461  if (LIKELY(
462  levelToCheck <
463  fileScopeInfo->level.load(::std::memory_order_relaxed))) {
464  return false;
465  }
466 
467  // If we are still here we the file-scope log level either needs to be
468  // initalized, or the log level check legitimately passed.
469  auto currentLevel =
470  loadLevelFull(categoryName, isOverridden, fileScopeInfo);
471  return levelToCheck >= currentLevel;
472  }
473 
474  private:
475  static LogLevel loadLevelFull(
476  folly::StringPiece categoryName,
477  bool isOverridden,
478  XlogFileScopeInfo* fileScopeInfo);
479 };
480 
487 template <>
488 class XlogCategoryInfo<false> {
489  public:
494  XlogFileScopeInfo* getInfo(XlogFileScopeInfo* fileScopeInfo) {
495  return fileScopeInfo;
496  }
497 };
498 #endif
499 
507 
508 constexpr bool xlogIsDirSeparator(char c) {
509  return c == '/' || (kIsWindows && c == '\\');
510 }
511 
512 namespace detail {
513 constexpr const char* xlogStripFilenameRecursive(
514  const char* filename,
515  const char* prefixes,
516  size_t prefixIdx,
517  size_t filenameIdx,
518  bool match);
519 constexpr const char* xlogStripFilenameMatchFound(
520  const char* filename,
521  const char* prefixes,
522  size_t prefixIdx,
523  size_t filenameIdx) {
524  return (filename[filenameIdx] == '\0')
525  ? xlogStripFilenameRecursive(filename, prefixes, prefixIdx + 1, 0, true)
526  : (xlogIsDirSeparator(filename[filenameIdx])
528  filename, prefixes, prefixIdx, filenameIdx + 1)
529  : (filename + filenameIdx));
530 }
531 constexpr const char* xlogStripFilenameRecursive(
532  const char* filename,
533  const char* prefixes,
534  size_t prefixIdx,
535  size_t filenameIdx,
536  bool match) {
537  // This would be much easier to understand if written as a while loop.
538  // However, in order to maintain compatibility with pre-C++14 compilers we
539  // have implemented it recursively to adhere to C++11 restrictions for
540  // constexpr functions.
541  return (prefixes[prefixIdx] == ':' || prefixes[prefixIdx] == '\0')
542  ? ((match && filenameIdx > 0 &&
543  (xlogIsDirSeparator(prefixes[filenameIdx - 1]) ||
544  xlogIsDirSeparator(filename[filenameIdx])))
546  filename, prefixes, prefixIdx, filenameIdx))
547  : ((prefixes[prefixIdx] == '\0')
548  ? filename
550  filename, prefixes, prefixIdx + 1, 0, true)))
551  : ((match && (prefixes[prefixIdx] == filename[filenameIdx]))
553  filename, prefixes, prefixIdx + 1, filenameIdx + 1, true)
555  filename, prefixes, prefixIdx + 1, 0, false));
556 }
557 } // namespace detail
558 
575 constexpr const char* xlogStripFilename(
576  const char* filename,
577  const char* prefixes) {
578  return detail::xlogStripFilenameRecursive(filename, prefixes, 0, 0, true);
579 }
580 } // namespace folly
581 
582 /*
583  * We intentionally use an unnamed namespace inside a header file here.
584  *
585  * We want each .cpp file that uses xlog.h to get its own separate
586  * implementation of the following functions and variables.
587  */
588 namespace {
589 namespace xlog_detail {
607 template <typename T>
608 constexpr inline folly::StringPiece getXlogCategoryName(
609  folly::StringPiece filename,
610  T) {
611  return filename;
612 }
613 
625 template <typename T>
626 constexpr inline bool isXlogCategoryOverridden(T) {
627  return false;
628 }
629 
638 ::folly::XlogFileScopeInfo xlogFileScopeInfo;
639 } // namespace xlog_detail
640 } // namespace
constexpr bool xlogIsDirSeparator(char c)
Definition: xlog.h:508
#define LIKELY(x)
Definition: Likely.h:47
LogCategory * category_
Definition: xlog.h:442
folly::std T
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
LogCategory * getCategory(XlogFileScopeInfo *)
Definition: xlog.h:427
StringPiece getXlogCategoryNameForFile(StringPiece filename)
Definition: xlog.cpp:57
void init(int *argc, char ***argv, bool removeFlags)
Definition: Init.cpp:34
constexpr const char * xlogStripFilename(const char *filename, const char *prefixes)
Definition: xlog.h:575
std::atomic< LogLevel > level_
Definition: xlog.h:415
std::atomic< bool > isInitialized_
Definition: xlog.h:441
bool check(LogLevel levelToCheck, folly::StringPiece categoryName, bool isOverridden, XlogFileScopeInfo *)
Definition: xlog.h:383
XlogCategoryInfo< IsInHeaderFile > * getInfo(XlogFileScopeInfo *)
Definition: xlog.h:435
LogLevel
Definition: LogLevel.h:38
constexpr const char * xlogStripFilenameRecursive(const char *filename, const char *prefixes, size_t prefixIdx, size_t filenameIdx, bool match)
Definition: xlog.h:531
bool isInitialized() const
Definition: xlog.h:421
constexpr const char * xlogStripFilenameMatchFound(const char *filename, const char *prefixes, size_t prefixIdx, size_t filenameIdx)
Definition: xlog.h:519
constexpr auto kIsWindows
Definition: Portability.h:367
char c
bool check(const dynamic &schema, const dynamic &value, bool check=true)