+ for (const auto& handlerName : entry.second.handlers.value()) {
+ auto iter = handlers->find(handlerName);
+ if (iter == handlers->end()) {
+ throw std::invalid_argument(to<std::string>(
+ "unknown log handler \"",
+ handlerName,
+ "\" configured for log category \"",
+ entry.first,
+ "\""));
+ }
+ }
+ }
+}
+
+/**
+ * Update handlerInfo_ at the end of a config update operation.
+ */
+void LoggerDB::finishConfigUpdate(
+ const Synchronized<HandlerInfo>::LockedPtr& handlerInfo,
+ NewHandlerMap* handlers,
+ OldToNewHandlerMap* oldToNewHandlerMap) {
+ // Build a new map to replace handlerInfo->handlers
+ // This will contain only the LogHandlers that are still in use by the
+ // current LogCategory settings.
+ std::unordered_map<std::string, std::weak_ptr<LogHandler>> newHandlerMap;
+ for (const auto& entry : *handlers) {
+ newHandlerMap.emplace(entry.first, entry.second);
+ }
+ // Drop all of our shared_ptr references to LogHandler objects,
+ // and then remove entries in newHandlerMap that are unreferenced.
+ handlers->clear();
+ oldToNewHandlerMap->clear();
+ handlerInfo->handlers.clear();
+ for (auto iter = newHandlerMap.begin(); iter != newHandlerMap.end(); /**/) {
+ if (iter->second.expired()) {
+ iter = newHandlerMap.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+ handlerInfo->handlers.swap(newHandlerMap);
+}
+
+std::vector<std::shared_ptr<LogHandler>> LoggerDB::buildCategoryHandlerList(
+ const NewHandlerMap& handlerMap,
+ StringPiece categoryName,
+ const std::vector<std::string>& categoryHandlerNames) {
+ std::vector<std::shared_ptr<LogHandler>> catHandlers;
+ for (const auto& handlerName : categoryHandlerNames) {
+ auto iter = handlerMap.find(handlerName);
+ if (iter == handlerMap.end()) {
+ // This really shouldn't be possible; the checks in startConfigUpdate()
+ // should have already bailed out if there was an unknown handler.
+ throw std::invalid_argument(to<std::string>(
+ "bug: unknown log handler \"",
+ handlerName,
+ "\" configured for log category \"",
+ categoryName,
+ "\""));
+ }
+ catHandlers.push_back(iter->second);
+ }
+
+ return catHandlers;
+}
+
+void LoggerDB::updateConfig(const LogConfig& config) {
+ // Grab the handlerInfo_ lock.
+ // We hold it in write mode for the entire config update operation. This
+ // ensures that only a single config update operation ever runs at once.
+ auto handlerInfo = handlerInfo_.wlock();
+
+ NewHandlerMap handlers;
+ OldToNewHandlerMap oldToNewHandlerMap;
+ startConfigUpdate(handlerInfo, config, &handlers, &oldToNewHandlerMap);
+
+ // If an existing LogHandler was replaced with a new one,
+ // walk all current LogCategories and replace this handler.
+ if (!oldToNewHandlerMap.empty()) {
+ auto loggerMap = loggersByName_.rlock();
+ for (const auto& entry : *loggerMap) {
+ entry.second->updateHandlers(oldToNewHandlerMap);
+ }
+ }
+
+ // Update log levels and handlers mentioned in the config update
+ auto loggersByName = loggersByName_.wlock();
+ for (const auto& entry : config.getCategoryConfigs()) {
+ LogCategory* category =
+ getOrCreateCategoryLocked(*loggersByName, entry.first);
+
+ // Update the log handlers
+ if (entry.second.handlers.hasValue()) {
+ auto catHandlers = buildCategoryHandlerList(
+ handlers, entry.first, entry.second.handlers.value());
+ category->replaceHandlers(std::move(catHandlers));
+ }