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>
18 #include <folly/String.h>
19 #include <folly/experimental/logging/LogCategory.h>
20 #include <folly/experimental/logging/LogLevel.h>
21 #include <folly/experimental/logging/Logger.h>
26 class LoggerDBSingleton {
28 explicit LoggerDBSingleton(LoggerDB* db) : db_{db} {}
29 ~LoggerDBSingleton() {
30 // We intentionally leak the LoggerDB object on destruction.
31 // We want Logger objects to remain valid for the entire lifetime of the
32 // program, without having to worry about destruction ordering issues, or
33 // making the Logger perform reference counting on the LoggerDB.
35 // Therefore the main LoggerDB object, and all of the LogCategory objects
36 // it contains, are always intentionally leaked.
38 // However, we do call db_->cleanupHandlers() to destroy any registered
39 // LogHandler objects. The LogHandlers can be user-defined objects and may
40 // hold resources that should be cleaned up.
41 db_->cleanupHandlers();
44 LoggerDB* getDB() const {
53 LoggerDB* LoggerDB::get() {
54 // Intentionally leaky singleton
55 static LoggerDBSingleton singleton{new LoggerDB()};
56 return singleton.getDB();
59 LoggerDB::LoggerDB() {
60 // Create the root log category, and set the level to ERROR by default
61 auto rootUptr = std::make_unique<LogCategory>(this);
62 LogCategory* root = rootUptr.get();
64 loggersByName_.wlock()->emplace(root->getName(), std::move(rootUptr));
67 root->setLevelLocked(LogLevel::ERROR, false);
70 LoggerDB::LoggerDB(TestConstructorArg) : LoggerDB() {}
72 LogCategory* LoggerDB::getCategory(StringPiece name) {
73 return getOrCreateCategoryLocked(*loggersByName_.wlock(), name);
76 LogCategory* FOLLY_NULLABLE LoggerDB::getCategoryOrNull(StringPiece name) {
77 auto loggersByName = loggersByName_.rlock();
79 auto it = loggersByName->find(name);
80 if (it == loggersByName->end()) {
83 return it->second.get();
86 void LoggerDB::setLevel(folly::StringPiece name, LogLevel level, bool inherit) {
87 auto loggersByName = loggersByName_.wlock();
88 LogCategory* category = getOrCreateCategoryLocked(*loggersByName, name);
89 category->setLevelLocked(level, inherit);
92 void LoggerDB::setLevel(LogCategory* category, LogLevel level, bool inherit) {
93 auto loggersByName = loggersByName_.wlock();
94 category->setLevelLocked(level, inherit);
97 std::vector<std::string> LoggerDB::processConfigString(
98 folly::StringPiece config) {
99 std::vector<std::string> errors;
100 if (config.empty()) {
104 std::vector<StringPiece> pieces;
105 folly::split(",", config, pieces);
106 for (const auto& p : pieces) {
107 auto idx = p.rfind('=');
108 if (idx == folly::StringPiece::npos) {
110 folly::sformat("missing '=' in logger configuration: \"{}\"", p));
114 auto category = p.subpiece(0, idx);
115 auto level_str = p.subpiece(idx + 1);
118 level = stringToLogLevel(level_str);
119 } catch (const std::exception& ex) {
120 errors.emplace_back(folly::sformat(
121 "invalid log level \"{}\" for category \"{}\"", level_str, category));
125 setLevel(category, level);
131 LogCategory* LoggerDB::getOrCreateCategoryLocked(
132 LoggerNameMap& loggersByName,
134 auto it = loggersByName.find(name);
135 if (it != loggersByName.end()) {
136 return it->second.get();
139 StringPiece parentName = LogName::getParent(name);
140 LogCategory* parent = getOrCreateCategoryLocked(loggersByName, parentName);
141 return createCategoryLocked(loggersByName, name, parent);
144 LogCategory* LoggerDB::createCategoryLocked(
145 LoggerNameMap& loggersByName,
147 LogCategory* parent) {
148 auto uptr = std::make_unique<LogCategory>(name, parent);
149 LogCategory* logger = uptr.get();
150 auto ret = loggersByName.emplace(logger->getName(), std::move(uptr));
155 void LoggerDB::cleanupHandlers() {
156 // Get a copy of all categories, so we can call clearHandlers() without
157 // holding the loggersByName_ lock. We don't need to worry about LogCategory
158 // lifetime, since LogCategory objects always live for the lifetime of the
160 std::vector<LogCategory*> categories;
162 auto loggersByName = loggersByName_.wlock();
163 categories.reserve(loggersByName->size());
164 for (const auto& entry : *loggersByName) {
165 categories.push_back(entry.second.get());
169 for (auto* category : categories) {
170 category->clearHandlers();
174 LogLevel LoggerDB::xlogInit(
175 StringPiece categoryName,
176 std::atomic<LogLevel>* xlogCategoryLevel,
177 LogCategory** xlogCategory) {
178 // Hold the lock for the duration of the operation
179 // xlogInit() may be called from multiple threads simultaneously.
180 // Only one needs to perform the initialization.
181 auto loggersByName = loggersByName_.wlock();
182 if (xlogCategory != nullptr && *xlogCategory != nullptr) {
183 // The xlogCategory was already initialized before we acquired the lock
184 return (*xlogCategory)->getEffectiveLevel();
187 auto* category = getOrCreateCategoryLocked(*loggersByName, categoryName);
189 // Set *xlogCategory before we update xlogCategoryLevel below.
190 // This is important, since the XLOG() macros check xlogCategoryLevel to
191 // tell if *xlogCategory has been initialized yet.
192 *xlogCategory = category;
194 auto level = category->getEffectiveLevel();
195 xlogCategoryLevel->store(level, std::memory_order_release);
196 category->registerXlogLevel(xlogCategoryLevel);
200 LogCategory* LoggerDB::xlogInitCategory(
201 StringPiece categoryName,
202 LogCategory** xlogCategory,
203 std::atomic<bool>* isInitialized) {
204 // Hold the lock for the duration of the operation
205 // xlogInitCategory() may be called from multiple threads simultaneously.
206 // Only one needs to perform the initialization.
207 auto loggersByName = loggersByName_.wlock();
208 if (isInitialized->load(std::memory_order_acquire)) {
209 // The xlogCategory was already initialized before we acquired the lock
210 return *xlogCategory;
213 auto* category = getOrCreateCategoryLocked(*loggersByName, categoryName);
214 *xlogCategory = category;
215 isInitialized->store(true, std::memory_order_release);