/* * Copyright 2017-present Facebook, Inc. * * 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 namespace folly { LogStreamProcessor::LogStreamProcessor( const LogCategory* category, LogLevel level, folly::StringPiece filename, unsigned int lineNumber, folly::StringPiece functionName, AppendType) noexcept : LogStreamProcessor( category, level, filename, lineNumber, functionName, INTERNAL, std::string()) {} LogStreamProcessor::LogStreamProcessor( XlogCategoryInfo* categoryInfo, LogLevel level, folly::StringPiece categoryName, bool isCategoryNameOverridden, folly::StringPiece filename, unsigned int lineNumber, folly::StringPiece functionName, AppendType) noexcept : LogStreamProcessor( categoryInfo, level, categoryName, isCategoryNameOverridden, filename, lineNumber, functionName, INTERNAL, std::string()) {} LogStreamProcessor::LogStreamProcessor( const LogCategory* category, LogLevel level, folly::StringPiece filename, unsigned int lineNumber, folly::StringPiece functionName, InternalType, std::string&& msg) noexcept : category_{category}, level_{level}, filename_{filename}, lineNumber_{lineNumber}, functionName_{functionName}, message_{std::move(msg)}, stream_{this} {} namespace { LogCategory* getXlogCategory( XlogCategoryInfo* categoryInfo, folly::StringPiece categoryName, bool isCategoryNameOverridden) { if (!categoryInfo->isInitialized()) { return categoryInfo->init(categoryName, isCategoryNameOverridden); } return categoryInfo->getCategory(&xlog_detail::xlogFileScopeInfo); } } // namespace /** * Construct a LogStreamProcessor from an XlogCategoryInfo. * * We intentionally define this in LogStreamProcessor.cpp instead of * LogStreamProcessor.h to avoid having it inlined at every XLOG() call site, * to reduce the emitted code size. */ LogStreamProcessor::LogStreamProcessor( XlogCategoryInfo* categoryInfo, LogLevel level, folly::StringPiece categoryName, bool isCategoryNameOverridden, folly::StringPiece filename, unsigned int lineNumber, folly::StringPiece functionName, InternalType, std::string&& msg) noexcept : category_{getXlogCategory( categoryInfo, categoryName, isCategoryNameOverridden)}, level_{level}, filename_{filename}, lineNumber_{lineNumber}, functionName_{functionName}, message_{std::move(msg)}, stream_{this} {} #ifdef __INCLUDE_LEVEL__ namespace { LogCategory* getXlogCategory(XlogFileScopeInfo* fileScopeInfo) { // By the time a LogStreamProcessor is created, the XlogFileScopeInfo object // should have already been initialized to perform the log level check. // Therefore we never need to check if it is initialized here. return fileScopeInfo->category; } } // namespace /** * Construct a LogStreamProcessor from an XlogFileScopeInfo. * * We intentionally define this in LogStreamProcessor.cpp instead of * LogStreamProcessor.h to avoid having it inlined at every XLOG() call site, * to reduce the emitted code size. * * This is only defined if __INCLUDE_LEVEL__ is available. The * XlogFileScopeInfo APIs are only invoked if we can use __INCLUDE_LEVEL__ to * tell that an XLOG() statement occurs in a non-header file. For compilers * that do not support __INCLUDE_LEVEL__, the category information is always * passed in as XlogCategoryInfo rather than as XlogFileScopeInfo. */ LogStreamProcessor::LogStreamProcessor( XlogFileScopeInfo* fileScopeInfo, LogLevel level, folly::StringPiece filename, unsigned int lineNumber, folly::StringPiece functionName, InternalType, std::string&& msg) noexcept : category_{getXlogCategory(fileScopeInfo)}, level_{level}, filename_{filename}, lineNumber_{lineNumber}, functionName_{functionName}, message_{std::move(msg)}, stream_{this} {} LogStreamProcessor::LogStreamProcessor( XlogFileScopeInfo* fileScopeInfo, LogLevel level, folly::StringPiece filename, unsigned int lineNumber, folly::StringPiece functionName, AppendType) noexcept : LogStreamProcessor( fileScopeInfo, level, filename, lineNumber, functionName, INTERNAL, std::string()) {} #endif /* * We intentionally define the LogStreamProcessor destructor in * LogStreamProcessor.cpp instead of LogStreamProcessor.h to avoid having it * emitted inline at every log statement site. This helps reduce the emitted * code size for each log statement. */ LogStreamProcessor::~LogStreamProcessor() noexcept { // The LogStreamProcessor destructor is responsible for logging the message. // Doing this in the destructor avoids an separate function call to log the // message being emitted inline at every log statement site. logNow(); } void LogStreamProcessor::logNow() noexcept { // Note that admitMessage() is not noexcept and theoretically may throw. // However, the only exception that should be possible is std::bad_alloc if // we fail to allocate memory. We intentionally let our noexcept specifier // crash in that case, since the program likely won't be able to continue // anyway. // // Any other error here is unexpected and we also want to fail hard // in that situation too. category_->admitMessage(LogMessage{category_, level_, filename_, lineNumber_, functionName_, extractMessageString(stream_)}); } std::string LogStreamProcessor::extractMessageString( LogStream& stream) noexcept { if (stream.empty()) { return std::move(message_); } if (message_.empty()) { return stream.extractString(); } message_.append(stream.extractString()); return std::move(message_); } void LogStreamVoidify::operator&(std::ostream& stream) { // Non-fatal log messages wait until the LogStreamProcessor destructor to log // the message. However for fatal messages we log immediately in the & // operator, since it is marked noreturn. // // This does result in slightly larger emitted code for fatal log messages // (since the operator & call cannot be completely omitted). However, fatal // log messages should typically be much more rare than non-fatal messages, // so the small amount of extra overhead shouldn't be a big deal. auto& logStream = static_cast(stream); logStream.getProcessor()->logNow(); abort(); } void logDisabledHelper(std::true_type) noexcept { // This function can only be reached if we had a disabled fatal log message. // This should never happen: LogCategory::setLevelLocked() does not allow // setting the threshold for a category lower than FATAL (in production // builds) or DFATAL (in debug builds). abort(); } } // namespace folly