2 * Copyright 2017-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/LogCategory.h>
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>
32 LogCategory::LogCategory(LoggerDB* db)
33 : effectiveLevel_{LogLevel::ERR},
34 level_{static_cast<uint32_t>(LogLevel::ERR)},
39 LogCategory::LogCategory(StringPiece name, LogCategory* parent)
40 : effectiveLevel_{parent->getEffectiveLevel()},
41 level_{static_cast<uint32_t>(LogLevel::MAX_LEVEL) | FLAG_INHERIT},
43 name_{LogName::canonicalize(name)},
45 nextSibling_{parent_->firstChild_} {
46 parent_->firstChild_ = this;
49 void LogCategory::admitMessage(const LogMessage& message) const {
50 processMessage(message);
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>(
62 message.getFileName(),
64 message.getLineNumber(),
68 folly::writeFull(STDERR_FILENO, msg.data(), msg.size());
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.
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;
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];
92 handlers = handlersArray.data();
94 handlersVector = *lockedHandlers;
95 handlers = handlersVector.data();
99 for (size_t n = 0; n < numHandlers; ++n) {
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(
109 "log handler for category \"",
111 "\" threw an error: ",
112 folly::exceptionStr(ex));
116 // Propagate the message up to our parent LogCategory.
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.
123 parent_->processMessage(message);
127 void LogCategory::addHandler(std::shared_ptr<LogHandler> handler) {
128 auto handlers = handlers_.wlock();
129 handlers->emplace_back(std::move(handler));
132 void LogCategory::clearHandlers() {
133 std::vector<std::shared_ptr<LogHandler>> emptyHandlersList;
134 // Swap out the handlers list with the handlers_ lock held.
136 auto handlers = handlers_.wlock();
137 handlers->swap(emptyHandlersList);
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.
144 std::vector<std::shared_ptr<LogHandler>> LogCategory::getHandlers() const {
145 return *(handlers_.rlock());
148 void LogCategory::replaceHandlers(
149 std::vector<std::shared_ptr<LogHandler>> handlers) {
150 return handlers_.wlock()->swap(handlers);
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);
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
169 db_->setLevel(this, level, inherit);
172 void LogCategory::setLevelLocked(LogLevel level, bool inherit) {
173 // Clamp the value to MIN_LEVEL and MAX_LEVEL.
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);
179 // Make sure the inherit flag is always off for the root logger.
183 auto newValue = static_cast<uint32_t>(level);
185 newValue |= FLAG_INHERIT;
188 // Update the stored value
189 uint32_t oldValue = level_.exchange(newValue, std::memory_order_acq_rel);
191 // Break out early if the value has not changed.
192 if (oldValue == newValue) {
196 // Update the effective log level
197 LogLevel newEffectiveLevel;
199 newEffectiveLevel = std::min(level, parent_->getEffectiveLevel());
201 newEffectiveLevel = level;
203 updateEffectiveLevel(newEffectiveLevel);
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) {
214 // Update all of the values in xlogLevels_
215 for (auto* levelPtr : xlogLevels_) {
216 levelPtr->store(newEffectiveLevel, std::memory_order_release);
219 // Update all children loggers
220 LogCategory* child = firstChild_;
221 while (child != nullptr) {
222 child->parentLevelUpdated(newEffectiveLevel);
223 child = child->nextSibling_;
227 void LogCategory::parentLevelUpdated(LogLevel parentEffectiveLevel) {
228 uint32_t levelValue = level_.load(std::memory_order_acquire);
229 auto inherit = (levelValue & FLAG_INHERIT);
234 auto myLevel = static_cast<LogLevel>(levelValue & ~FLAG_INHERIT);
235 auto newEffectiveLevel = std::min(myLevel, parentEffectiveLevel);
236 updateEffectiveLevel(newEffectiveLevel);
239 void LogCategory::registerXlogLevel(std::atomic<LogLevel>* levelPtr) {
240 xlogLevels_.push_back(levelPtr);