/*
- * 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.
*/
#pragma once
+#include <folly/Conv.h>
#include <folly/CppAttributes.h>
#include <folly/Range.h>
#include <folly/Synchronized.h>
namespace folly {
class LogCategory;
+class LogConfig;
+class LogHandler;
+class LogHandlerFactory;
enum class LogLevel : uint32_t;
/**
*/
static LoggerDB* get();
+ ~LoggerDB();
+
/**
* Get the LogCategory for the specified name.
*
void setLevel(LogCategory* category, LogLevel level, bool inherit = true);
/**
- * Apply a configuration string specifying a series a log levels.
+ * Get a LogConfig object describing the current state of the LoggerDB.
+ *
+ * Note that this may not 100% accurately describe the current configuration
+ * if callers have manually added LogHandlers to some categories without
+ * using the updateConfig() or resetConfig() functions. In this case
+ * getConfig() will simply report these handlers as "unknown_handler" when
+ * returning handler names for the categories in question.
+ */
+ LogConfig getConfig() const;
+
+ /**
+ * Update the current LoggerDB state with the specified LogConfig settings.
+ *
+ * Log categories and handlers listed in the LogConfig object will be updated
+ * to the new state listed in the LogConfig. Settings on categories and
+ * handlers not listed in the config will be left as-is.
+ */
+ void updateConfig(const LogConfig& config);
+
+ /**
+ * Reset the current LoggerDB state to the specified LogConfig settings.
*
- * The string format is a comma separated list of <name>=<level> sections.
- * e.g.: "foo=DBG3,log.bar=WARN"
+ * All LogCategories not mentioned in the new LogConfig will have all
+ * currently configured log handlers removed and their log level set to its
+ * default state. For the root category the default log level is ERR; for
+ * all other categories the default level is MAX_LEVEL with log level
+ * inheritance enabled.
*
- * Returns a list of error messages for each error encountered trying to
- * parse the config string. The return value will be an empty vector if no
- * errors were encountered.
+ * LogCategories listed in the new config but without LogHandler information
+ * defined will have all existing handlers removed.
*/
- std::vector<std::string> processConfigString(folly::StringPiece config);
+ void resetConfig(const LogConfig& config);
/**
* Remove all registered LogHandlers on all LogCategory objects.
*/
void cleanupHandlers();
+ /**
+ * Call flush() on all LogHandler objects registered on any LogCategory in
+ * this LoggerDB.
+ *
+ * Returns the number of registered LogHandlers.
+ */
+ size_t flushAllHandlers();
+
+ /**
+ * Register a LogHandlerFactory.
+ *
+ * The LogHandlerFactory will be used to create LogHandler objects from a
+ * LogConfig object during updateConfig() and resetConfig() calls.
+ *
+ * Only one factory can be registered for a given handler type name.
+ * LogHandlerFactory::getType() returns the handler type supported by this
+ * LogHandlerFactory.
+ *
+ * If an existing LogHandlerFactory is already registered with this type name
+ * and replaceExisting is false a std::range_error will be thrown.
+ * Otherwise, if replaceExisting is true, the new factory will replace the
+ * existing factory.
+ */
+ void registerHandlerFactory(
+ std::unique_ptr<LogHandlerFactory> factory,
+ bool replaceExisting = false);
+
+ /**
+ * Remove a registered LogHandlerFactory.
+ *
+ * The type parameter should be the name of the handler type, as returned by
+ * LogHandlerFactory::getType().
+ *
+ * Throws std::range_error if no handler factory with this type name exists.
+ */
+ void unregisterHandlerFactory(folly::StringPiece type);
+
+ /**
+ * Initialize the LogCategory* and std::atomic<LogLevel> used by an XLOG()
+ * statement.
+ *
+ * Returns the current effective LogLevel of the category.
+ */
+ LogLevel xlogInit(
+ folly::StringPiece categoryName,
+ std::atomic<LogLevel>* xlogCategoryLevel,
+ LogCategory** xlogCategory);
+ LogCategory* xlogInitCategory(
+ folly::StringPiece categoryName,
+ LogCategory** xlogCategory,
+ std::atomic<bool>* isInitialized);
+
enum TestConstructorArg { TESTING };
/**
*/
explicit LoggerDB(TestConstructorArg);
+ /**
+ * internalWarning() is used to report a problem when something goes wrong
+ * internally in the logging library.
+ *
+ * We can't log these messages through the normal logging flow since logging
+ * itself has failed.
+ *
+ * Example scenarios where this is used:
+ * - We fail to write to a log file (for instance, when the disk is full)
+ * - A LogHandler throws an unexpected exception
+ */
+ template <typename... Args>
+ static void internalWarning(
+ folly::StringPiece file,
+ int lineNumber,
+ Args&&... args) noexcept {
+ internalWarningImpl(
+ file, lineNumber, folly::to<std::string>(std::forward<Args>(args)...));
+ }
+
+ using InternalWarningHandler =
+ void (*)(folly::StringPiece file, int lineNumber, std::string&&);
+
+ /**
+ * Set a function to be called when the logging library generates an internal
+ * warning.
+ *
+ * The supplied handler should never throw exceptions.
+ *
+ * If a null handler is supplied, the default built-in handler will be used.
+ *
+ * The default handler reports the message with _CrtDbgReport(_CRT_WARN) on
+ * Windows, and prints the message to stderr on other platforms. It also
+ * rate limits messages if they are arriving too quickly.
+ */
+ static void setInternalWarningHandler(InternalWarningHandler handler);
+
private:
using LoggerNameMap = std::unordered_map<
folly::StringPiece,
LogName::Hash,
LogName::Equals>;
+ using HandlerFactoryMap =
+ std::unordered_map<std::string, std::unique_ptr<LogHandlerFactory>>;
+ using HandlerMap = std::unordered_map<std::string, std::weak_ptr<LogHandler>>;
+ struct HandlerInfo {
+ HandlerFactoryMap factories;
+ HandlerMap handlers;
+ };
+
// Forbidden copy constructor and assignment operator
LoggerDB(LoggerDB const&) = delete;
LoggerDB& operator=(LoggerDB const&) = delete;
folly::StringPiece name,
LogCategory* parent);
+ using NewHandlerMap =
+ std::unordered_map<std::string, std::shared_ptr<LogHandler>>;
+ using OldToNewHandlerMap = std::
+ unordered_map<std::shared_ptr<LogHandler>, std::shared_ptr<LogHandler>>;
+ void startConfigUpdate(
+ const Synchronized<HandlerInfo>::LockedPtr& handlerInfo,
+ const LogConfig& config,
+ NewHandlerMap* handlers,
+ OldToNewHandlerMap* oldToNewHandlerMap);
+ void finishConfigUpdate(
+ const Synchronized<HandlerInfo>::LockedPtr& handlerInfo,
+ NewHandlerMap* handlers,
+ OldToNewHandlerMap* oldToNewHandlerMap);
+ std::vector<std::shared_ptr<LogHandler>> buildCategoryHandlerList(
+ const NewHandlerMap& handlerMap,
+ StringPiece categoryName,
+ const std::vector<std::string>& categoryHandlerNames);
+
+ static void internalWarningImpl(
+ folly::StringPiece filename,
+ int lineNumber,
+ std::string&& msg) noexcept;
+ static void defaultInternalWarningImpl(
+ folly::StringPiece filename,
+ int lineNumber,
+ std::string&& msg) noexcept;
+
/**
* A map of LogCategory objects by name.
*
* have to be in canonical form.
*/
folly::Synchronized<LoggerNameMap> loggersByName_;
+
+ /**
+ * The LogHandlers and LogHandlerFactories.
+ *
+ * For lock ordering purposes, if you need to acquire both the loggersByName_
+ * and handlerInfo_ locks, the handlerInfo_ lock must be acquired first.
+ */
+ folly::Synchronized<HandlerInfo> handlerInfo_;
+
+ static std::atomic<InternalWarningHandler> warningHandler_;
};
-}
+} // namespace folly