logging: implement FATAL and DFATAL log levels
[folly.git] / folly / experimental / logging / LogCategory.cpp
index f1b2856ccce56bbb7dec09e3b8a81bf15d5a9e5e..ff06ba52d8eda0c507674c723b3fba1bf3e2e4e8 100644 (file)
 #include <folly/experimental/logging/LogCategory.h>
 
 #include <cstdio>
+#include <cstdlib>
 
 #include <folly/ExceptionString.h>
+#include <folly/FileUtil.h>
 #include <folly/experimental/logging/LogHandler.h>
 #include <folly/experimental/logging/LogMessage.h>
 #include <folly/experimental/logging/LogName.h>
@@ -42,6 +44,31 @@ LogCategory::LogCategory(StringPiece name, LogCategory* parent)
   parent_->firstChild_ = this;
 }
 
+void LogCategory::admitMessage(const LogMessage& message) const {
+  processMessage(message);
+
+  // If this is a fatal message, flush the handlers to make sure the log
+  // message was written out, then crash.
+  if (isLogLevelFatal(message.getLevel())) {
+    auto numHandlers = db_->flushAllHandlers();
+    if (numHandlers == 0) {
+      // No log handlers were configured.
+      // Print the message to stderr, to make sure we always print the reason
+      // we are crashing somewhere.
+      auto msg = folly::to<std::string>(
+          "FATAL:",
+          message.getFileName(),
+          ":",
+          message.getLineNumber(),
+          ": ",
+          message.getMessage(),
+          "\n");
+      folly::writeFull(STDERR_FILENO, msg.data(), msg.size());
+    }
+    std::abort();
+  }
+}
+
 void LogCategory::processMessage(const LogMessage& message) const {
   // Make a copy of any attached LogHandlers, so we can release the handlers_
   // lock before holding them.
@@ -69,19 +96,18 @@ void LogCategory::processMessage(const LogMessage& message) const {
 
   for (size_t n = 0; n < numHandlers; ++n) {
     try {
-      handlers[n]->log(message, this);
+      handlers[n]->handleMessage(message, this);
     } catch (const std::exception& ex) {
-      // If a LogHandler throws an exception, complain about this fact on
-      // stderr to avoid swallowing the error information completely.  We
-      // don't propagate the exception up to our caller: most code does not
-      // prepare for log statements to throw.  We also want to continue
-      // trying to log the message to any other handlers attached to ourself
-      // or one of our parent categories.
-      fprintf(
-          stderr,
-          "WARNING: log handler for category %s threw an error: %s\n",
-          name_.c_str(),
-          folly::exceptionStr(ex).c_str());
+      // Use LoggerDB::internalWarning() to report the error, but continue
+      // trying to log the message to any other handlers attached to ourself or
+      // one of our parent categories.
+      LoggerDB::internalWarning(
+          __FILE__,
+          __LINE__,
+          "log handler for category \"",
+          name_,
+          "\" threw an error: ",
+          folly::exceptionStr(ex));
     }
   }
 
@@ -113,6 +139,10 @@ void LogCategory::clearHandlers() {
   // LogHandler destructors.
 }
 
+std::vector<std::shared_ptr<LogHandler>> LogCategory::getHandlers() const {
+  return *(handlers_.rlock());
+}
+
 void LogCategory::setLevel(LogLevel level, bool inherit) {
   // We have to set the level through LoggerDB, since we require holding
   // the LoggerDB lock to iterate through our children in case our effective
@@ -121,11 +151,16 @@ void LogCategory::setLevel(LogLevel level, bool inherit) {
 }
 
 void LogCategory::setLevelLocked(LogLevel level, bool inherit) {
-  // Truncate to LogLevel::MAX_LEVEL to make sure it does not conflict
-  // with our flag bits.
+  // Clamp the value to MIN_LEVEL and MAX_LEVEL.
+  //
+  // This makes sure that UNINITIALIZED is always less than any valid level
+  // value, and that level values cannot conflict with our flag bits.
   if (level > LogLevel::MAX_LEVEL) {
     level = LogLevel::MAX_LEVEL;
+  } else if (level < LogLevel::MIN_LEVEL) {
+    level = LogLevel::MIN_LEVEL;
   }
+
   // Make sure the inherit flag is always off for the root logger.
   if (!parent_) {
     inherit = false;
@@ -161,6 +196,11 @@ void LogCategory::updateEffectiveLevel(LogLevel newEffectiveLevel) {
     return;
   }
 
+  // Update all of the values in xlogLevels_
+  for (auto* levelPtr : xlogLevels_) {
+    levelPtr->store(newEffectiveLevel, std::memory_order_release);
+  }
+
   // Update all children loggers
   LogCategory* child = firstChild_;
   while (child != nullptr) {
@@ -180,4 +220,8 @@ void LogCategory::parentLevelUpdated(LogLevel parentEffectiveLevel) {
   auto newEffectiveLevel = std::min(myLevel, parentEffectiveLevel);
   updateEffectiveLevel(newEffectiveLevel);
 }
+
+void LogCategory::registerXlogLevel(std::atomic<LogLevel>* levelPtr) {
+  xlogLevels_.push_back(levelPtr);
+}
 }