level_.load(std::memory_order_acquire) & ~FLAG_INHERIT);
}
+ /**
+ * Get the log level and inheritance flag.
+ */
+ std::pair<LogLevel, bool> getLevelInfo() const {
+ auto value = level_.load(std::memory_order_acquire);
+ return {static_cast<LogLevel>(value & ~FLAG_INHERIT),
+ bool(value & FLAG_INHERIT)};
+ }
+
/**
* Get the effective level for this log category.
*
return effectiveLevel_.load(std::memory_order_relaxed);
}
+ /**
+ * 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 effectiveLevel_.load(std::memory_order_relaxed) <= level;
+ }
+
/**
* Set the log level for this LogCategory.
*
*/
void setLevel(LogLevel level, bool inherit = true);
- /**
- * Process a log message.
- *
- * This method generally should be invoked through the Logger APIs,
- * rather than calling this directly.
- *
- * This method assumes that log level admittance checks have already been
- * performed. This method unconditionally passes the message to the
- * LogHandlers attached to this LogCategory, without any additional log level
- * checks (apart from the ones done in the LogHandlers).
- */
- void processMessage(const LogMessage& message);
-
/**
* Get the LoggerDB that this LogCategory belongs to.
*
void clearHandlers();
/**
- * Note: setLevelLocked() may only be called while holding the main
- * LoggerDB lock.
+ * Get the list of LogHandlers attached to this category.
+ */
+ std::vector<std::shared_ptr<LogHandler>> getHandlers() const;
+
+ /**
+ * Replace the list of LogHandlers with a completely new list.
+ */
+ void replaceHandlers(std::vector<std::shared_ptr<LogHandler>> handlers);
+
+ /**
+ * Update the LogHandlers attached to this LogCategory by replacing
+ * currently attached handlers with new LogHandler objects.
+ *
+ * The handlerMap argument is a map of (old_handler -> new_handler)
+ * If any of the LogHandlers currently attached to this category are found in
+ * the handlerMap, replace them with the new handler indicated in the map.
+ *
+ * This is used when the LogHandler configuration is changed requiring one or
+ * more LogHandler objects to be replaced with new ones.
+ */
+ void updateHandlers(const std::unordered_map<
+ std::shared_ptr<LogHandler>,
+ std::shared_ptr<LogHandler>>& handlerMap);
+
+ /* Internal methods for use by other parts of the logging library code */
+
+ /**
+ * Admit a message into the LogCategory hierarchy to be logged.
+ *
+ * The caller is responsible for having already performed log level
+ * admittance checks.
+ *
+ * This method generally should be invoked only through the logging macros,
+ * rather than calling this directly.
+ */
+ void admitMessage(const LogMessage& message) const;
+
+ /**
+ * Note: setLevelLocked() may only be called while holding the
+ * LoggerDB loggersByName_ lock. It is safe to call this while holding the
+ * loggersByName_ lock in read-mode; holding it exclusively is not required.
*
* This method should only be invoked by LoggerDB.
*/
void setLevelLocked(LogLevel level, bool inherit);
+ /**
+ * Register a std::atomic<LogLevel> value used by XLOG*() macros to check the
+ * effective level for this category.
+ *
+ * The LogCategory will keep this value updated whenever its effective log
+ * level changes.
+ *
+ * This function should only be invoked by LoggerDB, and the LoggerDB lock
+ * must be held when calling it.
+ */
+ void registerXlogLevel(std::atomic<LogLevel>* levelPtr);
+
private:
enum : uint32_t { FLAG_INHERIT = 0x80000000 };
+ // FLAG_INHERIT is the stored in the uppermost bit of the LogLevel field.
+ // assert that it does not conflict with valid LogLevel values.
+ static_assert(
+ static_cast<uint32_t>(LogLevel::MAX_LEVEL) < FLAG_INHERIT,
+ "The FLAG_INHERIT bit must not be set in any valid LogLevel value");
+
// Forbidden copy constructor and assignment operator
LogCategory(LogCategory const&) = delete;
LogCategory& operator=(LogCategory const&) = delete;
+ void processMessage(const LogMessage& message) const;
void updateEffectiveLevel(LogLevel newEffectiveLevel);
void parentLevelUpdated(LogLevel parentEffectiveLevel);
/**
* Pointers to children and sibling loggers.
- * These pointers should only ever be accessed while holding the main
- * LoggerDB lock. (These are only modified when creating new loggers,
- * which occurs with the main LoggerDB lock held.)
+ * These pointers should only ever be accessed while holding the
+ * LoggerDB::loggersByName_ lock. (These are only modified when creating new
+ * loggers, which occurs with the main LoggerDB lock held.)
*/
LogCategory* firstChild_{nullptr};
LogCategory* nextSibling_{nullptr};
+
+ /**
+ * A list of LogLevel values used by XLOG*() statements for this LogCategory.
+ * The XLOG*() statements will check these values. We ensure they are kept
+ * up-to-date each time the effective log level changes for this category.
+ *
+ * This list may only be accessed while holding the main LoggerDB lock.
+ */
+ std::vector<std::atomic<LogLevel>*> xlogLevels_;
};
-}
+} // namespace folly