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