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/LogCategory.h>
20 #include <folly/ExceptionString.h>
21 #include <folly/experimental/logging/LogHandler.h>
22 #include <folly/experimental/logging/LogMessage.h>
23 #include <folly/experimental/logging/LogName.h>
24 #include <folly/experimental/logging/LoggerDB.h>
28 LogCategory::LogCategory(LoggerDB* db)
29 : effectiveLevel_{LogLevel::ERROR},
30 level_{static_cast<uint32_t>(LogLevel::ERROR)},
35 LogCategory::LogCategory(StringPiece name, LogCategory* parent)
36 : effectiveLevel_{parent->getEffectiveLevel()},
37 level_{static_cast<uint32_t>(LogLevel::MAX_LEVEL) | FLAG_INHERIT},
39 name_{LogName::canonicalize(name)},
41 nextSibling_{parent_->firstChild_} {
42 parent_->firstChild_ = this;
45 void LogCategory::processMessage(const LogMessage& message) const {
46 // Make a copy of any attached LogHandlers, so we can release the handlers_
47 // lock before holding them.
49 // In the common case there will only be a small number of handlers. Use a
50 // std::array in this case to avoid a heap allocation for the vector.
51 const std::shared_ptr<LogHandler>* handlers = nullptr;
52 size_t numHandlers = 0;
53 constexpr uint32_t kSmallOptimizationSize = 5;
54 std::array<std::shared_ptr<LogHandler>, kSmallOptimizationSize> handlersArray;
55 std::vector<std::shared_ptr<LogHandler>> handlersVector;
57 auto lockedHandlers = handlers_.rlock();
58 numHandlers = lockedHandlers->size();
59 if (numHandlers <= kSmallOptimizationSize) {
60 for (size_t n = 0; n < numHandlers; ++n) {
61 handlersArray[n] = (*lockedHandlers)[n];
63 handlers = handlersArray.data();
65 handlersVector = *lockedHandlers;
66 handlers = handlersVector.data();
70 for (size_t n = 0; n < numHandlers; ++n) {
72 handlers[n]->handleMessage(message, this);
73 } catch (const std::exception& ex) {
74 // If a LogHandler throws an exception, complain about this fact on
75 // stderr to avoid swallowing the error information completely. We
76 // don't propagate the exception up to our caller: most code does not
77 // prepare for log statements to throw. We also want to continue
78 // trying to log the message to any other handlers attached to ourself
79 // or one of our parent categories.
82 "WARNING: log handler for category %s threw an error: %s\n",
84 folly::exceptionStr(ex).c_str());
88 // Propagate the message up to our parent LogCategory.
90 // Maybe in the future it might be worth adding a flag to control if a
91 // LogCategory should propagate messages to its parent or not. (This would
92 // be similar to log4j's "additivity" flag.)
93 // For now I don't have a strong use case for this.
95 parent_->processMessage(message);
99 void LogCategory::addHandler(std::shared_ptr<LogHandler> handler) {
100 auto handlers = handlers_.wlock();
101 handlers->emplace_back(std::move(handler));
104 void LogCategory::clearHandlers() {
105 std::vector<std::shared_ptr<LogHandler>> emptyHandlersList;
106 // Swap out the handlers list with the handlers_ lock held.
108 auto handlers = handlers_.wlock();
109 handlers->swap(emptyHandlersList);
111 // Destroy emptyHandlersList now that the handlers_ lock is released.
112 // This way we don't hold the handlers_ lock while invoking any of the
113 // LogHandler destructors.
116 void LogCategory::setLevel(LogLevel level, bool inherit) {
117 // We have to set the level through LoggerDB, since we require holding
118 // the LoggerDB lock to iterate through our children in case our effective
120 db_->setLevel(this, level, inherit);
123 void LogCategory::setLevelLocked(LogLevel level, bool inherit) {
124 // Clamp the value to MIN_LEVEL and MAX_LEVEL.
126 // This makes sure that UNINITIALIZED is always less than any valid level
127 // value, and that level values cannot conflict with our flag bits.
128 if (level > LogLevel::MAX_LEVEL) {
129 level = LogLevel::MAX_LEVEL;
130 } else if (level < LogLevel::MIN_LEVEL) {
131 level = LogLevel::MIN_LEVEL;
134 // Make sure the inherit flag is always off for the root logger.
138 auto newValue = static_cast<uint32_t>(level);
140 newValue |= FLAG_INHERIT;
143 // Update the stored value
144 uint32_t oldValue = level_.exchange(newValue, std::memory_order_acq_rel);
146 // Break out early if the value has not changed.
147 if (oldValue == newValue) {
151 // Update the effective log level
152 LogLevel newEffectiveLevel;
154 newEffectiveLevel = std::min(level, parent_->getEffectiveLevel());
156 newEffectiveLevel = level;
158 updateEffectiveLevel(newEffectiveLevel);
161 void LogCategory::updateEffectiveLevel(LogLevel newEffectiveLevel) {
162 auto oldEffectiveLevel =
163 effectiveLevel_.exchange(newEffectiveLevel, std::memory_order_acq_rel);
164 // Break out early if the value did not change.
165 if (newEffectiveLevel == oldEffectiveLevel) {
169 // Update all of the values in xlogLevels_
170 for (auto* levelPtr : xlogLevels_) {
171 levelPtr->store(newEffectiveLevel, std::memory_order_release);
174 // Update all children loggers
175 LogCategory* child = firstChild_;
176 while (child != nullptr) {
177 child->parentLevelUpdated(newEffectiveLevel);
178 child = child->nextSibling_;
182 void LogCategory::parentLevelUpdated(LogLevel parentEffectiveLevel) {
183 uint32_t levelValue = level_.load(std::memory_order_acquire);
184 auto inherit = (levelValue & FLAG_INHERIT);
189 auto myLevel = static_cast<LogLevel>(levelValue & ~FLAG_INHERIT);
190 auto newEffectiveLevel = std::min(myLevel, parentEffectiveLevel);
191 updateEffectiveLevel(newEffectiveLevel);
194 void LogCategory::registerXlogLevel(std::atomic<LogLevel>* levelPtr) {
195 xlogLevels_.push_back(levelPtr);