2611e23c98101dde2d9b9f9382a2e6bf2c81ac56
[folly.git] / folly / experimental / logging / LoggerDB.cpp
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 #include <folly/experimental/logging/LoggerDB.h>
17
18 #include <set>
19
20 #include <folly/Conv.h>
21 #include <folly/FileUtil.h>
22 #include <folly/String.h>
23 #include <folly/experimental/logging/LogCategory.h>
24 #include <folly/experimental/logging/LogConfig.h>
25 #include <folly/experimental/logging/LogHandler.h>
26 #include <folly/experimental/logging/LogHandlerFactory.h>
27 #include <folly/experimental/logging/LogLevel.h>
28 #include <folly/experimental/logging/Logger.h>
29 #include <folly/experimental/logging/RateLimiter.h>
30
31 using std::string;
32
33 namespace folly {
34
35 namespace {
36 class LoggerDBSingleton {
37  public:
38   explicit LoggerDBSingleton(LoggerDB* db) : db_{db} {}
39   ~LoggerDBSingleton() {
40     // We intentionally leak the LoggerDB object on destruction.
41     // We want Logger objects to remain valid for the entire lifetime of the
42     // program, without having to worry about destruction ordering issues, or
43     // making the Logger perform reference counting on the LoggerDB.
44     //
45     // Therefore the main LoggerDB object, and all of the LogCategory objects
46     // it contains, are always intentionally leaked.
47     //
48     // However, we do call db_->cleanupHandlers() to destroy any registered
49     // LogHandler objects.  The LogHandlers can be user-defined objects and may
50     // hold resources that should be cleaned up.  This also ensures that the
51     // LogHandlers flush all outstanding messages before we exit.
52     db_->cleanupHandlers();
53   }
54
55   LoggerDB* getDB() const {
56     return db_;
57   }
58
59  private:
60   LoggerDB* db_;
61 };
62 } // namespace
63
64 LoggerDB* LoggerDB::get() {
65   // Intentionally leaky singleton
66   static LoggerDBSingleton singleton{new LoggerDB()};
67   return singleton.getDB();
68 }
69
70 LoggerDB::LoggerDB() {
71   // Create the root log category, and set the level to ERR by default
72   auto rootUptr = std::make_unique<LogCategory>(this);
73   LogCategory* root = rootUptr.get();
74   auto ret =
75       loggersByName_.wlock()->emplace(root->getName(), std::move(rootUptr));
76   DCHECK(ret.second);
77
78   root->setLevelLocked(LogLevel::ERR, false);
79 }
80
81 LoggerDB::LoggerDB(TestConstructorArg) : LoggerDB() {}
82
83 LoggerDB::~LoggerDB() {}
84
85 LogCategory* LoggerDB::getCategory(StringPiece name) {
86   return getOrCreateCategoryLocked(*loggersByName_.wlock(), name);
87 }
88
89 LogCategory* FOLLY_NULLABLE LoggerDB::getCategoryOrNull(StringPiece name) {
90   auto loggersByName = loggersByName_.rlock();
91
92   auto it = loggersByName->find(name);
93   if (it == loggersByName->end()) {
94     return nullptr;
95   }
96   return it->second.get();
97 }
98
99 void LoggerDB::setLevel(folly::StringPiece name, LogLevel level, bool inherit) {
100   auto loggersByName = loggersByName_.wlock();
101   LogCategory* category = getOrCreateCategoryLocked(*loggersByName, name);
102   category->setLevelLocked(level, inherit);
103 }
104
105 void LoggerDB::setLevel(LogCategory* category, LogLevel level, bool inherit) {
106   auto loggersByName = loggersByName_.wlock();
107   category->setLevelLocked(level, inherit);
108 }
109
110 std::vector<std::string> LoggerDB::processConfigString(
111     folly::StringPiece config) {
112   std::vector<std::string> errors;
113   if (config.empty()) {
114     return errors;
115   }
116
117   std::vector<StringPiece> pieces;
118   folly::split(",", config, pieces);
119   for (const auto& p : pieces) {
120     auto idx = p.rfind('=');
121     if (idx == folly::StringPiece::npos) {
122       errors.emplace_back(
123           folly::sformat("missing '=' in logger configuration: \"{}\"", p));
124       continue;
125     }
126
127     auto category = p.subpiece(0, idx);
128     auto level_str = p.subpiece(idx + 1);
129     LogLevel level;
130     try {
131       level = stringToLogLevel(level_str);
132     } catch (const std::exception&) {
133       errors.emplace_back(folly::sformat(
134           "invalid log level \"{}\" for category \"{}\"", level_str, category));
135       continue;
136     }
137
138     setLevel(category, level);
139   }
140
141   return errors;
142 }
143
144 LogConfig LoggerDB::getConfig() const {
145   auto handlerInfo = handlerInfo_.rlock();
146
147   LogConfig::HandlerConfigMap handlerConfigs;
148   std::unordered_map<std::shared_ptr<LogHandler>, string> handlersToName;
149   for (const auto& entry : handlerInfo->handlers) {
150     auto handler = entry.second.lock();
151     if (!handler) {
152       continue;
153     }
154     handlersToName.emplace(handler, entry.first);
155     handlerConfigs.emplace(entry.first, handler->getConfig());
156   }
157
158   size_t anonymousNameIndex = 1;
159   auto generateAnonymousHandlerName = [&]() {
160     // Return a unique name of the form "anonymousHandlerN"
161     // Keep incrementing N until we find a name that isn't currently taken.
162     while (true) {
163       auto name = to<string>("anonymousHandler", anonymousNameIndex);
164       ++anonymousNameIndex;
165       if (handlerInfo->handlers.find(name) == handlerInfo->handlers.end()) {
166         return name;
167       }
168     }
169   };
170
171   LogConfig::CategoryConfigMap categoryConfigs;
172   {
173     auto loggersByName = loggersByName_.rlock();
174     for (const auto& entry : *loggersByName) {
175       auto* category = entry.second.get();
176       auto levelInfo = category->getLevelInfo();
177       auto handlers = category->getHandlers();
178
179       // Don't report categories that have default settings.
180       if (handlers.empty() && levelInfo.first == LogLevel::MAX_LEVEL &&
181           levelInfo.second) {
182         continue;
183       }
184
185       // Translate the handler pointers to names
186       std::vector<string> handlerNames;
187       for (const auto& handler : handlers) {
188         auto iter = handlersToName.find(handler);
189         if (iter == handlersToName.end()) {
190           // This LogHandler must have been manually attached to the category,
191           // rather than defined with `updateConfig()` or `resetConfig()`.
192           // Generate a unique name to use for reporting it in the config.
193           auto name = generateAnonymousHandlerName();
194           handlersToName.emplace(handler, name);
195           handlerConfigs.emplace(name, handler->getConfig());
196           handlerNames.emplace_back(name);
197         } else {
198           handlerNames.emplace_back(iter->second);
199         }
200       }
201
202       LogCategoryConfig categoryConfig(
203           levelInfo.first, levelInfo.second, handlerNames);
204       categoryConfigs.emplace(category->getName(), std::move(categoryConfig));
205     }
206   }
207
208   return LogConfig{std::move(handlerConfigs), std::move(categoryConfigs)};
209 }
210
211 /**
212  * Process handler config information when starting a config update operation.
213  */
214 void LoggerDB::startConfigUpdate(
215     const Synchronized<HandlerInfo>::LockedPtr& handlerInfo,
216     const LogConfig& config,
217     NewHandlerMap* handlers,
218     OldToNewHandlerMap* oldToNewHandlerMap) {
219   // Get a map of all currently existing LogHandlers.
220   // This resolves weak_ptrs to shared_ptrs, and ignores expired weak_ptrs.
221   // This prevents any of these LogHandler pointers from expiring during the
222   // config update.
223   for (const auto& entry : handlerInfo->handlers) {
224     auto handler = entry.second.lock();
225     if (handler) {
226       handlers->emplace(entry.first, std::move(handler));
227     }
228   }
229
230   // Create all of the new LogHandlers needed from this configuration
231   for (const auto& entry : config.getHandlerConfigs()) {
232     // Look up the LogHandlerFactory
233     auto factoryIter = handlerInfo->factories.find(entry.second.type);
234     if (factoryIter == handlerInfo->factories.end()) {
235       throw std::invalid_argument(to<std::string>(
236           "unknown log handler type \"", entry.second.type, "\""));
237     }
238
239     // Check to see if there is an existing LogHandler with this name
240     std::shared_ptr<LogHandler> oldHandler;
241     auto iter = handlers->find(entry.first);
242     if (iter != handlers->end()) {
243       oldHandler = iter->second;
244     }
245
246     // Create the new log handler
247     const auto& factory = factoryIter->second;
248     std::shared_ptr<LogHandler> handler;
249     if (oldHandler) {
250       handler = factory->updateHandler(oldHandler, entry.second.options);
251       if (handler != oldHandler) {
252         oldToNewHandlerMap->emplace(oldHandler, handler);
253       }
254     } else {
255       handler = factory->createHandler(entry.second.options);
256     }
257     handlerInfo->handlers[entry.first] = handler;
258     (*handlers)[entry.first] = handler;
259   }
260
261   // Before we start making any LogCategory changes, confirm that all handlers
262   // named in the category configs are known handlers.
263   for (const auto& entry : config.getCategoryConfigs()) {
264     if (!entry.second.handlers.hasValue()) {
265       continue;
266     }
267     for (const auto& handlerName : entry.second.handlers.value()) {
268       auto iter = handlers->find(handlerName);
269       if (iter == handlers->end()) {
270         throw std::invalid_argument(to<std::string>(
271             "unknown log handler \"",
272             handlerName,
273             "\" configured for log category \"",
274             entry.first,
275             "\""));
276       }
277     }
278   }
279 }
280
281 /**
282  * Update handlerInfo_ at the end of a config update operation.
283  */
284 void LoggerDB::finishConfigUpdate(
285     const Synchronized<HandlerInfo>::LockedPtr& handlerInfo,
286     NewHandlerMap* handlers,
287     OldToNewHandlerMap* oldToNewHandlerMap) {
288   // Build a new map to replace handlerInfo->handlers
289   // This will contain only the LogHandlers that are still in use by the
290   // current LogCategory settings.
291   std::unordered_map<std::string, std::weak_ptr<LogHandler>> newHandlerMap;
292   for (const auto& entry : *handlers) {
293     newHandlerMap.emplace(entry.first, entry.second);
294   }
295   // Drop all of our shared_ptr references to LogHandler objects,
296   // and then remove entries in newHandlerMap that are unreferenced.
297   handlers->clear();
298   oldToNewHandlerMap->clear();
299   handlerInfo->handlers.clear();
300   for (auto iter = newHandlerMap.begin(); iter != newHandlerMap.end(); /**/) {
301     if (iter->second.expired()) {
302       iter = newHandlerMap.erase(iter);
303     } else {
304       ++iter;
305     }
306   }
307   handlerInfo->handlers.swap(newHandlerMap);
308 }
309
310 std::vector<std::shared_ptr<LogHandler>> LoggerDB::buildCategoryHandlerList(
311     const NewHandlerMap& handlerMap,
312     StringPiece categoryName,
313     const std::vector<std::string>& categoryHandlerNames) {
314   std::vector<std::shared_ptr<LogHandler>> catHandlers;
315   for (const auto& handlerName : categoryHandlerNames) {
316     auto iter = handlerMap.find(handlerName);
317     if (iter == handlerMap.end()) {
318       // This really shouldn't be possible; the checks in startConfigUpdate()
319       // should have already bailed out if there was an unknown handler.
320       throw std::invalid_argument(to<std::string>(
321           "bug: unknown log handler \"",
322           handlerName,
323           "\" configured for log category \"",
324           categoryName,
325           "\""));
326     }
327     catHandlers.push_back(iter->second);
328   }
329
330   return catHandlers;
331 }
332
333 void LoggerDB::updateConfig(const LogConfig& config) {
334   // Grab the handlerInfo_ lock.
335   // We hold it in write mode for the entire config update operation.  This
336   // ensures that only a single config update operation ever runs at once.
337   auto handlerInfo = handlerInfo_.wlock();
338
339   NewHandlerMap handlers;
340   OldToNewHandlerMap oldToNewHandlerMap;
341   startConfigUpdate(handlerInfo, config, &handlers, &oldToNewHandlerMap);
342
343   // If an existing LogHandler was replaced with a new one,
344   // walk all current LogCategories and replace this handler.
345   if (!oldToNewHandlerMap.empty()) {
346     auto loggerMap = loggersByName_.rlock();
347     for (const auto& entry : *loggerMap) {
348       entry.second->updateHandlers(oldToNewHandlerMap);
349     }
350   }
351
352   // Update log levels and handlers mentioned in the config update
353   auto loggersByName = loggersByName_.wlock();
354   for (const auto& entry : config.getCategoryConfigs()) {
355     LogCategory* category =
356         getOrCreateCategoryLocked(*loggersByName, entry.first);
357
358     // Update the log handlers
359     if (entry.second.handlers.hasValue()) {
360       auto catHandlers = buildCategoryHandlerList(
361           handlers, entry.first, entry.second.handlers.value());
362       category->replaceHandlers(std::move(catHandlers));
363     }
364
365     // Update the level settings
366     category->setLevelLocked(
367         entry.second.level, entry.second.inheritParentLevel);
368   }
369
370   finishConfigUpdate(handlerInfo, &handlers, &oldToNewHandlerMap);
371 }
372
373 void LoggerDB::resetConfig(const LogConfig& config) {
374   // Grab the handlerInfo_ lock.
375   // We hold it in write mode for the entire config update operation.  This
376   // ensures that only a single config update operation ever runs at once.
377   auto handlerInfo = handlerInfo_.wlock();
378
379   NewHandlerMap handlers;
380   OldToNewHandlerMap oldToNewHandlerMap;
381   startConfigUpdate(handlerInfo, config, &handlers, &oldToNewHandlerMap);
382
383   // Make sure all log categories mentioned in the new config exist.
384   // This ensures that we will cover them in our walk below.
385   LogCategory* rootCategory;
386   {
387     auto loggersByName = loggersByName_.wlock();
388     rootCategory = getOrCreateCategoryLocked(*loggersByName, "");
389     for (const auto& entry : config.getCategoryConfigs()) {
390       getOrCreateCategoryLocked(*loggersByName, entry.first);
391     }
392   }
393
394   {
395     // Update all log categories
396     auto loggersByName = loggersByName_.rlock();
397     for (const auto& entry : *loggersByName) {
398       auto* category = entry.second.get();
399
400       auto configIter = config.getCategoryConfigs().find(category->getName());
401       if (configIter == config.getCategoryConfigs().end()) {
402         // This category is not listed in the config settings.
403         // Reset it to the default settings.
404         category->clearHandlers();
405
406         if (category == rootCategory) {
407           category->setLevelLocked(LogLevel::ERR, false);
408         } else {
409           category->setLevelLocked(LogLevel::MAX_LEVEL, true);
410         }
411         continue;
412       }
413
414       const auto& catConfig = configIter->second;
415
416       // Update the category log level
417       category->setLevelLocked(catConfig.level, catConfig.inheritParentLevel);
418
419       // Update the category handlers list.
420       // If the handler list is not set in the config, clear out any existing
421       // handlers rather than leaving it as-is.
422       std::vector<std::shared_ptr<LogHandler>> catHandlers;
423       if (catConfig.handlers.hasValue()) {
424         catHandlers = buildCategoryHandlerList(
425             handlers, entry.first, catConfig.handlers.value());
426       }
427       category->replaceHandlers(std::move(catHandlers));
428     }
429   }
430
431   finishConfigUpdate(handlerInfo, &handlers, &oldToNewHandlerMap);
432 }
433
434 LogCategory* LoggerDB::getOrCreateCategoryLocked(
435     LoggerNameMap& loggersByName,
436     StringPiece name) {
437   auto it = loggersByName.find(name);
438   if (it != loggersByName.end()) {
439     return it->second.get();
440   }
441
442   StringPiece parentName = LogName::getParent(name);
443   LogCategory* parent = getOrCreateCategoryLocked(loggersByName, parentName);
444   return createCategoryLocked(loggersByName, name, parent);
445 }
446
447 LogCategory* LoggerDB::createCategoryLocked(
448     LoggerNameMap& loggersByName,
449     StringPiece name,
450     LogCategory* parent) {
451   auto uptr = std::make_unique<LogCategory>(name, parent);
452   LogCategory* logger = uptr.get();
453   auto ret = loggersByName.emplace(logger->getName(), std::move(uptr));
454   DCHECK(ret.second);
455   return logger;
456 }
457
458 void LoggerDB::cleanupHandlers() {
459   // Get a copy of all categories, so we can call clearHandlers() without
460   // holding the loggersByName_ lock.  We don't need to worry about LogCategory
461   // lifetime, since LogCategory objects always live for the lifetime of the
462   // LoggerDB.
463   std::vector<LogCategory*> categories;
464   {
465     auto loggersByName = loggersByName_.wlock();
466     categories.reserve(loggersByName->size());
467     for (const auto& entry : *loggersByName) {
468       categories.push_back(entry.second.get());
469     }
470   }
471
472   // Also extract our HandlerFactoryMap and HandlerMap, so we can clear them
473   // later without holding the handlerInfo_ lock.
474   HandlerFactoryMap factories;
475   HandlerMap handlers;
476   {
477     auto handlerInfo = handlerInfo_.wlock();
478     factories.swap(handlerInfo->factories);
479     handlers.swap(handlerInfo->handlers);
480   }
481
482   // Remove all of the LogHandlers from all log categories,
483   // to drop any shared_ptr references to the LogHandlers
484   for (auto* category : categories) {
485     category->clearHandlers();
486   }
487 }
488
489 size_t LoggerDB::flushAllHandlers() {
490   // Build a set of all LogHandlers.  We use a set to avoid calling flush()
491   // more than once on the same handler if it is registered on multiple
492   // different categories.
493   std::set<std::shared_ptr<LogHandler>> handlers;
494   {
495     auto loggersByName = loggersByName_.wlock();
496     for (const auto& entry : *loggersByName) {
497       for (const auto& handler : entry.second->getHandlers()) {
498         handlers.emplace(handler);
499       }
500     }
501   }
502
503   // Call flush() on each handler
504   for (const auto& handler : handlers) {
505     handler->flush();
506   }
507   return handlers.size();
508 }
509
510 void LoggerDB::registerHandlerFactory(
511     std::unique_ptr<LogHandlerFactory> factory,
512     bool replaceExisting) {
513   auto type = factory->getType();
514   auto handlerInfo = handlerInfo_.wlock();
515   if (replaceExisting) {
516     handlerInfo->factories[type.str()] = std::move(factory);
517   } else {
518     auto ret = handlerInfo->factories.emplace(type.str(), std::move(factory));
519     if (!ret.second) {
520       throw std::range_error(to<std::string>(
521           "a LogHandlerFactory for the type \"", type, "\" already exists"));
522     }
523   }
524 }
525
526 void LoggerDB::unregisterHandlerFactory(StringPiece type) {
527   auto handlerInfo = handlerInfo_.wlock();
528   auto numRemoved = handlerInfo->factories.erase(type.str());
529   if (numRemoved != 1) {
530     throw std::range_error(
531         to<std::string>("no LogHandlerFactory for type \"", type, "\" found"));
532   }
533 }
534
535 LogLevel LoggerDB::xlogInit(
536     StringPiece categoryName,
537     std::atomic<LogLevel>* xlogCategoryLevel,
538     LogCategory** xlogCategory) {
539   // Hold the lock for the duration of the operation
540   // xlogInit() may be called from multiple threads simultaneously.
541   // Only one needs to perform the initialization.
542   auto loggersByName = loggersByName_.wlock();
543   if (xlogCategory != nullptr && *xlogCategory != nullptr) {
544     // The xlogCategory was already initialized before we acquired the lock
545     return (*xlogCategory)->getEffectiveLevel();
546   }
547
548   auto* category = getOrCreateCategoryLocked(*loggersByName, categoryName);
549   if (xlogCategory) {
550     // Set *xlogCategory before we update xlogCategoryLevel below.
551     // This is important, since the XLOG() macros check xlogCategoryLevel to
552     // tell if *xlogCategory has been initialized yet.
553     *xlogCategory = category;
554   }
555   auto level = category->getEffectiveLevel();
556   xlogCategoryLevel->store(level, std::memory_order_release);
557   category->registerXlogLevel(xlogCategoryLevel);
558   return level;
559 }
560
561 LogCategory* LoggerDB::xlogInitCategory(
562     StringPiece categoryName,
563     LogCategory** xlogCategory,
564     std::atomic<bool>* isInitialized) {
565   // Hold the lock for the duration of the operation
566   // xlogInitCategory() may be called from multiple threads simultaneously.
567   // Only one needs to perform the initialization.
568   auto loggersByName = loggersByName_.wlock();
569   if (isInitialized->load(std::memory_order_acquire)) {
570     // The xlogCategory was already initialized before we acquired the lock
571     return *xlogCategory;
572   }
573
574   auto* category = getOrCreateCategoryLocked(*loggersByName, categoryName);
575   *xlogCategory = category;
576   isInitialized->store(true, std::memory_order_release);
577   return category;
578 }
579
580 std::atomic<LoggerDB::InternalWarningHandler> LoggerDB::warningHandler_;
581
582 void LoggerDB::internalWarningImpl(
583     folly::StringPiece filename,
584     int lineNumber,
585     std::string&& msg) noexcept {
586   auto handler = warningHandler_.load();
587   if (handler) {
588     handler(filename, lineNumber, std::move(msg));
589   } else {
590     defaultInternalWarningImpl(filename, lineNumber, std::move(msg));
591   }
592 }
593
594 void LoggerDB::setInternalWarningHandler(InternalWarningHandler handler) {
595   // This API is intentionally pretty basic.  It has a number of limitations:
596   //
597   // - We only support plain function pointers, and not full std::function
598   //   objects.  This makes it possible to use std::atomic to access the
599   //   handler pointer, and also makes it safe to store in a zero-initialized
600   //   file-static pointer.
601   //
602   // - We don't support any void* argument to the handler.  The caller is
603   //   responsible for storing any callback state themselves.
604   //
605   // - When replacing or unsetting a handler we don't make any guarantees about
606   //   when the old handler will stop being called.  It may still be called
607   //   from other threads briefly even after setInternalWarningHandler()
608   //   returns.  This is also a consequence of using std::atomic rather than a
609   //   full lock.
610   //
611   // This provides the minimum capabilities needed to customize the handler,
612   // while still keeping the implementation simple and safe to use even before
613   // main().
614   warningHandler_.store(handler);
615 }
616
617 void LoggerDB::defaultInternalWarningImpl(
618     folly::StringPiece filename,
619     int lineNumber,
620     std::string&& msg) noexcept {
621   // Rate limit to 10 messages every 5 seconds.
622   //
623   // We intentonally use a leaky Meyer's singleton here over folly::Singleton:
624   // - We want this code to work even before main()
625   // - This singleton does not depend on any other singletons.
626   static auto* rateLimiter =
627       new logging::IntervalRateLimiter{10, std::chrono::seconds(5)};
628   if (!rateLimiter->check()) {
629     return;
630   }
631
632   if (folly::kIsDebug) {
633     // Write directly to file descriptor 2.
634     //
635     // It's possible the application has closed fd 2 and is using it for
636     // something other than stderr.  However we have no good way to detect
637     // this, which is the main reason we only write to stderr in debug build
638     // modes.  assert() also writes directly to stderr on failure, which seems
639     // like a reasonable precedent.
640     //
641     // Another option would be to use openlog() and syslog().  However
642     // calling openlog() may inadvertently affect the behavior of other parts
643     // of the program also using syslog().
644     //
645     // We don't check for write errors here, since there's not much else we can
646     // do if it fails.
647     auto fullMsg = folly::to<std::string>(
648         "logging warning:", filename, ":", lineNumber, ": ", msg, "\n");
649     folly::writeFull(STDERR_FILENO, fullMsg.data(), fullMsg.size());
650   }
651 }
652 } // namespace folly