a0038b3f3286d0d04e4b70bf5f762807c9bd8ccd
[folly.git] / folly / experimental / logging / LoggerDB.h
1 /*
2  * Copyright 2004-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17
18 #include <folly/Conv.h>
19 #include <folly/CppAttributes.h>
20 #include <folly/Range.h>
21 #include <folly/Synchronized.h>
22 #include <memory>
23 #include <string>
24 #include <unordered_map>
25 #include <vector>
26
27 #include <folly/experimental/logging/LogName.h>
28
29 namespace folly {
30
31 class LogCategory;
32 class LogConfig;
33 class LogHandler;
34 class LogHandlerFactory;
35 enum class LogLevel : uint32_t;
36
37 /**
38  * LoggerDB stores the set of LogCategory objects.
39  */
40 class LoggerDB {
41  public:
42   /**
43    * Get the main LoggerDB singleton.
44    */
45   static LoggerDB* get();
46
47   ~LoggerDB();
48
49   /**
50    * Get the LogCategory for the specified name.
51    *
52    * This creates the LogCategory for the specified name if it does not exist
53    * already.
54    */
55   LogCategory* getCategory(folly::StringPiece name);
56
57   /**
58    * Get the LogCategory for the specified name, if it already exists.
59    *
60    * This returns nullptr if no LogCategory has been created yet for the
61    * specified name.
62    */
63   LogCategory* FOLLY_NULLABLE getCategoryOrNull(folly::StringPiece name);
64
65   /**
66    * Set the log level for the specified category.
67    *
68    * Messages logged to a specific log category will be ignored unless the
69    * message log level is greater than the LogCategory's effective log level.
70    *
71    * If inherit is true, LogCategory's effective log level is the minimum of
72    * its level and it's parent category's effective log level.  If inherit is
73    * false, the LogCategory's effective log level is simply its log level.
74    * (Setting inherit to false is necessary if you want a child LogCategory to
75    * use a less verbose level than its parent categories.)
76    */
77   void setLevel(folly::StringPiece name, LogLevel level, bool inherit = true);
78   void setLevel(LogCategory* category, LogLevel level, bool inherit = true);
79
80   /**
81    * Get a LogConfig object describing the current state of the LoggerDB.
82    *
83    * Note that this may not 100% accurately describe the current configuration
84    * if callers have manually added LogHandlers to some categories without
85    * using the updateConfig() or resetConfig() functions.  In this case
86    * getConfig() will simply report these handlers as "unknown_handler" when
87    * returning handler names for the categories in question.
88    */
89   LogConfig getConfig() const;
90
91   /**
92    * Update the current LoggerDB state with the specified LogConfig settings.
93    *
94    * Log categories and handlers listed in the LogConfig object will be updated
95    * to the new state listed in the LogConfig.  Settings on categories and
96    * handlers not listed in the config will be left as-is.
97    */
98   void updateConfig(const LogConfig& config);
99
100   /**
101    * Reset the current LoggerDB state to the specified LogConfig settings.
102    *
103    * All LogCategories not mentioned in the new LogConfig will have all
104    * currently configured log handlers removed and their log level set to its
105    * default state.  For the root category the default log level is ERR; for
106    * all other categories the default level is MAX_LEVEL with log level
107    * inheritance enabled.
108    *
109    * LogCategories listed in the new config but without LogHandler information
110    * defined will have all existing handlers removed.
111    */
112   void resetConfig(const LogConfig& config);
113
114   /**
115    * Apply a configuration string specifying a series a log levels.
116    *
117    * The string format is a comma separated list of <name>=<level> sections.
118    * e.g.: "foo=DBG3,log.bar=WARN"
119    *
120    * Returns a list of error messages for each error encountered trying to
121    * parse the config string.  The return value will be an empty vector if no
122    * errors were encountered.
123    */
124   std::vector<std::string> processConfigString(folly::StringPiece config);
125
126   /**
127    * Remove all registered LogHandlers on all LogCategory objects.
128    *
129    * This is called on the main LoggerDB object during shutdown.
130    */
131   void cleanupHandlers();
132
133   /**
134    * Call flush() on all LogHandler objects registered on any LogCategory in
135    * this LoggerDB.
136    *
137    * Returns the number of registered LogHandlers.
138    */
139   size_t flushAllHandlers();
140
141   /**
142    * Register a LogHandlerFactory.
143    *
144    * The LogHandlerFactory will be used to create LogHandler objects from a
145    * LogConfig object during updateConfig() and resetConfig() calls.
146    *
147    * Only one factory can be registered for a given handler type name.
148    * LogHandlerFactory::getType() returns the handler type supported by this
149    * LogHandlerFactory.
150    *
151    * If an existing LogHandlerFactory is already registered with this type name
152    * and replaceExisting is false a std::range_error will be thrown.
153    * Otherwise, if replaceExisting is true, the new factory will replace the
154    * existing factory.
155    */
156   void registerHandlerFactory(
157       std::unique_ptr<LogHandlerFactory> factory,
158       bool replaceExisting = false);
159
160   /**
161    * Remove a registered LogHandlerFactory.
162    *
163    * The type parameter should be the name of the handler type, as returned by
164    * LogHandlerFactory::getType().
165    *
166    * Throws std::range_error if no handler factory with this type name exists.
167    */
168   void unregisterHandlerFactory(folly::StringPiece type);
169
170   /**
171    * Initialize the LogCategory* and std::atomic<LogLevel> used by an XLOG()
172    * statement.
173    *
174    * Returns the current effective LogLevel of the category.
175    */
176   LogLevel xlogInit(
177       folly::StringPiece categoryName,
178       std::atomic<LogLevel>* xlogCategoryLevel,
179       LogCategory** xlogCategory);
180   LogCategory* xlogInitCategory(
181       folly::StringPiece categoryName,
182       LogCategory** xlogCategory,
183       std::atomic<bool>* isInitialized);
184
185   enum TestConstructorArg { TESTING };
186
187   /**
188    * Construct a LoggerDB for testing purposes.
189    *
190    * Most callers should not need this function, and should use
191    * LoggerDB::get() to obtain the main LoggerDB singleton.  This function
192    * exists mainly to allow testing LoggerDB objects in unit tests.
193    * It requires an explicit argument just to prevent callers from calling it
194    * unintentionally.
195    */
196   explicit LoggerDB(TestConstructorArg);
197
198   /**
199    * internalWarning() is used to report a problem when something goes wrong
200    * internally in the logging library.
201    *
202    * We can't log these messages through the normal logging flow since logging
203    * itself has failed.
204    *
205    * Example scenarios where this is used:
206    * - We fail to write to a log file (for instance, when the disk is full)
207    * - A LogHandler throws an unexpected exception
208    */
209   template <typename... Args>
210   static void internalWarning(
211       folly::StringPiece file,
212       int lineNumber,
213       Args&&... args) noexcept {
214     internalWarningImpl(
215         file, lineNumber, folly::to<std::string>(std::forward<Args>(args)...));
216   }
217
218   using InternalWarningHandler =
219       void (*)(folly::StringPiece file, int lineNumber, std::string&&);
220
221   /**
222    * Set a function to be called when the logging library generates an internal
223    * warning.
224    *
225    * The supplied handler should never throw exceptions.
226    *
227    * If a null handler is supplied, the default built-in handler will be used.
228    *
229    * The default handler reports the message with _CrtDbgReport(_CRT_WARN) on
230    * Windows, and prints the message to stderr on other platforms.  It also
231    * rate limits messages if they are arriving too quickly.
232    */
233   static void setInternalWarningHandler(InternalWarningHandler handler);
234
235  private:
236   using LoggerNameMap = std::unordered_map<
237       folly::StringPiece,
238       std::unique_ptr<LogCategory>,
239       LogName::Hash,
240       LogName::Equals>;
241
242   using HandlerFactoryMap =
243       std::unordered_map<std::string, std::unique_ptr<LogHandlerFactory>>;
244   using HandlerMap = std::unordered_map<std::string, std::weak_ptr<LogHandler>>;
245   struct HandlerInfo {
246     HandlerFactoryMap factories;
247     HandlerMap handlers;
248   };
249
250   // Forbidden copy constructor and assignment operator
251   LoggerDB(LoggerDB const&) = delete;
252   LoggerDB& operator=(LoggerDB const&) = delete;
253
254   LoggerDB();
255   LogCategory* getOrCreateCategoryLocked(
256       LoggerNameMap& loggersByName,
257       folly::StringPiece name);
258   LogCategory* createCategoryLocked(
259       LoggerNameMap& loggersByName,
260       folly::StringPiece name,
261       LogCategory* parent);
262
263   using NewHandlerMap =
264       std::unordered_map<std::string, std::shared_ptr<LogHandler>>;
265   using OldToNewHandlerMap = std::
266       unordered_map<std::shared_ptr<LogHandler>, std::shared_ptr<LogHandler>>;
267   void startConfigUpdate(
268       const Synchronized<HandlerInfo>::LockedPtr& handlerInfo,
269       const LogConfig& config,
270       NewHandlerMap* handlers,
271       OldToNewHandlerMap* oldToNewHandlerMap);
272   void finishConfigUpdate(
273       const Synchronized<HandlerInfo>::LockedPtr& handlerInfo,
274       NewHandlerMap* handlers,
275       OldToNewHandlerMap* oldToNewHandlerMap);
276   std::vector<std::shared_ptr<LogHandler>> buildCategoryHandlerList(
277       const NewHandlerMap& handlerMap,
278       StringPiece categoryName,
279       const std::vector<std::string>& categoryHandlerNames);
280
281   static void internalWarningImpl(
282       folly::StringPiece filename,
283       int lineNumber,
284       std::string&& msg) noexcept;
285   static void defaultInternalWarningImpl(
286       folly::StringPiece filename,
287       int lineNumber,
288       std::string&& msg) noexcept;
289
290   /**
291    * A map of LogCategory objects by name.
292    *
293    * Lookups can be performed using arbitrary StringPiece values that do not
294    * have to be in canonical form.
295    */
296   folly::Synchronized<LoggerNameMap> loggersByName_;
297
298   /**
299    * The LogHandlers and LogHandlerFactories.
300    *
301    * For lock ordering purposes, if you need to acquire both the loggersByName_
302    * and handlerInfo_ locks, the handlerInfo_ lock must be acquired first.
303    */
304   folly::Synchronized<HandlerInfo> handlerInfo_;
305
306   static std::atomic<InternalWarningHandler> warningHandler_;
307 };
308 } // namespace folly