Fix copyright lines
[folly.git] / folly / experimental / logging / xlog.h
index 452041f6fa150e1e16d7cbed94bcf2d6f97462ac..06478af03ef33e90e9a113f8bfa171c9abe91bfe 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-present Facebook, Inc.
+ * Copyright 2017-present Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
 #include <folly/experimental/logging/LogStream.h>
 #include <folly/experimental/logging/Logger.h>
 #include <folly/experimental/logging/LoggerDB.h>
+#include <cstdlib>
 
 /*
  * This file contains the XLOG() and XLOGF() macros.
@@ -32,7 +33,7 @@
  * "src.foo.bar"
  *
  * If desired, the log category name used by XLOG() in a .cpp file may be
- * overridden using XLOG_SET_CATEGORY() macro.
+ * overridden using XLOG_SET_CATEGORY_NAME() macro.
  */
 
 /**
  *   initialized.  On all subsequent calls, disabled log statements can be
  *   skipped with just a single check of the LogLevel.
  */
-#define XLOG_IMPL(level, type, ...)                        \
-  (!XLOG_IS_ON_IMPL(level)) ? static_cast<void>(0)         \
-                            : ::folly::LogStreamProcessor( \
-                                  XLOG_GET_CATEGORY(),     \
-                                  (level),                 \
-                                  __FILE__,                \
-                                  __LINE__,                \
-                                  (type),                  \
-                                  ##__VA_ARGS__) &         \
-          ::folly::LogStream()
+/* clang-format off */
+#define XLOG_IMPL(level, type, ...)                                          \
+  (!XLOG_IS_ON_IMPL(level))                                                  \
+      ? ::folly::logDisabledHelper(                                          \
+            std::integral_constant<bool, ::folly::isLogLevelFatal(level)>{}) \
+      : ::folly::LogStreamVoidify< ::folly::isLogLevelFatal(level)>{} &      \
+          ::folly::LogStreamProcessor(                                       \
+              [] {                                                           \
+                static ::folly::XlogCategoryInfo<XLOG_IS_IN_HEADER_FILE>     \
+                    _xlogCategory_;                                          \
+                return _xlogCategory_.getInfo(                               \
+                    &xlog_detail::xlogFileScopeInfo);                        \
+              }(),                                                           \
+              (level),                                                       \
+              xlog_detail::getXlogCategoryName(__FILE__, 0),                 \
+              xlog_detail::isXlogCategoryOverridden(0),                      \
+              __FILE__,                                                      \
+              __LINE__,                                                      \
+              (type),                                                        \
+              ##__VA_ARGS__)                                                 \
+              .stream()
+/* clang-format on */
 
 /**
  * Check if and XLOG() statement with the given log level would be enabled.
+ *
+ * The level parameter must be an unqualified LogLevel enum value.
  */
 #define XLOG_IS_ON(level) XLOG_IS_ON_IMPL(::folly::LogLevel::level)
 
  * automatically keeps it up-to-date when the category's effective level is
  * changed.
  *
- * Most of this code must live in the macro definition itself, and cannot be
- * moved into a helper function: The getXlogCategoryName() call must be made as
- * part of the macro expansion in order to work correctly.  We also want to
- * avoid calling it whenever possible.  Therefore most of the logic must be
- * done in the macro expansion.
- *
  * See XlogLevelInfo for the implementation details.
  */
-#define XLOG_IS_ON_IMPL(level)                                                 \
-  ([] {                                                                        \
-    static ::folly::XlogLevelInfo<XLOG_IS_IN_HEADER_FILE> _xlogLevel_;         \
-    const auto _xlogLevelToCheck_ = (level);                                   \
-    /*                                                                         \
-     * Do an initial relaxed check.  If this fails we know the category level  \
-     * is initialized and the log admittance check failed.                     \
-     * Use LIKELY() to optimize for the case of disabled debug statements:     \
-     * we disabled debug statements to be cheap.  If the log message is        \
-     * enabled then this check will still be minimal perf overhead compared to \
-     * the overall cost of logging it.                                         \
-     */                                                                        \
-    if (LIKELY(                                                                \
-            _xlogLevelToCheck_ <                                               \
-            _xlogLevel_.getLevel(                                              \
-                &_xlogFileScopeInfo_, std::memory_order_relaxed))) {           \
-      return false;                                                            \
-    }                                                                          \
-    /*                                                                         \
-     * Load the level value with memory_order_acquire, and check               \
-     * to see if it is initialized or not.                                     \
-     */                                                                        \
-    auto _xlogCurrentLevel_ =                                                  \
-        _xlogLevel_.getLevel(&_xlogFileScopeInfo_, std::memory_order_acquire); \
-    if (UNLIKELY(_xlogCurrentLevel_ == ::folly::LogLevel::UNINITIALIZED)) {    \
-      _xlogCurrentLevel_ = _xlogLevel_.init(                                   \
-          getXlogCategoryName(__FILE__), &_xlogFileScopeInfo_);                \
-    }                                                                          \
-    return _xlogLevelToCheck_ >= _xlogCurrentLevel_;                           \
+#define XLOG_IS_ON_IMPL(level)                                         \
+  ([] {                                                                \
+    static ::folly::XlogLevelInfo<XLOG_IS_IN_HEADER_FILE> _xlogLevel_; \
+    return _xlogLevel_.check(                                          \
+        (level),                                                       \
+        xlog_detail::getXlogCategoryName(__FILE__, 0),                 \
+        xlog_detail::isXlogCategoryOverridden(0),                      \
+        &xlog_detail::xlogFileScopeInfo);                              \
   }())
 
+/**
+ * Get the name of the log category that will be used by XLOG() statements
+ * in this file.
+ */
+#define XLOG_GET_CATEGORY_NAME()                       \
+  (xlog_detail::isXlogCategoryOverridden(0)            \
+       ? xlog_detail::getXlogCategoryName(__FILE__, 0) \
+       : ::folly::getXlogCategoryNameForFile(__FILE__))
+
 /**
  * Get a pointer to the LogCategory that will be used by XLOG() statements in
  * this file.
  *
- * This macro is used in the XLOG() implementation, and therefore must be as
- * cheap as possible.  It stores the LogCategory* pointer as a local static
- * variable.  Only the first invocation has to look up the log category by
- * name.  Subsequent invocations re-use the already looked-up LogCategory
- * pointer.
- *
- * Most of this code must live in the macro definition itself, and cannot be
- * moved into a helper function: The getXlogCategoryName() call must be made as
- * part of the macro expansion in order to work correctly.  We also want to
- * avoid calling it whenever possible.  Therefore most of the logic must be
- * done in the macro expansion.
- *
- * See XlogCategoryInfo for the implementation details.
+ * This is just a small wrapper around a LoggerDB::getCategory() call.
+ * This must be implemented as a macro since it uses __FILE__, and that must
+ * expand to the correct filename based on where the macro is used.
  */
-#define XLOG_GET_CATEGORY()                                                  \
-  [] {                                                                       \
-    static ::folly::XlogCategoryInfo<XLOG_IS_IN_HEADER_FILE> _xlogCategory_; \
-    if (!_xlogCategory_.isInitialized()) {                                   \
-      return _xlogCategory_.init(getXlogCategoryName(__FILE__));             \
-    }                                                                        \
-    return _xlogCategory_.getCategory(&_xlogFileScopeInfo_);                 \
-  }()
+#define XLOG_GET_CATEGORY() \
+  folly::LoggerDB::get()->getCategory(XLOG_GET_CATEGORY_NAME())
 
 /**
- * XLOG_SET_CATEGORY() can be used to explicitly define the log category name
- * used by all XLOG() and XLOGF() calls in this translation unit.
+ * XLOG_SET_CATEGORY_NAME() can be used to explicitly define the log category
+ * name used by all XLOG() and XLOGF() calls in this translation unit.
  *
  * This overrides the default behavior of picking a category name based on the
  * current filename.
  * This should be used at the top-level scope in a .cpp file, before any XLOG()
  * or XLOGF() macros have been used in the file.
  *
- * XLOG_SET_CATEGORY() cannot be used inside header files.
+ * XLOG_SET_CATEGORY_NAME() cannot be used inside header files.
  */
 #ifdef __INCLUDE_LEVEL__
-#define XLOG_SET_CATEGORY(category)                              \
-  namespace {                                                    \
-  static_assert(                                                 \
-      __INCLUDE_LEVEL__ == 0,                                    \
-      "XLOG_SET_CATEGORY() should not be used in header files"); \
-  inline std::string getXlogCategoryName(const char*) {          \
-    return category;                                             \
-  }                                                              \
-  }
+#define XLOG_SET_CATEGORY_CHECK \
+  static_assert(                \
+      __INCLUDE_LEVEL__ == 0,   \
+      "XLOG_SET_CATEGORY_NAME() should not be used in header files");
 #else
-#define XLOG_SET_CATEGORY(category)                     \
-  namespace {                                           \
-  inline std::string getXlogCategoryName(const char*) { \
-    return category;                                    \
-  }                                                     \
-  }
+#define XLOG_SET_CATEGORY_CHECK
 #endif
 
+#define XLOG_SET_CATEGORY_NAME(category)                   \
+  namespace {                                              \
+  namespace xlog_detail {                                  \
+  XLOG_SET_CATEGORY_CHECK                                  \
+  constexpr inline folly::StringPiece getXlogCategoryName( \
+      folly::StringPiece,                                  \
+      int) {                                               \
+    return category;                                       \
+  }                                                        \
+  constexpr inline bool isXlogCategoryOverridden(int) {    \
+    return true;                                           \
+  }                                                        \
+  }                                                        \
+  }
+
 /**
  * XLOG_IS_IN_HEADER_FILE evaluates to false if we can definitively tell if we
  * are not in a header file.  Otherwise, it evaluates to true.
@@ -269,37 +255,64 @@ class XlogFileScopeInfo {
  * used during dynamic object initialization before main().
  */
 template <bool IsInHeaderFile>
-struct XlogLevelInfo {
+class XlogLevelInfo {
  public:
-  inline LogLevel getLevel(XlogFileScopeInfo*, std::memory_order order) {
-    return level_.load(order);
-  }
+  bool check(
+      LogLevel levelToCheck,
+      folly::StringPiece categoryName,
+      bool isOverridden,
+      XlogFileScopeInfo*) {
+    // Do an initial relaxed check.  If this fails we know the category level
+    // is initialized and the log admittance check failed.
+    // Use LIKELY() to optimize for the case of disabled debug statements:
+    // we disabled debug statements to be cheap.  If the log message is
+    // enabled then this check will still be minimal perf overhead compared to
+    // the overall cost of logging it.
+    if (LIKELY(levelToCheck < level_.load(std::memory_order_relaxed))) {
+      return false;
+    }
 
-  inline LogLevel init(folly::StringPiece categoryName, XlogFileScopeInfo*) {
-    return LoggerDB::get()->xlogInit(categoryName, &level_, nullptr);
+    // If we are still here, then either:
+    // - The log level check actually passed, or
+    // - level_ has not been initialized yet, and we have to initialize it and
+    //   then re-perform the check.
+    //
+    // Do this work in a separate helper method.  It is intentionally defined
+    // in the xlog.cpp file to avoid inlining, to reduce the amount of code
+    // emitted for each XLOG() statement.
+    auto currentLevel = loadLevelFull(categoryName, isOverridden);
+    return levelToCheck >= currentLevel;
   }
 
  private:
+  LogLevel loadLevelFull(folly::StringPiece categoryName, bool isOverridden);
+
+  // XlogLevelInfo objects are always defined with static storage.
   // This member will always be zero-initialized on program start.
   std::atomic<LogLevel> level_;
 };
 
 template <bool IsInHeaderFile>
-struct XlogCategoryInfo {
+class XlogCategoryInfo {
  public:
-  bool isInitialized() {
+  bool isInitialized() const {
     return isInitialized_.load(std::memory_order_acquire);
   }
 
-  LogCategory* init(folly::StringPiece categoryName) {
-    return LoggerDB::get()->xlogInitCategory(
-        categoryName, &category_, &isInitialized_);
-  }
+  LogCategory* init(folly::StringPiece categoryName, bool isOverridden);
 
   LogCategory* getCategory(XlogFileScopeInfo*) {
     return category_;
   }
 
+  /**
+   * Get a pointer to pass into the LogStreamProcessor constructor,
+   * so that it is able to look up the LogCategory information.
+   */
+  XlogCategoryInfo<IsInHeaderFile>* getInfo(XlogFileScopeInfo*) {
+    return this;
+  }
+
  private:
   // These variables will always be zero-initialized on program start.
   std::atomic<bool> isInitialized_;
@@ -313,20 +326,33 @@ struct XlogCategoryInfo {
  * for the entire file, rather than defining one for each XLOG() statement.
  */
 template <>
-struct XlogLevelInfo<false> {
+class XlogLevelInfo<false> {
  public:
-  inline LogLevel getLevel(
-      XlogFileScopeInfo* fileScopeInfo,
-      std::memory_order order) {
-    return fileScopeInfo->level.load(order);
-  }
-
-  inline LogLevel init(
+  static bool check(
+      LogLevel levelToCheck,
       folly::StringPiece categoryName,
+      bool isOverridden,
       XlogFileScopeInfo* fileScopeInfo) {
-    return LoggerDB::get()->xlogInit(
-        categoryName, &fileScopeInfo->level, &fileScopeInfo->category);
+    // As above in the non-specialized XlogFileScopeInfo code, do a simple
+    // relaxed check first.
+    if (LIKELY(
+            levelToCheck <
+            fileScopeInfo->level.load(::std::memory_order_relaxed))) {
+      return false;
+    }
+
+    // If we are still here we the file-scope log level either needs to be
+    // initalized, or the log level check legitimately passed.
+    auto currentLevel =
+        loadLevelFull(categoryName, isOverridden, fileScopeInfo);
+    return levelToCheck >= currentLevel;
   }
+
+ private:
+  static LogLevel loadLevelFull(
+      folly::StringPiece categoryName,
+      bool isOverridden,
+      XlogFileScopeInfo* fileScopeInfo);
 };
 
 /**
@@ -336,21 +362,14 @@ struct XlogLevelInfo<false> {
  * statement.
  */
 template <>
-struct XlogCategoryInfo<false> {
+class XlogCategoryInfo<false> {
  public:
-  constexpr bool isInitialized() {
-    // XlogLevelInfo<false>::check() is always called before XlogCategoryInfo
-    // is used, and it will will have already initialized fileScopeInfo.
-    // Therefore we never have to check if it is initialized yet here.
-    return true;
-  }
-  LogCategory* init(folly::StringPiece) {
-    // This method is never used given that isInitialized() always returns true
-    abort();
-    return nullptr;
-  }
-  LogCategory* getCategory(XlogFileScopeInfo* fileScopeInfo) {
-    return fileScopeInfo->category;
+  /**
+   * Get a pointer to pass into the LogStreamProcessor constructor,
+   * so that it is able to look up the LogCategory information.
+   */
+  XlogFileScopeInfo* getInfo(XlogFileScopeInfo* fileScopeInfo) {
+    return fileScopeInfo;
   }
 };
 #endif
@@ -359,10 +378,10 @@ struct XlogCategoryInfo<false> {
  * Get the default XLOG() category name for the given filename.
  *
  * This function returns the category name that will be used by XLOG() if
- * XLOG_SET_CATEGORY() has not been used.
+ * XLOG_SET_CATEGORY_NAME() has not been used.
  */
 std::string getXlogCategoryNameForFile(folly::StringPiece filename);
-}
+} // namespace folly
 
 /*
  * We intentionally use an unnamed namespace inside a header file here.
@@ -371,17 +390,45 @@ std::string getXlogCategoryNameForFile(folly::StringPiece filename);
  * implementation of the following functions and variables.
  */
 namespace {
+namespace xlog_detail {
 /**
- * The default getXlogCategoryName() implementation.
- * This will be used if XLOG_SET_CATEGORY() has not been used yet.
+ * The default getXlogCategoryName() function.
+ *
+ * By default this simply returns the filename argument passed in.
+ * The default isXlogCategoryOverridden() function returns false, indicating
+ * that the return value from getXlogCategoryName() needs to be converted
+ * using getXlogCategoryNameForFile().
+ *
+ * These are two separate steps because getXlogCategoryName() itself needs to
+ * remain constexpr--it is always evaluated in XLOG() statements, but we only
+ * want to call getXlogCategoryNameForFile() the very first time through, when
+ * we have to initialize the LogCategory object.
  *
- * This is a template purely so that XLOG_SET_CATEGORY() can define a more
- * specific version if desired, allowing XLOG_SET_CATEGORY() to override this
- * implementation once it has been used.  The template paramete
+ * This is a template function purely so that XLOG_SET_CATEGORY_NAME() can
+ * define a more specific version of this function that will take precedence
+ * over this one.
  */
 template <typename T>
-inline std::string getXlogCategoryName(const T* filename) {
-  return ::folly::getXlogCategoryNameForFile(filename);
+constexpr inline folly::StringPiece getXlogCategoryName(
+    folly::StringPiece filename,
+    T) {
+  return filename;
+}
+
+/**
+ * The default isXlogCategoryOverridden() function.
+ *
+ * This returns false indicating that the category name has not been
+ * overridden, so getXlogCategoryName() returns a raw filename that needs
+ * to be translated with getXlogCategoryNameForFile().
+ *
+ * This is a template function purely so that XLOG_SET_CATEGORY_NAME() can
+ * define a more specific version of this function that will take precedence
+ * over this one.
+ */
+template <typename T>
+constexpr inline bool isXlogCategoryOverridden(T) {
+  return false;
 }
 
 /**
@@ -392,5 +439,6 @@ inline std::string getXlogCategoryName(const T* filename) {
  * entire .cpp file, rather than needing a separate copy for each XLOG()
  * statement.
  */
-::folly::XlogFileScopeInfo _xlogFileScopeInfo_;
-}
+::folly::XlogFileScopeInfo xlogFileScopeInfo;
+} // namespace xlog_detail
+} // namespace