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