2 * Copyright 2004-present Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <folly/experimental/logging/LoggerDB.h>
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>
36 class LoggerDBSingleton {
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.
45 // Therefore the main LoggerDB object, and all of the LogCategory objects
46 // it contains, are always intentionally leaked.
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();
55 LoggerDB* getDB() const {
64 LoggerDB* LoggerDB::get() {
65 // Intentionally leaky singleton
66 static LoggerDBSingleton singleton{new LoggerDB()};
67 return singleton.getDB();
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();
75 loggersByName_.wlock()->emplace(root->getName(), std::move(rootUptr));
78 root->setLevelLocked(LogLevel::ERR, false);
81 LoggerDB::LoggerDB(TestConstructorArg) : LoggerDB() {}
83 LoggerDB::~LoggerDB() {}
85 LogCategory* LoggerDB::getCategory(StringPiece name) {
86 return getOrCreateCategoryLocked(*loggersByName_.wlock(), name);
89 LogCategory* FOLLY_NULLABLE LoggerDB::getCategoryOrNull(StringPiece name) {
90 auto loggersByName = loggersByName_.rlock();
92 auto it = loggersByName->find(name);
93 if (it == loggersByName->end()) {
96 return it->second.get();
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);
105 void LoggerDB::setLevel(LogCategory* category, LogLevel level, bool inherit) {
106 auto loggersByName = loggersByName_.wlock();
107 category->setLevelLocked(level, inherit);
110 LogConfig LoggerDB::getConfig() const {
111 auto handlerInfo = handlerInfo_.rlock();
113 LogConfig::HandlerConfigMap handlerConfigs;
114 std::unordered_map<std::shared_ptr<LogHandler>, string> handlersToName;
115 for (const auto& entry : handlerInfo->handlers) {
116 auto handler = entry.second.lock();
120 handlersToName.emplace(handler, entry.first);
121 handlerConfigs.emplace(entry.first, handler->getConfig());
124 size_t anonymousNameIndex = 1;
125 auto generateAnonymousHandlerName = [&]() {
126 // Return a unique name of the form "anonymousHandlerN"
127 // Keep incrementing N until we find a name that isn't currently taken.
129 auto name = to<string>("anonymousHandler", anonymousNameIndex);
130 ++anonymousNameIndex;
131 if (handlerInfo->handlers.find(name) == handlerInfo->handlers.end()) {
137 LogConfig::CategoryConfigMap categoryConfigs;
139 auto loggersByName = loggersByName_.rlock();
140 for (const auto& entry : *loggersByName) {
141 auto* category = entry.second.get();
142 auto levelInfo = category->getLevelInfo();
143 auto handlers = category->getHandlers();
145 // Don't report categories that have default settings.
146 if (handlers.empty() && levelInfo.first == LogLevel::MAX_LEVEL &&
151 // Translate the handler pointers to names
152 std::vector<string> handlerNames;
153 for (const auto& handler : handlers) {
154 auto iter = handlersToName.find(handler);
155 if (iter == handlersToName.end()) {
156 // This LogHandler must have been manually attached to the category,
157 // rather than defined with `updateConfig()` or `resetConfig()`.
158 // Generate a unique name to use for reporting it in the config.
159 auto name = generateAnonymousHandlerName();
160 handlersToName.emplace(handler, name);
161 handlerConfigs.emplace(name, handler->getConfig());
162 handlerNames.emplace_back(name);
164 handlerNames.emplace_back(iter->second);
168 LogCategoryConfig categoryConfig(
169 levelInfo.first, levelInfo.second, handlerNames);
170 categoryConfigs.emplace(category->getName(), std::move(categoryConfig));
174 return LogConfig{std::move(handlerConfigs), std::move(categoryConfigs)};
178 * Process handler config information when starting a config update operation.
180 void LoggerDB::startConfigUpdate(
181 const Synchronized<HandlerInfo>::LockedPtr& handlerInfo,
182 const LogConfig& config,
183 NewHandlerMap* handlers,
184 OldToNewHandlerMap* oldToNewHandlerMap) {
185 // Get a map of all currently existing LogHandlers.
186 // This resolves weak_ptrs to shared_ptrs, and ignores expired weak_ptrs.
187 // This prevents any of these LogHandler pointers from expiring during the
189 for (const auto& entry : handlerInfo->handlers) {
190 auto handler = entry.second.lock();
192 handlers->emplace(entry.first, std::move(handler));
196 // Create all of the new LogHandlers needed from this configuration
197 for (const auto& entry : config.getHandlerConfigs()) {
198 // Check to see if there is an existing LogHandler with this name
199 std::shared_ptr<LogHandler> oldHandler;
200 auto iter = handlers->find(entry.first);
201 if (iter != handlers->end()) {
202 oldHandler = iter->second;
205 LogHandlerConfig updatedConfig;
206 const LogHandlerConfig* handlerConfig;
207 if (entry.second.type.hasValue()) {
208 handlerConfig = &entry.second;
210 // This configuration is intended to update an existing LogHandler
212 throw std::invalid_argument(to<std::string>(
213 "cannot update unknown log handler \"", entry.first, "\""));
216 updatedConfig = oldHandler->getConfig();
217 if (!updatedConfig.type.hasValue()) {
218 // This normally should not happen unless someone improperly manually
219 // constructed a LogHandler object. All existing LogHandler objects
220 // should indicate their type.
221 throw std::invalid_argument(to<std::string>(
222 "existing log handler \"",
224 "\" is missing type information"));
226 updatedConfig.update(entry.second);
227 handlerConfig = &updatedConfig;
230 // Look up the LogHandlerFactory
231 auto factoryIter = handlerInfo->factories.find(handlerConfig->type.value());
232 if (factoryIter == handlerInfo->factories.end()) {
233 throw std::invalid_argument(to<std::string>(
234 "unknown log handler type \"", handlerConfig->type.value(), "\""));
237 // Create the new log handler
238 const auto& factory = factoryIter->second;
239 std::shared_ptr<LogHandler> handler;
242 handler = factory->updateHandler(oldHandler, handlerConfig->options);
243 if (handler != oldHandler) {
244 oldToNewHandlerMap->emplace(oldHandler, handler);
247 handler = factory->createHandler(handlerConfig->options);
249 } catch (const std::exception& ex) {
250 // Errors creating or updating the the log handler are generally due to
251 // bad configuration options. It is useful to update the exception
252 // message to include the name of the log handler we were trying to
254 throw std::invalid_argument(to<string>(
256 oldHandler ? "updating" : "creating",
262 handlerInfo->handlers[entry.first] = handler;
263 (*handlers)[entry.first] = handler;
266 // Before we start making any LogCategory changes, confirm that all handlers
267 // named in the category configs are known handlers.
268 for (const auto& entry : config.getCategoryConfigs()) {
269 if (!entry.second.handlers.hasValue()) {
272 for (const auto& handlerName : entry.second.handlers.value()) {
273 auto iter = handlers->find(handlerName);
274 if (iter == handlers->end()) {
275 throw std::invalid_argument(to<std::string>(
276 "unknown log handler \"",
278 "\" configured for log category \"",
287 * Update handlerInfo_ at the end of a config update operation.
289 void LoggerDB::finishConfigUpdate(
290 const Synchronized<HandlerInfo>::LockedPtr& handlerInfo,
291 NewHandlerMap* handlers,
292 OldToNewHandlerMap* oldToNewHandlerMap) {
293 // Build a new map to replace handlerInfo->handlers
294 // This will contain only the LogHandlers that are still in use by the
295 // current LogCategory settings.
296 std::unordered_map<std::string, std::weak_ptr<LogHandler>> newHandlerMap;
297 for (const auto& entry : *handlers) {
298 newHandlerMap.emplace(entry.first, entry.second);
300 // Drop all of our shared_ptr references to LogHandler objects,
301 // and then remove entries in newHandlerMap that are unreferenced.
303 oldToNewHandlerMap->clear();
304 handlerInfo->handlers.clear();
305 for (auto iter = newHandlerMap.begin(); iter != newHandlerMap.end(); /**/) {
306 if (iter->second.expired()) {
307 iter = newHandlerMap.erase(iter);
312 handlerInfo->handlers.swap(newHandlerMap);
315 std::vector<std::shared_ptr<LogHandler>> LoggerDB::buildCategoryHandlerList(
316 const NewHandlerMap& handlerMap,
317 StringPiece categoryName,
318 const std::vector<std::string>& categoryHandlerNames) {
319 std::vector<std::shared_ptr<LogHandler>> catHandlers;
320 for (const auto& handlerName : categoryHandlerNames) {
321 auto iter = handlerMap.find(handlerName);
322 if (iter == handlerMap.end()) {
323 // This really shouldn't be possible; the checks in startConfigUpdate()
324 // should have already bailed out if there was an unknown handler.
325 throw std::invalid_argument(to<std::string>(
326 "bug: unknown log handler \"",
328 "\" configured for log category \"",
332 catHandlers.push_back(iter->second);
338 void LoggerDB::updateConfig(const LogConfig& config) {
339 // Grab the handlerInfo_ lock.
340 // We hold it in write mode for the entire config update operation. This
341 // ensures that only a single config update operation ever runs at once.
342 auto handlerInfo = handlerInfo_.wlock();
344 NewHandlerMap handlers;
345 OldToNewHandlerMap oldToNewHandlerMap;
346 startConfigUpdate(handlerInfo, config, &handlers, &oldToNewHandlerMap);
348 // If an existing LogHandler was replaced with a new one,
349 // walk all current LogCategories and replace this handler.
350 if (!oldToNewHandlerMap.empty()) {
351 auto loggerMap = loggersByName_.rlock();
352 for (const auto& entry : *loggerMap) {
353 entry.second->updateHandlers(oldToNewHandlerMap);
357 // Update log levels and handlers mentioned in the config update
358 auto loggersByName = loggersByName_.wlock();
359 for (const auto& entry : config.getCategoryConfigs()) {
360 LogCategory* category =
361 getOrCreateCategoryLocked(*loggersByName, entry.first);
363 // Update the log handlers
364 if (entry.second.handlers.hasValue()) {
365 auto catHandlers = buildCategoryHandlerList(
366 handlers, entry.first, entry.second.handlers.value());
367 category->replaceHandlers(std::move(catHandlers));
370 // Update the level settings
371 category->setLevelLocked(
372 entry.second.level, entry.second.inheritParentLevel);
375 finishConfigUpdate(handlerInfo, &handlers, &oldToNewHandlerMap);
378 void LoggerDB::resetConfig(const LogConfig& config) {
379 // Grab the handlerInfo_ lock.
380 // We hold it in write mode for the entire config update operation. This
381 // ensures that only a single config update operation ever runs at once.
382 auto handlerInfo = handlerInfo_.wlock();
384 NewHandlerMap handlers;
385 OldToNewHandlerMap oldToNewHandlerMap;
386 startConfigUpdate(handlerInfo, config, &handlers, &oldToNewHandlerMap);
388 // Make sure all log categories mentioned in the new config exist.
389 // This ensures that we will cover them in our walk below.
390 LogCategory* rootCategory;
392 auto loggersByName = loggersByName_.wlock();
393 rootCategory = getOrCreateCategoryLocked(*loggersByName, "");
394 for (const auto& entry : config.getCategoryConfigs()) {
395 getOrCreateCategoryLocked(*loggersByName, entry.first);
400 // Update all log categories
401 auto loggersByName = loggersByName_.rlock();
402 for (const auto& entry : *loggersByName) {
403 auto* category = entry.second.get();
405 auto configIter = config.getCategoryConfigs().find(category->getName());
406 if (configIter == config.getCategoryConfigs().end()) {
407 // This category is not listed in the config settings.
408 // Reset it to the default settings.
409 category->clearHandlers();
411 if (category == rootCategory) {
412 category->setLevelLocked(LogLevel::ERR, false);
414 category->setLevelLocked(LogLevel::MAX_LEVEL, true);
419 const auto& catConfig = configIter->second;
421 // Update the category log level
422 category->setLevelLocked(catConfig.level, catConfig.inheritParentLevel);
424 // Update the category handlers list.
425 // If the handler list is not set in the config, clear out any existing
426 // handlers rather than leaving it as-is.
427 std::vector<std::shared_ptr<LogHandler>> catHandlers;
428 if (catConfig.handlers.hasValue()) {
429 catHandlers = buildCategoryHandlerList(
430 handlers, entry.first, catConfig.handlers.value());
432 category->replaceHandlers(std::move(catHandlers));
436 finishConfigUpdate(handlerInfo, &handlers, &oldToNewHandlerMap);
439 LogCategory* LoggerDB::getOrCreateCategoryLocked(
440 LoggerNameMap& loggersByName,
442 auto it = loggersByName.find(name);
443 if (it != loggersByName.end()) {
444 return it->second.get();
447 StringPiece parentName = LogName::getParent(name);
448 LogCategory* parent = getOrCreateCategoryLocked(loggersByName, parentName);
449 return createCategoryLocked(loggersByName, name, parent);
452 LogCategory* LoggerDB::createCategoryLocked(
453 LoggerNameMap& loggersByName,
455 LogCategory* parent) {
456 auto uptr = std::make_unique<LogCategory>(name, parent);
457 LogCategory* logger = uptr.get();
458 auto ret = loggersByName.emplace(logger->getName(), std::move(uptr));
463 void LoggerDB::cleanupHandlers() {
464 // Get a copy of all categories, so we can call clearHandlers() without
465 // holding the loggersByName_ lock. We don't need to worry about LogCategory
466 // lifetime, since LogCategory objects always live for the lifetime of the
468 std::vector<LogCategory*> categories;
470 auto loggersByName = loggersByName_.wlock();
471 categories.reserve(loggersByName->size());
472 for (const auto& entry : *loggersByName) {
473 categories.push_back(entry.second.get());
477 // Also extract our HandlerFactoryMap and HandlerMap, so we can clear them
478 // later without holding the handlerInfo_ lock.
479 HandlerFactoryMap factories;
482 auto handlerInfo = handlerInfo_.wlock();
483 factories.swap(handlerInfo->factories);
484 handlers.swap(handlerInfo->handlers);
487 // Remove all of the LogHandlers from all log categories,
488 // to drop any shared_ptr references to the LogHandlers
489 for (auto* category : categories) {
490 category->clearHandlers();
494 size_t LoggerDB::flushAllHandlers() {
495 // Build a set of all LogHandlers. We use a set to avoid calling flush()
496 // more than once on the same handler if it is registered on multiple
497 // different categories.
498 std::set<std::shared_ptr<LogHandler>> handlers;
500 auto loggersByName = loggersByName_.wlock();
501 for (const auto& entry : *loggersByName) {
502 for (const auto& handler : entry.second->getHandlers()) {
503 handlers.emplace(handler);
508 // Call flush() on each handler
509 for (const auto& handler : handlers) {
512 return handlers.size();
515 void LoggerDB::registerHandlerFactory(
516 std::unique_ptr<LogHandlerFactory> factory,
517 bool replaceExisting) {
518 auto type = factory->getType();
519 auto handlerInfo = handlerInfo_.wlock();
520 if (replaceExisting) {
521 handlerInfo->factories[type.str()] = std::move(factory);
523 auto ret = handlerInfo->factories.emplace(type.str(), std::move(factory));
525 throw std::range_error(to<std::string>(
526 "a LogHandlerFactory for the type \"", type, "\" already exists"));
531 void LoggerDB::unregisterHandlerFactory(StringPiece type) {
532 auto handlerInfo = handlerInfo_.wlock();
533 auto numRemoved = handlerInfo->factories.erase(type.str());
534 if (numRemoved != 1) {
535 throw std::range_error(
536 to<std::string>("no LogHandlerFactory for type \"", type, "\" found"));
540 LogLevel LoggerDB::xlogInit(
541 StringPiece categoryName,
542 std::atomic<LogLevel>* xlogCategoryLevel,
543 LogCategory** xlogCategory) {
544 // Hold the lock for the duration of the operation
545 // xlogInit() may be called from multiple threads simultaneously.
546 // Only one needs to perform the initialization.
547 auto loggersByName = loggersByName_.wlock();
548 if (xlogCategory != nullptr && *xlogCategory != nullptr) {
549 // The xlogCategory was already initialized before we acquired the lock
550 return (*xlogCategory)->getEffectiveLevel();
553 auto* category = getOrCreateCategoryLocked(*loggersByName, categoryName);
555 // Set *xlogCategory before we update xlogCategoryLevel below.
556 // This is important, since the XLOG() macros check xlogCategoryLevel to
557 // tell if *xlogCategory has been initialized yet.
558 *xlogCategory = category;
560 auto level = category->getEffectiveLevel();
561 xlogCategoryLevel->store(level, std::memory_order_release);
562 category->registerXlogLevel(xlogCategoryLevel);
566 LogCategory* LoggerDB::xlogInitCategory(
567 StringPiece categoryName,
568 LogCategory** xlogCategory,
569 std::atomic<bool>* isInitialized) {
570 // Hold the lock for the duration of the operation
571 // xlogInitCategory() may be called from multiple threads simultaneously.
572 // Only one needs to perform the initialization.
573 auto loggersByName = loggersByName_.wlock();
574 if (isInitialized->load(std::memory_order_acquire)) {
575 // The xlogCategory was already initialized before we acquired the lock
576 return *xlogCategory;
579 auto* category = getOrCreateCategoryLocked(*loggersByName, categoryName);
580 *xlogCategory = category;
581 isInitialized->store(true, std::memory_order_release);
585 std::atomic<LoggerDB::InternalWarningHandler> LoggerDB::warningHandler_;
587 void LoggerDB::internalWarningImpl(
588 folly::StringPiece filename,
590 std::string&& msg) noexcept {
591 auto handler = warningHandler_.load();
593 handler(filename, lineNumber, std::move(msg));
595 defaultInternalWarningImpl(filename, lineNumber, std::move(msg));
599 void LoggerDB::setInternalWarningHandler(InternalWarningHandler handler) {
600 // This API is intentionally pretty basic. It has a number of limitations:
602 // - We only support plain function pointers, and not full std::function
603 // objects. This makes it possible to use std::atomic to access the
604 // handler pointer, and also makes it safe to store in a zero-initialized
605 // file-static pointer.
607 // - We don't support any void* argument to the handler. The caller is
608 // responsible for storing any callback state themselves.
610 // - When replacing or unsetting a handler we don't make any guarantees about
611 // when the old handler will stop being called. It may still be called
612 // from other threads briefly even after setInternalWarningHandler()
613 // returns. This is also a consequence of using std::atomic rather than a
616 // This provides the minimum capabilities needed to customize the handler,
617 // while still keeping the implementation simple and safe to use even before
619 warningHandler_.store(handler);
622 void LoggerDB::defaultInternalWarningImpl(
623 folly::StringPiece filename,
625 std::string&& msg) noexcept {
626 // Rate limit to 10 messages every 5 seconds.
628 // We intentonally use a leaky Meyer's singleton here over folly::Singleton:
629 // - We want this code to work even before main()
630 // - This singleton does not depend on any other singletons.
631 static auto* rateLimiter =
632 new logging::IntervalRateLimiter{10, std::chrono::seconds(5)};
633 if (!rateLimiter->check()) {
637 if (folly::kIsDebug) {
638 // Write directly to file descriptor 2.
640 // It's possible the application has closed fd 2 and is using it for
641 // something other than stderr. However we have no good way to detect
642 // this, which is the main reason we only write to stderr in debug build
643 // modes. assert() also writes directly to stderr on failure, which seems
644 // like a reasonable precedent.
646 // Another option would be to use openlog() and syslog(). However
647 // calling openlog() may inadvertently affect the behavior of other parts
648 // of the program also using syslog().
650 // We don't check for write errors here, since there's not much else we can
652 auto fullMsg = folly::to<std::string>(
653 "logging warning:", filename, ":", lineNumber, ": ", msg, "\n");
654 folly::writeFull(STDERR_FILENO, fullMsg.data(), fullMsg.size());