logging: rename the `DEBUG` log level to `DBG`
[folly.git] / folly / experimental / logging / LogCategory.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/LogCategory.h>
17
18 #include <cstdio>
19 #include <cstdlib>
20
21 #include <folly/ConstexprMath.h>
22 #include <folly/ExceptionString.h>
23 #include <folly/FileUtil.h>
24 #include <folly/MapUtil.h>
25 #include <folly/experimental/logging/LogHandler.h>
26 #include <folly/experimental/logging/LogMessage.h>
27 #include <folly/experimental/logging/LogName.h>
28 #include <folly/experimental/logging/LoggerDB.h>
29
30 namespace folly {
31
32 LogCategory::LogCategory(LoggerDB* db)
33     : effectiveLevel_{LogLevel::ERR},
34       level_{static_cast<uint32_t>(LogLevel::ERR)},
35       parent_{nullptr},
36       name_{},
37       db_{db} {}
38
39 LogCategory::LogCategory(StringPiece name, LogCategory* parent)
40     : effectiveLevel_{parent->getEffectiveLevel()},
41       level_{static_cast<uint32_t>(LogLevel::MAX_LEVEL) | FLAG_INHERIT},
42       parent_{parent},
43       name_{LogName::canonicalize(name)},
44       db_{parent->getDB()},
45       nextSibling_{parent_->firstChild_} {
46   parent_->firstChild_ = this;
47 }
48
49 void LogCategory::admitMessage(const LogMessage& message) const {
50   processMessage(message);
51
52   // If this is a fatal message, flush the handlers to make sure the log
53   // message was written out, then crash.
54   if (isLogLevelFatal(message.getLevel())) {
55     auto numHandlers = db_->flushAllHandlers();
56     if (numHandlers == 0) {
57       // No log handlers were configured.
58       // Print the message to stderr, to make sure we always print the reason
59       // we are crashing somewhere.
60       auto msg = folly::to<std::string>(
61           "FATAL:",
62           message.getFileName(),
63           ":",
64           message.getLineNumber(),
65           ": ",
66           message.getMessage(),
67           "\n");
68       folly::writeFull(STDERR_FILENO, msg.data(), msg.size());
69     }
70     std::abort();
71   }
72 }
73
74 void LogCategory::processMessage(const LogMessage& message) const {
75   // Make a copy of any attached LogHandlers, so we can release the handlers_
76   // lock before holding them.
77   //
78   // In the common case there will only be a small number of handlers.  Use a
79   // std::array in this case to avoid a heap allocation for the vector.
80   const std::shared_ptr<LogHandler>* handlers = nullptr;
81   size_t numHandlers = 0;
82   constexpr uint32_t kSmallOptimizationSize = 5;
83   std::array<std::shared_ptr<LogHandler>, kSmallOptimizationSize> handlersArray;
84   std::vector<std::shared_ptr<LogHandler>> handlersVector;
85   {
86     auto lockedHandlers = handlers_.rlock();
87     numHandlers = lockedHandlers->size();
88     if (numHandlers <= kSmallOptimizationSize) {
89       for (size_t n = 0; n < numHandlers; ++n) {
90         handlersArray[n] = (*lockedHandlers)[n];
91       }
92       handlers = handlersArray.data();
93     } else {
94       handlersVector = *lockedHandlers;
95       handlers = handlersVector.data();
96     }
97   }
98
99   for (size_t n = 0; n < numHandlers; ++n) {
100     try {
101       handlers[n]->handleMessage(message, this);
102     } catch (const std::exception& ex) {
103       // Use LoggerDB::internalWarning() to report the error, but continue
104       // trying to log the message to any other handlers attached to ourself or
105       // one of our parent categories.
106       LoggerDB::internalWarning(
107           __FILE__,
108           __LINE__,
109           "log handler for category \"",
110           name_,
111           "\" threw an error: ",
112           folly::exceptionStr(ex));
113     }
114   }
115
116   // Propagate the message up to our parent LogCategory.
117   //
118   // Maybe in the future it might be worth adding a flag to control if a
119   // LogCategory should propagate messages to its parent or not.  (This would
120   // be similar to log4j's "additivity" flag.)
121   // For now I don't have a strong use case for this.
122   if (parent_) {
123     parent_->processMessage(message);
124   }
125 }
126
127 void LogCategory::addHandler(std::shared_ptr<LogHandler> handler) {
128   auto handlers = handlers_.wlock();
129   handlers->emplace_back(std::move(handler));
130 }
131
132 void LogCategory::clearHandlers() {
133   std::vector<std::shared_ptr<LogHandler>> emptyHandlersList;
134   // Swap out the handlers list with the handlers_ lock held.
135   {
136     auto handlers = handlers_.wlock();
137     handlers->swap(emptyHandlersList);
138   }
139   // Destroy emptyHandlersList now that the handlers_ lock is released.
140   // This way we don't hold the handlers_ lock while invoking any of the
141   // LogHandler destructors.
142 }
143
144 std::vector<std::shared_ptr<LogHandler>> LogCategory::getHandlers() const {
145   return *(handlers_.rlock());
146 }
147
148 void LogCategory::replaceHandlers(
149     std::vector<std::shared_ptr<LogHandler>> handlers) {
150   return handlers_.wlock()->swap(handlers);
151 }
152
153 void LogCategory::updateHandlers(const std::unordered_map<
154                                  std::shared_ptr<LogHandler>,
155                                  std::shared_ptr<LogHandler>>& handlerMap) {
156   auto handlers = handlers_.wlock();
157   for (auto& entry : *handlers) {
158     auto* ptr = get_ptr(handlerMap, entry);
159     if (ptr) {
160       entry = *ptr;
161     }
162   }
163 }
164
165 void LogCategory::setLevel(LogLevel level, bool inherit) {
166   // We have to set the level through LoggerDB, since we require holding
167   // the LoggerDB lock to iterate through our children in case our effective
168   // level changes.
169   db_->setLevel(this, level, inherit);
170 }
171
172 void LogCategory::setLevelLocked(LogLevel level, bool inherit) {
173   // Clamp the value to MIN_LEVEL and MAX_LEVEL.
174   //
175   // This makes sure that UNINITIALIZED is always less than any valid level
176   // value, and that level values cannot conflict with our flag bits.
177   level = constexpr_clamp(level, LogLevel::MIN_LEVEL, LogLevel::MAX_LEVEL);
178
179   // Make sure the inherit flag is always off for the root logger.
180   if (!parent_) {
181     inherit = false;
182   }
183   auto newValue = static_cast<uint32_t>(level);
184   if (inherit) {
185     newValue |= FLAG_INHERIT;
186   }
187
188   // Update the stored value
189   uint32_t oldValue = level_.exchange(newValue, std::memory_order_acq_rel);
190
191   // Break out early if the value has not changed.
192   if (oldValue == newValue) {
193     return;
194   }
195
196   // Update the effective log level
197   LogLevel newEffectiveLevel;
198   if (inherit) {
199     newEffectiveLevel = std::min(level, parent_->getEffectiveLevel());
200   } else {
201     newEffectiveLevel = level;
202   }
203   updateEffectiveLevel(newEffectiveLevel);
204 }
205
206 void LogCategory::updateEffectiveLevel(LogLevel newEffectiveLevel) {
207   auto oldEffectiveLevel =
208       effectiveLevel_.exchange(newEffectiveLevel, std::memory_order_acq_rel);
209   // Break out early if the value did not change.
210   if (newEffectiveLevel == oldEffectiveLevel) {
211     return;
212   }
213
214   // Update all of the values in xlogLevels_
215   for (auto* levelPtr : xlogLevels_) {
216     levelPtr->store(newEffectiveLevel, std::memory_order_release);
217   }
218
219   // Update all children loggers
220   LogCategory* child = firstChild_;
221   while (child != nullptr) {
222     child->parentLevelUpdated(newEffectiveLevel);
223     child = child->nextSibling_;
224   }
225 }
226
227 void LogCategory::parentLevelUpdated(LogLevel parentEffectiveLevel) {
228   uint32_t levelValue = level_.load(std::memory_order_acquire);
229   auto inherit = (levelValue & FLAG_INHERIT);
230   if (!inherit) {
231     return;
232   }
233
234   auto myLevel = static_cast<LogLevel>(levelValue & ~FLAG_INHERIT);
235   auto newEffectiveLevel = std::min(myLevel, parentEffectiveLevel);
236   updateEffectiveLevel(newEffectiveLevel);
237 }
238
239 void LogCategory::registerXlogLevel(std::atomic<LogLevel>* levelPtr) {
240   xlogLevels_.push_back(levelPtr);
241 }
242 } // namespace folly