/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace folly { class LogCategory; class LogConfig; class LogHandler; class LogHandlerFactory; enum class LogLevel : uint32_t; /** * LoggerDB stores the set of LogCategory objects. */ class LoggerDB { using ContextCallback = folly::Function; public: /** * Get the main LoggerDB singleton. */ static LoggerDB& get(); ~LoggerDB(); /** * Get the LogCategory for the specified name. * * This creates the LogCategory for the specified name if it does not exist * already. */ LogCategory* getCategory(folly::StringPiece name); /** * Get the LogCategory for the specified name, if it already exists. * * This returns nullptr if no LogCategory has been created yet for the * specified name. */ LogCategory* FOLLY_NULLABLE getCategoryOrNull(folly::StringPiece name); /** * Set the log level for the specified category. * * Messages logged to a specific log category will be ignored unless the * message log level is greater than the LogCategory's effective log level. * * If inherit is true, LogCategory's effective log level is the minimum of * its level and it's parent category's effective log level. If inherit is * false, the LogCategory's effective log level is simply its log level. * (Setting inherit to false is necessary if you want a child LogCategory to * use a less verbose level than its parent categories.) */ void setLevel(folly::StringPiece name, LogLevel level, bool inherit = true); void setLevel(LogCategory* category, LogLevel level, bool inherit = true); /** * Get a LogConfig object describing the current state of the LoggerDB. */ LogConfig getConfig() const; /** * Get a LogConfig object fully describing the state of the LoggerDB. * * This is similar to getConfig(), but it returns LogCategoryConfig objects * for all defined log categories, including ones that are using the default * configuration settings. */ LogConfig getFullConfig() 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. * * 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 * kDefaultLogLevel (see LogLevel.h); for all other categories the default * level is MAX_LEVEL with log level inheritance enabled. * * LogCategories listed in the new config but without LogHandler information * defined will have all existing handlers removed. */ void resetConfig(const LogConfig& config); /** * Remove all registered LogHandlers on all LogCategory objects. * * This is called on the main LoggerDB object during shutdown. */ 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 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 used by an XLOG() * statement. * * Returns the current effective LogLevel of the category. */ LogLevel xlogInit( folly::StringPiece categoryName, std::atomic* xlogCategoryLevel, LogCategory** xlogCategory); LogCategory* xlogInitCategory( folly::StringPiece categoryName, LogCategory** xlogCategory, std::atomic* isInitialized); enum TestConstructorArg { TESTING }; /** * Construct a LoggerDB for testing purposes. * * Most callers should not need this function, and should use * LoggerDB::get() to obtain the main LoggerDB singleton. This function * exists mainly to allow testing LoggerDB objects in unit tests. * It requires an explicit argument just to prevent callers from calling it * unintentionally. */ explicit LoggerDB(TestConstructorArg); /** * Add a new context string callback to the list. * * The callbacks will be invoked during the construction of log messages, * and returned strings will be appended in order to the tail of log * log entry prefixes with space prepended to each item. */ void addContextCallback(ContextCallback); /** * Return a context string to be appended after default log prefixes. * * The context string is cutomized through adding context callbacks to * LoggerDB objects. */ std::string getContextString() const; /** * 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 static void internalWarning( folly::StringPiece file, int lineNumber, Args&&... args) noexcept { internalWarningImpl( file, lineNumber, folly::to(std::forward(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, std::unique_ptr, LogName::Hash, LogName::Equals>; using HandlerFactoryMap = std::unordered_map>; using HandlerMap = std::unordered_map>; struct HandlerInfo { HandlerFactoryMap factories; HandlerMap handlers; }; class ContextCallbackList { public: void addCallback(ContextCallback); std::string getContextString() const; ~ContextCallbackList(); private: class CallbacksObj; std::atomic callbacks_{nullptr}; std::mutex writeMutex_; }; // Forbidden copy constructor and assignment operator LoggerDB(LoggerDB const&) = delete; LoggerDB& operator=(LoggerDB const&) = delete; LoggerDB(); LogCategory* getOrCreateCategoryLocked( LoggerNameMap& loggersByName, folly::StringPiece name); LogCategory* createCategoryLocked( LoggerNameMap& loggersByName, folly::StringPiece name, LogCategory* parent); using NewHandlerMap = std::unordered_map>; using OldToNewHandlerMap = std:: unordered_map, std::shared_ptr>; LogConfig getConfigImpl(bool includeAllCategories) const; void startConfigUpdate( const Synchronized::LockedPtr& handlerInfo, const LogConfig& config, NewHandlerMap* handlers, OldToNewHandlerMap* oldToNewHandlerMap); void finishConfigUpdate( const Synchronized::LockedPtr& handlerInfo, NewHandlerMap* handlers, OldToNewHandlerMap* oldToNewHandlerMap); std::vector> buildCategoryHandlerList( const NewHandlerMap& handlerMap, StringPiece categoryName, const std::vector& 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. * * Lookups can be performed using arbitrary StringPiece values that do not * have to be in canonical form. */ folly::Synchronized 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_; /** * Callbacks returning context strings. * * Exceptions from the callbacks are catched and reflected in corresponding * position in log entries. */ ContextCallbackList contextCallbacks_; static std::atomic warningHandler_; }; /** * initializeLoggerDB() will be called to configure the main LoggerDB singleton * the first time that LoggerDB::get() is called. * * This function can be used apply basic default settings to the LoggerDB, * including default log level and log handler settings. * * A default implementation is provided as a weak symbol, so it can be * overridden on a per-program basis if you want to customize the initial * LoggerDB settings for your program. * * However, note that this function may be invoked before main() starts (if * other code that runs before main uses the logging library). Therefore you * should be careful about what code runs here. For instance, you probably * should not create LogHandler objects that spawn new threads. It is * generally a good idea to defer more complicated setup until after main() * starts. * * In most situations it is normally better to override getBaseLoggingConfig() * from logging/Init.h rather than overriding initializeLoggerDB(). You only * need to override initializeLoggerDB() if you want to change the settings * that are used for messages that get logged before initLogging() is called. * * The default implementation configures the root log category to write all * warning and higher-level log messages to stderr, using a format similar to * that used by GLOG. */ void initializeLoggerDB(LoggerDB& db); FOLLY_ALWAYS_INLINE LoggerDB& LoggerDB::get() { struct Singleton : LoggerDB { Singleton() { initializeLoggerDB(*this); // This allows log handlers to flush any buffered messages before // the program exits. /* library-local */ static auto guard = makeGuard([this] { cleanupHandlers(); }); } }; // We intentionally leak the LoggerDB singleton and all of the LogCategory // objects it contains. // // We want Logger objects to remain valid for the entire lifetime of the // program, without having to worry about destruction ordering issues, or // making the Logger perform reference counting on the LoggerDB. return detail::createGlobal(); } } // namespace folly