/* * 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 #include #include #include #include #include #include #include #include #include #include namespace folly { LogCategory::LogCategory(LoggerDB* db) : effectiveLevel_{LogLevel::ERR}, level_{static_cast(LogLevel::ERR)}, parent_{nullptr}, name_{}, db_{db} {} LogCategory::LogCategory(StringPiece name, LogCategory* parent) : effectiveLevel_{parent->getEffectiveLevel()}, level_{static_cast(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( "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* handlers = nullptr; size_t numHandlers = 0; constexpr uint32_t kSmallOptimizationSize = 5; std::array, kSmallOptimizationSize> handlersArray; std::vector> 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 handler) { auto handlers = handlers_.wlock(); handlers->emplace_back(std::move(handler)); } void LogCategory::clearHandlers() { std::vector> 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> LogCategory::getHandlers() const { return *(handlers_.rlock()); } void LogCategory::replaceHandlers( std::vector> handlers) { return handlers_.wlock()->swap(handlers); } void LogCategory::updateHandlers(const std::unordered_map< std::shared_ptr, std::shared_ptr>& 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(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(levelValue & ~FLAG_INHERIT); auto newEffectiveLevel = std::min(myLevel, parentEffectiveLevel); updateEffectiveLevel(newEffectiveLevel); } void LogCategory::registerXlogLevel(std::atomic* levelPtr) { xlogLevels_.push_back(levelPtr); } } // namespace folly