3eec18f16fb3764b3a37803f5bb240ea002f5988
[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
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>
25
26 namespace folly {
27
28 LogCategory::LogCategory(LoggerDB* db)
29     : effectiveLevel_{LogLevel::ERROR},
30       level_{static_cast<uint32_t>(LogLevel::ERROR)},
31       parent_{nullptr},
32       name_{},
33       db_{db} {}
34
35 LogCategory::LogCategory(StringPiece name, LogCategory* parent)
36     : effectiveLevel_{parent->getEffectiveLevel()},
37       level_{static_cast<uint32_t>(LogLevel::MAX_LEVEL) | FLAG_INHERIT},
38       parent_{parent},
39       name_{LogName::canonicalize(name)},
40       db_{parent->getDB()},
41       nextSibling_{parent_->firstChild_} {
42   parent_->firstChild_ = this;
43 }
44
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.
48   //
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;
56   {
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];
62       }
63       handlers = handlersArray.data();
64     } else {
65       handlersVector = *lockedHandlers;
66       handlers = handlersVector.data();
67     }
68   }
69
70   for (size_t n = 0; n < numHandlers; ++n) {
71     try {
72       handlers[n]->log(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.
80       fprintf(
81           stderr,
82           "WARNING: log handler for category %s threw an error: %s\n",
83           name_.c_str(),
84           folly::exceptionStr(ex).c_str());
85     }
86   }
87
88   // Propagate the message up to our parent LogCategory.
89   //
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.
94   if (parent_) {
95     parent_->processMessage(message);
96   }
97 }
98
99 void LogCategory::addHandler(std::shared_ptr<LogHandler> handler) {
100   auto handlers = handlers_.wlock();
101   handlers->emplace_back(std::move(handler));
102 }
103
104 void LogCategory::clearHandlers() {
105   std::vector<std::shared_ptr<LogHandler>> emptyHandlersList;
106   // Swap out the handlers list with the handlers_ lock held.
107   {
108     auto handlers = handlers_.wlock();
109     handlers->swap(emptyHandlersList);
110   }
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.
114 }
115
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
119   // level changes.
120   db_->setLevel(this, level, inherit);
121 }
122
123 void LogCategory::setLevelLocked(LogLevel level, bool inherit) {
124   // Clamp the value to MIN_LEVEL and MAX_LEVEL.
125   //
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;
132   }
133
134   // Make sure the inherit flag is always off for the root logger.
135   if (!parent_) {
136     inherit = false;
137   }
138   auto newValue = static_cast<uint32_t>(level);
139   if (inherit) {
140     newValue |= FLAG_INHERIT;
141   }
142
143   // Update the stored value
144   uint32_t oldValue = level_.exchange(newValue, std::memory_order_acq_rel);
145
146   // Break out early if the value has not changed.
147   if (oldValue == newValue) {
148     return;
149   }
150
151   // Update the effective log level
152   LogLevel newEffectiveLevel;
153   if (inherit) {
154     newEffectiveLevel = std::min(level, parent_->getEffectiveLevel());
155   } else {
156     newEffectiveLevel = level;
157   }
158   updateEffectiveLevel(newEffectiveLevel);
159 }
160
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) {
166     return;
167   }
168
169   // Update all of the values in xlogLevels_
170   for (auto* levelPtr : xlogLevels_) {
171     levelPtr->store(newEffectiveLevel, std::memory_order_release);
172   }
173
174   // Update all children loggers
175   LogCategory* child = firstChild_;
176   while (child != nullptr) {
177     child->parentLevelUpdated(newEffectiveLevel);
178     child = child->nextSibling_;
179   }
180 }
181
182 void LogCategory::parentLevelUpdated(LogLevel parentEffectiveLevel) {
183   uint32_t levelValue = level_.load(std::memory_order_acquire);
184   auto inherit = (levelValue & FLAG_INHERIT);
185   if (!inherit) {
186     return;
187   }
188
189   auto myLevel = static_cast<LogLevel>(levelValue & ~FLAG_INHERIT);
190   auto newEffectiveLevel = std::min(myLevel, parentEffectiveLevel);
191   updateEffectiveLevel(newEffectiveLevel);
192 }
193
194 void LogCategory::registerXlogLevel(std::atomic<LogLevel>* levelPtr) {
195   xlogLevels_.push_back(levelPtr);
196 }
197 }