logging: add support for streaming operators
[folly.git] / folly / experimental / logging / Logger.h
index 4f743d1d051689a366e6a88f4df0bb68dd348f95..ab9bd24f0950c346704dad1923ba6bb1b1172d31 100644 (file)
 #include <folly/Format.h>
 #include <folly/experimental/logging/LogCategory.h>
 #include <folly/experimental/logging/LogLevel.h>
+#include <folly/experimental/logging/LogStream.h>
+#include <folly/experimental/logging/LogStreamProcessor.h>
+
+/**
+ * Helper macro for implementing FB_LOG() and FB_LOGF().
+ *
+ * This macro generally should not be used directly by end users.
+ */
+#define FB_LOG_IMPL(logger, level, type, ...)               \
+  (!(logger).getCategory()->logCheck(level))                \
+      ? (void)0                                             \
+      : ::folly::LogStreamProcessor{(logger).getCategory(), \
+                                    (level),                \
+                                    __FILE__,               \
+                                    __LINE__,               \
+                                    (type),                 \
+                                    ##__VA_ARGS__} &        \
+          ::folly::LogStream()
 
 /**
  * Log a message to the specified logger.
  *
  * This macro avoids evaluating the log arguments unless the log level check
  * succeeds.
+ *
+ * Beware that the logger argument is evaluated twice, so this argument should
+ * be an expression with no side-effects.
  */
-#define FB_LOG(logger, level, msg, ...)                             \
-  do {                                                              \
-    const auto fbLogLevelTmp = ::folly::LogLevel::level;            \
-    const auto& fbLoggerTmp = (logger);                             \
-    if (fbLoggerTmp.logCheck(fbLogLevelTmp)) {                      \
-      fbLoggerTmp.log(                                              \
-          fbLogLevelTmp, __FILE__, __LINE__, (msg), ##__VA_ARGS__); \
-    }                                                               \
-  } while (0)
+#define FB_LOG(logger, level, ...)         \
+  FB_LOG_IMPL(                             \
+      logger,                              \
+      ::folly::LogLevel::level,            \
+      ::folly::LogStreamProcessor::APPEND, \
+      ##__VA_ARGS__)
 
 /**
  * Log a message to the specified logger, using a folly::format() string.
  *
  * This macro avoids evaluating the log arguments unless the log level check
  * succeeds.
+ *
+ * Beware that the logger argument is evaluated twice, so this argument should
+ * be an expression with no side-effects.
  */
-#define FB_LOGF(logger, level, fmt, ...)                            \
-  do {                                                              \
-    const auto fbLogLevelTmp = ::folly::LogLevel::level;            \
-    const auto& fbLoggerTmp = (logger);                             \
-    if (fbLoggerTmp.logCheck(fbLogLevelTmp)) {                      \
-      fbLoggerTmp.logf(                                             \
-          fbLogLevelTmp, __FILE__, __LINE__, (fmt), ##__VA_ARGS__); \
-    }                                                               \
-  } while (0)
+#define FB_LOGF(logger, level, fmt, arg1, ...) \
+  FB_LOG_IMPL(                                 \
+      logger,                                  \
+      ::folly::LogLevel::level,                \
+      ::folly::LogStreamProcessor::FORMAT,     \
+      fmt,                                     \
+      arg1,                                    \
+      ##__VA_ARGS__)
 
 namespace folly {
 
@@ -91,114 +111,6 @@ class Logger {
    */
   Logger(LoggerDB* db, folly::StringPiece name);
 
-  /**
-   * Get the effective level for this logger.
-   *
-   * This is the minimum log level of this logger, or any of its parents.
-   * Log messages below this level will be ignored, while messages at or
-   * above this level need to be processed by this logger or one of its
-   * parents.
-   */
-  LogLevel getEffectiveLevel() const {
-    return category_->getEffectiveLevel();
-  }
-
-  /**
-   * Check whether this Logger or any of its parent Loggers would do anything
-   * with a log message at the given level.
-   */
-  bool logCheck(LogLevel level) const {
-    // We load the effective level using std::memory_order_relaxed.
-    //
-    // We want to make log checks as lightweight as possible.  It's fine if we
-    // don't immediately respond to changes made to the log level from other
-    // threads.  We can wait until some other operation triggers a memory
-    // barrier before we honor the new log level setting.  No other memory
-    // accesses depend on the log level value.  Callers should not rely on all
-    // other threads to immediately stop logging as soon as they decrease the
-    // log level for a given category.
-    return category_->getEffectiveLevelRelaxed() <= level;
-  }
-
-  /**
-   * Unconditionally log a message.
-   *
-   * The caller is responsible for calling logCheck() before log() to ensure
-   * that this log message should be admitted.  This is typically done with one
-   * of the logging macros.
-   */
-  void log(
-      LogLevel level,
-      folly::StringPiece filename,
-      unsigned int lineNumber,
-      std::string&& msg) const;
-  void log(
-      LogLevel level,
-      folly::StringPiece filename,
-      unsigned int lineNumber,
-      folly::StringPiece msg) const;
-
-  /**
-   * Unconditionally log a message.
-   *
-   * This concatenates the arguments into a string using
-   * folly::to<std::string>()
-   */
-  template <typename... Args>
-  void log(
-      LogLevel level,
-      folly::StringPiece filename,
-      unsigned int lineNumber,
-      Args&&... args) const {
-    std::string msg;
-    try {
-      msg = folly::to<std::string>(std::forward<Args>(args)...);
-    } catch (const std::exception& ex) {
-      // This most likely means there was some error converting the arguments
-      // to strings.  Handle the exception here, rather than letting it
-      // propagate up, since callers generally do not expect log statements to
-      // throw.
-      //
-      // Just log an error message letting indicating that something went wrong
-      // formatting the log message.
-      msg =
-          folly::to<std::string>("error constructing log message: ", ex.what());
-    }
-    log(level, filename, lineNumber, std::move(msg));
-  }
-
-  /**
-   * Unconditionally log a message using a format string.
-   *
-   * This uses folly::format() to format the message.
-   */
-  template <typename... Args>
-  void logf(
-      LogLevel level,
-      folly::StringPiece filename,
-      unsigned int lineNumber,
-      folly::StringPiece fmt,
-      Args&&... args) const {
-    std::string msg;
-    try {
-      msg = folly::sformat(fmt, std::forward<Args>(args)...);
-    } catch (const std::exception& ex) {
-      // This most likely means that the caller had a bug in their format
-      // string/arguments.  Handle the exception here, rather than letting it
-      // propagate up, since callers generally do not expect log statements to
-      // throw.
-      //
-      // Log the format string by itself, to help the developer at least
-      // identify the buggy format string in their code.
-      msg = folly::to<std::string>(
-          "error formatting log message: ",
-          ex.what(),
-          "; format string: ",
-          fmt);
-    }
-    log(level, filename, lineNumber, std::move(msg));
-  }
-
   /**
    * Get the LogCategory that this Logger refers to.
    */