249 lines
7.8 KiB
C++
249 lines
7.8 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <folly/logging/LogCategory.h>
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
|
|
#include <folly/ConstexprMath.h>
|
|
#include <folly/ExceptionString.h>
|
|
#include <folly/FileUtil.h>
|
|
#include <folly/MapUtil.h>
|
|
#include <folly/logging/LogHandler.h>
|
|
#include <folly/logging/LogMessage.h>
|
|
#include <folly/logging/LogName.h>
|
|
#include <folly/logging/LoggerDB.h>
|
|
|
|
namespace folly {
|
|
|
|
LogCategory::LogCategory(LoggerDB* db)
|
|
: effectiveLevel_{LogLevel::ERR},
|
|
level_{static_cast<uint32_t>(LogLevel::ERR)},
|
|
parent_{nullptr},
|
|
name_{},
|
|
db_{db} {}
|
|
|
|
LogCategory::LogCategory(StringPiece name, LogCategory* parent)
|
|
: effectiveLevel_{parent->getEffectiveLevel()},
|
|
level_{static_cast<uint32_t>(LogLevel::MAX_LEVEL) | FLAG_INHERIT},
|
|
parent_{parent},
|
|
name_{LogName::canonicalize(name)},
|
|
db_{parent->getDB()},
|
|
nextSibling_{parent_->firstChild_} {
|
|
parent_->firstChild_ = this;
|
|
}
|
|
|
|
void LogCategory::admitMessage(const LogMessage& message) const {
|
|
processMessage(message);
|
|
|
|
// If this is a fatal message, flush the handlers to make sure the log
|
|
// message was written out, then crash.
|
|
if (isLogLevelFatal(message.getLevel())) {
|
|
auto numHandlers = db_->flushAllHandlers();
|
|
if (numHandlers == 0) {
|
|
// No log handlers were configured.
|
|
// Print the message to stderr, to make sure we always print the reason
|
|
// we are crashing somewhere.
|
|
auto msg = folly::to<std::string>(
|
|
"FATAL:",
|
|
message.getFileName(),
|
|
":",
|
|
message.getLineNumber(),
|
|
": ",
|
|
message.getMessage(),
|
|
"\n");
|
|
folly::writeFull(STDERR_FILENO, msg.data(), msg.size());
|
|
}
|
|
std::abort();
|
|
}
|
|
}
|
|
|
|
void LogCategory::processMessage(const LogMessage& message) const {
|
|
// Make a copy of any attached LogHandlers, so we can release the handlers_
|
|
// lock before holding them.
|
|
//
|
|
// In the common case there will only be a small number of handlers. Use a
|
|
// std::array in this case to avoid a heap allocation for the vector.
|
|
const std::shared_ptr<LogHandler>* handlers = nullptr;
|
|
size_t numHandlers = 0;
|
|
constexpr uint32_t kSmallOptimizationSize = 5;
|
|
std::array<std::shared_ptr<LogHandler>, kSmallOptimizationSize> handlersArray;
|
|
std::vector<std::shared_ptr<LogHandler>> handlersVector;
|
|
{
|
|
auto lockedHandlers = handlers_.rlock();
|
|
numHandlers = lockedHandlers->size();
|
|
if (numHandlers <= kSmallOptimizationSize) {
|
|
for (size_t n = 0; n < numHandlers; ++n) {
|
|
handlersArray[n] = (*lockedHandlers)[n];
|
|
}
|
|
handlers = handlersArray.data();
|
|
} else {
|
|
handlersVector = *lockedHandlers;
|
|
handlers = handlersVector.data();
|
|
}
|
|
}
|
|
|
|
for (size_t n = 0; n < numHandlers; ++n) {
|
|
try {
|
|
handlers[n]->handleMessage(message, this);
|
|
} catch (const std::exception& ex) {
|
|
// Use LoggerDB::internalWarning() to report the error, but continue
|
|
// trying to log the message to any other handlers attached to ourself or
|
|
// one of our parent categories.
|
|
LoggerDB::internalWarning(
|
|
__FILE__,
|
|
__LINE__,
|
|
"log handler for category \"",
|
|
name_,
|
|
"\" threw an error: ",
|
|
folly::exceptionStr(ex));
|
|
}
|
|
}
|
|
|
|
// Propagate the message up to our parent LogCategory.
|
|
if (parent_ &&
|
|
message.getLevel() >=
|
|
propagateLevelMessagesToParent_.load(std::memory_order_relaxed)) {
|
|
parent_->processMessage(message);
|
|
}
|
|
}
|
|
|
|
void LogCategory::addHandler(std::shared_ptr<LogHandler> handler) {
|
|
auto handlers = handlers_.wlock();
|
|
handlers->emplace_back(std::move(handler));
|
|
}
|
|
|
|
void LogCategory::clearHandlers() {
|
|
std::vector<std::shared_ptr<LogHandler>> emptyHandlersList;
|
|
// Swap out the handlers list with the handlers_ lock held.
|
|
{
|
|
auto handlers = handlers_.wlock();
|
|
handlers->swap(emptyHandlersList);
|
|
}
|
|
// Destroy emptyHandlersList now that the handlers_ lock is released.
|
|
// This way we don't hold the handlers_ lock while invoking any of the
|
|
// LogHandler destructors.
|
|
}
|
|
|
|
std::vector<std::shared_ptr<LogHandler>> LogCategory::getHandlers() const {
|
|
return *(handlers_.rlock());
|
|
}
|
|
|
|
void LogCategory::replaceHandlers(
|
|
std::vector<std::shared_ptr<LogHandler>> handlers) {
|
|
return handlers_.wlock()->swap(handlers);
|
|
}
|
|
|
|
void LogCategory::updateHandlers(const std::unordered_map<
|
|
std::shared_ptr<LogHandler>,
|
|
std::shared_ptr<LogHandler>>& handlerMap) {
|
|
auto handlers = handlers_.wlock();
|
|
for (auto& entry : *handlers) {
|
|
auto* ptr = get_ptr(handlerMap, entry);
|
|
if (ptr) {
|
|
entry = *ptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LogCategory::setLevel(LogLevel level, bool inherit) {
|
|
// We have to set the level through LoggerDB, since we require holding
|
|
// the LoggerDB lock to iterate through our children in case our effective
|
|
// level changes.
|
|
db_->setLevel(this, level, inherit);
|
|
}
|
|
|
|
void LogCategory::setPropagateLevelMessagesToParent(LogLevel level) {
|
|
propagateLevelMessagesToParent_.store(level, std::memory_order_relaxed);
|
|
}
|
|
|
|
LogLevel LogCategory::getPropagateLevelMessagesToParentRelaxed() {
|
|
return propagateLevelMessagesToParent_.load(std::memory_order_relaxed);
|
|
}
|
|
|
|
void LogCategory::setLevelLocked(LogLevel level, bool inherit) {
|
|
// Clamp the value to MIN_LEVEL and MAX_LEVEL.
|
|
//
|
|
// This makes sure that UNINITIALIZED is always less than any valid level
|
|
// value, and that level values cannot conflict with our flag bits.
|
|
level = constexpr_clamp(level, LogLevel::MIN_LEVEL, LogLevel::MAX_LEVEL);
|
|
|
|
// Make sure the inherit flag is always off for the root logger.
|
|
if (!parent_) {
|
|
inherit = false;
|
|
}
|
|
auto newValue = static_cast<uint32_t>(level);
|
|
if (inherit) {
|
|
newValue |= FLAG_INHERIT;
|
|
}
|
|
|
|
// Update the stored value
|
|
uint32_t oldValue = level_.exchange(newValue, std::memory_order_acq_rel);
|
|
|
|
// Break out early if the value has not changed.
|
|
if (oldValue == newValue) {
|
|
return;
|
|
}
|
|
|
|
// Update the effective log level
|
|
LogLevel newEffectiveLevel;
|
|
if (inherit) {
|
|
newEffectiveLevel = std::min(level, parent_->getEffectiveLevel());
|
|
} else {
|
|
newEffectiveLevel = level;
|
|
}
|
|
updateEffectiveLevel(newEffectiveLevel);
|
|
}
|
|
|
|
void LogCategory::updateEffectiveLevel(LogLevel newEffectiveLevel) {
|
|
auto oldEffectiveLevel =
|
|
effectiveLevel_.exchange(newEffectiveLevel, std::memory_order_acq_rel);
|
|
// Break out early if the value did not change.
|
|
if (newEffectiveLevel == oldEffectiveLevel) {
|
|
return;
|
|
}
|
|
|
|
// Update all of the values in xlogLevels_
|
|
for (auto* levelPtr : xlogLevels_) {
|
|
levelPtr->store(newEffectiveLevel, std::memory_order_release);
|
|
}
|
|
|
|
// Update all children loggers
|
|
LogCategory* child = firstChild_;
|
|
while (child != nullptr) {
|
|
child->parentLevelUpdated(newEffectiveLevel);
|
|
child = child->nextSibling_;
|
|
}
|
|
}
|
|
|
|
void LogCategory::parentLevelUpdated(LogLevel parentEffectiveLevel) {
|
|
uint32_t levelValue = level_.load(std::memory_order_acquire);
|
|
auto inherit = (levelValue & FLAG_INHERIT);
|
|
if (!inherit) {
|
|
return;
|
|
}
|
|
|
|
auto myLevel = static_cast<LogLevel>(levelValue & ~FLAG_INHERIT);
|
|
auto newEffectiveLevel = std::min(myLevel, parentEffectiveLevel);
|
|
updateEffectiveLevel(newEffectiveLevel);
|
|
}
|
|
|
|
void LogCategory::registerXlogLevel(std::atomic<LogLevel>* levelPtr) {
|
|
xlogLevels_.push_back(levelPtr);
|
|
}
|
|
} // namespace folly
|