953570b485c21ed5db10a5c213ca871193c1f2d7
[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 <folly/String.h>
19 #include <folly/experimental/logging/LogCategory.h>
20 #include <folly/experimental/logging/LogLevel.h>
21 #include <folly/experimental/logging/Logger.h>
22
23 namespace folly {
24
25 namespace {
26 class LoggerDBSingleton {
27  public:
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.
34     //
35     // Therefore the main LoggerDB object, and all of the LogCategory objects
36     // it contains, are always intentionally leaked.
37     //
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();
42   }
43
44   LoggerDB* getDB() const {
45     return db_;
46   }
47
48  private:
49   LoggerDB* db_;
50 };
51 }
52
53 LoggerDB* LoggerDB::get() {
54   // Intentionally leaky singleton
55   static LoggerDBSingleton singleton{new LoggerDB()};
56   return singleton.getDB();
57 }
58
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();
63   auto ret =
64       loggersByName_.wlock()->emplace(root->getName(), std::move(rootUptr));
65   DCHECK(ret.second);
66
67   root->setLevelLocked(LogLevel::ERROR, false);
68 }
69
70 LoggerDB::LoggerDB(TestConstructorArg) : LoggerDB() {}
71
72 LogCategory* LoggerDB::getCategory(StringPiece name) {
73   return getOrCreateCategoryLocked(*loggersByName_.wlock(), name);
74 }
75
76 LogCategory* FOLLY_NULLABLE LoggerDB::getCategoryOrNull(StringPiece name) {
77   auto loggersByName = loggersByName_.rlock();
78
79   auto it = loggersByName->find(name);
80   if (it == loggersByName->end()) {
81     return nullptr;
82   }
83   return it->second.get();
84 }
85
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);
90 }
91
92 void LoggerDB::setLevel(LogCategory* category, LogLevel level, bool inherit) {
93   auto loggersByName = loggersByName_.wlock();
94   category->setLevelLocked(level, inherit);
95 }
96
97 std::vector<std::string> LoggerDB::processConfigString(
98     folly::StringPiece config) {
99   std::vector<std::string> errors;
100   if (config.empty()) {
101     return errors;
102   }
103
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) {
109       errors.emplace_back(
110           folly::sformat("missing '=' in logger configuration: \"{}\"", p));
111       continue;
112     }
113
114     auto category = p.subpiece(0, idx);
115     auto level_str = p.subpiece(idx + 1);
116     LogLevel level;
117     try {
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));
122       continue;
123     }
124
125     setLevel(category, level);
126   }
127
128   return errors;
129 }
130
131 LogCategory* LoggerDB::getOrCreateCategoryLocked(
132     LoggerNameMap& loggersByName,
133     StringPiece name) {
134   auto it = loggersByName.find(name);
135   if (it != loggersByName.end()) {
136     return it->second.get();
137   }
138
139   StringPiece parentName = LogName::getParent(name);
140   LogCategory* parent = getOrCreateCategoryLocked(loggersByName, parentName);
141   return createCategoryLocked(loggersByName, name, parent);
142 }
143
144 LogCategory* LoggerDB::createCategoryLocked(
145     LoggerNameMap& loggersByName,
146     StringPiece name,
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));
151   DCHECK(ret.second);
152   return logger;
153 }
154
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
159   // LoggerDB.
160   std::vector<LogCategory*> categories;
161   {
162     auto loggersByName = loggersByName_.wlock();
163     categories.reserve(loggersByName->size());
164     for (const auto& entry : *loggersByName) {
165       categories.push_back(entry.second.get());
166     }
167   }
168
169   for (auto* category : categories) {
170     category->clearHandlers();
171   }
172 }
173
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();
185   }
186
187   auto* category = getOrCreateCategoryLocked(*loggersByName, categoryName);
188   if (xlogCategory) {
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;
193   }
194   auto level = category->getEffectiveLevel();
195   xlogCategoryLevel->store(level, std::memory_order_release);
196   category->registerXlogLevel(xlogCategoryLevel);
197   return level;
198 }
199
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;
211   }
212
213   auto* category = getOrCreateCategoryLocked(*loggersByName, categoryName);
214   *xlogCategory = category;
215   isInitialized->store(true, std::memory_order_release);
216   return category;
217 }
218 }