logging: add a LogHandler registry to LoggerDB
[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 LogHandler;
33 class LogHandlerFactory;
34 enum class LogLevel : uint32_t;
35
36 /**
37  * LoggerDB stores the set of LogCategory objects.
38  */
39 class LoggerDB {
40  public:
41   /**
42    * Get the main LoggerDB singleton.
43    */
44   static LoggerDB* get();
45
46   ~LoggerDB();
47
48   /**
49    * Get the LogCategory for the specified name.
50    *
51    * This creates the LogCategory for the specified name if it does not exist
52    * already.
53    */
54   LogCategory* getCategory(folly::StringPiece name);
55
56   /**
57    * Get the LogCategory for the specified name, if it already exists.
58    *
59    * This returns nullptr if no LogCategory has been created yet for the
60    * specified name.
61    */
62   LogCategory* FOLLY_NULLABLE getCategoryOrNull(folly::StringPiece name);
63
64   /**
65    * Set the log level for the specified category.
66    *
67    * Messages logged to a specific log category will be ignored unless the
68    * message log level is greater than the LogCategory's effective log level.
69    *
70    * If inherit is true, LogCategory's effective log level is the minimum of
71    * its level and it's parent category's effective log level.  If inherit is
72    * false, the LogCategory's effective log level is simply its log level.
73    * (Setting inherit to false is necessary if you want a child LogCategory to
74    * use a less verbose level than its parent categories.)
75    */
76   void setLevel(folly::StringPiece name, LogLevel level, bool inherit = true);
77   void setLevel(LogCategory* category, LogLevel level, bool inherit = true);
78
79   /**
80    * Apply a configuration string specifying a series a log levels.
81    *
82    * The string format is a comma separated list of <name>=<level> sections.
83    * e.g.: "foo=DBG3,log.bar=WARN"
84    *
85    * Returns a list of error messages for each error encountered trying to
86    * parse the config string.  The return value will be an empty vector if no
87    * errors were encountered.
88    */
89   std::vector<std::string> processConfigString(folly::StringPiece config);
90
91   /**
92    * Remove all registered LogHandlers on all LogCategory objects.
93    *
94    * This is called on the main LoggerDB object during shutdown.
95    */
96   void cleanupHandlers();
97
98   /**
99    * Call flush() on all LogHandler objects registered on any LogCategory in
100    * this LoggerDB.
101    *
102    * Returns the number of registered LogHandlers.
103    */
104   size_t flushAllHandlers();
105
106   /**
107    * Register a LogHandlerFactory.
108    *
109    * The LogHandlerFactory will be used to create LogHandler objects from a
110    * LogConfig object during updateConfig() and resetConfig() calls.
111    *
112    * Only one factory can be registered for a given handler type name.
113    * LogHandlerFactory::getType() returns the handler type supported by this
114    * LogHandlerFactory.
115    *
116    * If an existing LogHandlerFactory is already registered with this type name
117    * and replaceExisting is false a std::range_error will be thrown.
118    * Otherwise, if replaceExisting is true, the new factory will replace the
119    * existing factory.
120    */
121   void registerHandlerFactory(
122       std::unique_ptr<LogHandlerFactory> factory,
123       bool replaceExisting = false);
124
125   /**
126    * Remove a registered LogHandlerFactory.
127    *
128    * The type parameter should be the name of the handler type, as returned by
129    * LogHandlerFactory::getType().
130    *
131    * Throws std::range_error if no handler factory with this type name exists.
132    */
133   void unregisterHandlerFactory(folly::StringPiece type);
134
135   /**
136    * Initialize the LogCategory* and std::atomic<LogLevel> used by an XLOG()
137    * statement.
138    *
139    * Returns the current effective LogLevel of the category.
140    */
141   LogLevel xlogInit(
142       folly::StringPiece categoryName,
143       std::atomic<LogLevel>* xlogCategoryLevel,
144       LogCategory** xlogCategory);
145   LogCategory* xlogInitCategory(
146       folly::StringPiece categoryName,
147       LogCategory** xlogCategory,
148       std::atomic<bool>* isInitialized);
149
150   enum TestConstructorArg { TESTING };
151
152   /**
153    * Construct a LoggerDB for testing purposes.
154    *
155    * Most callers should not need this function, and should use
156    * LoggerDB::get() to obtain the main LoggerDB singleton.  This function
157    * exists mainly to allow testing LoggerDB objects in unit tests.
158    * It requires an explicit argument just to prevent callers from calling it
159    * unintentionally.
160    */
161   explicit LoggerDB(TestConstructorArg);
162
163   /**
164    * internalWarning() is used to report a problem when something goes wrong
165    * internally in the logging library.
166    *
167    * We can't log these messages through the normal logging flow since logging
168    * itself has failed.
169    *
170    * Example scenarios where this is used:
171    * - We fail to write to a log file (for instance, when the disk is full)
172    * - A LogHandler throws an unexpected exception
173    */
174   template <typename... Args>
175   static void internalWarning(
176       folly::StringPiece file,
177       int lineNumber,
178       Args&&... args) noexcept {
179     internalWarningImpl(
180         file, lineNumber, folly::to<std::string>(std::forward<Args>(args)...));
181   }
182
183   using InternalWarningHandler =
184       void (*)(folly::StringPiece file, int lineNumber, std::string&&);
185
186   /**
187    * Set a function to be called when the logging library generates an internal
188    * warning.
189    *
190    * The supplied handler should never throw exceptions.
191    *
192    * If a null handler is supplied, the default built-in handler will be used.
193    *
194    * The default handler reports the message with _CrtDbgReport(_CRT_WARN) on
195    * Windows, and prints the message to stderr on other platforms.  It also
196    * rate limits messages if they are arriving too quickly.
197    */
198   static void setInternalWarningHandler(InternalWarningHandler handler);
199
200  private:
201   using LoggerNameMap = std::unordered_map<
202       folly::StringPiece,
203       std::unique_ptr<LogCategory>,
204       LogName::Hash,
205       LogName::Equals>;
206
207   using HandlerFactoryMap =
208       std::unordered_map<std::string, std::unique_ptr<LogHandlerFactory>>;
209   using HandlerMap = std::unordered_map<std::string, std::weak_ptr<LogHandler>>;
210   struct HandlerInfo {
211     HandlerFactoryMap factories;
212     HandlerMap handlers;
213   };
214
215   // Forbidden copy constructor and assignment operator
216   LoggerDB(LoggerDB const&) = delete;
217   LoggerDB& operator=(LoggerDB const&) = delete;
218
219   LoggerDB();
220   LogCategory* getOrCreateCategoryLocked(
221       LoggerNameMap& loggersByName,
222       folly::StringPiece name);
223   LogCategory* createCategoryLocked(
224       LoggerNameMap& loggersByName,
225       folly::StringPiece name,
226       LogCategory* parent);
227
228   static void internalWarningImpl(
229       folly::StringPiece filename,
230       int lineNumber,
231       std::string&& msg) noexcept;
232   static void defaultInternalWarningImpl(
233       folly::StringPiece filename,
234       int lineNumber,
235       std::string&& msg) noexcept;
236
237   /**
238    * A map of LogCategory objects by name.
239    *
240    * Lookups can be performed using arbitrary StringPiece values that do not
241    * have to be in canonical form.
242    */
243   folly::Synchronized<LoggerNameMap> loggersByName_;
244
245   /**
246    * The LogHandlers and LogHandlerFactories.
247    */
248   folly::Synchronized<HandlerInfo> handlerInfo_;
249
250   static std::atomic<InternalWarningHandler> warningHandler_;
251 };
252 } // namespace folly