unioil-loyalty-rn-app/ios/Pods/Flipper-Folly/folly/logging/LogStreamProcessor.h

470 lines
16 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.
*/
#pragma once
#include <cstdlib>
#include <fmt/core.h>
#include <folly/CPortability.h>
#include <folly/Conv.h>
#include <folly/ExceptionString.h>
#include <folly/Portability.h>
#include <folly/lang/Exception.h>
#include <folly/logging/LogCategory.h>
#include <folly/logging/LogMessage.h>
#include <folly/logging/LogStream.h>
#include <folly/logging/ObjectToString.h>
namespace folly {
template <bool IsInHeaderFile>
class XlogCategoryInfo;
class XlogFileScopeInfo;
/**
* LogStreamProcessor receives a LogStream and logs it.
*
* This class is primarily intended to be used through the FB_LOG*() macros.
* Its API is designed to support these macros, and is not designed for other
* use.
*
* The operator&() method is used to trigger the logging.
* This operator is used because it has lower precedence than <<, but higher
* precedence than the ? ternary operator, allowing it to bind with the correct
* precedence in the log macro implementations.
*/
class LogStreamProcessor {
public:
enum AppendType { APPEND };
enum FormatType { FORMAT };
/**
* LogStreamProcessor constructor for use with a LOG() macro with no extra
* arguments.
*
* Note that the filename argument is not copied. The caller should ensure
* that it points to storage that will remain valid for the lifetime of the
* LogStreamProcessor. (This is always the case for the __FILE__
* preprocessor macro.)
*/
LogStreamProcessor(
const LogCategory* category,
LogLevel level,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
AppendType) noexcept;
/**
* LogStreamProcessor constructor for use with a LOG() macro with arguments
* to be concatenated with folly::to<std::string>()
*
* Note that the filename argument is not copied. The caller should ensure
* that it points to storage that will remain valid for the lifetime of the
* LogStreamProcessor. (This is always the case for the __FILE__
* preprocessor macro.)
*/
template <typename... Args>
LogStreamProcessor(
const LogCategory* category,
LogLevel level,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
AppendType,
Args&&... args) noexcept
: LogStreamProcessor(
category,
level,
filename,
lineNumber,
functionName,
INTERNAL,
createLogString(std::forward<Args>(args)...)) {}
/**
* LogStreamProcessor constructor for use with a LOG() macro with arguments
* to be concatenated with folly::to<std::string>()
*
* Note that the filename argument is not copied. The caller should ensure
* that it points to storage that will remain valid for the lifetime of the
* LogStreamProcessor. (This is always the case for the __FILE__
* preprocessor macro.)
*/
template <typename... Args>
LogStreamProcessor(
const LogCategory* category,
LogLevel level,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
FormatType,
folly::StringPiece fmt,
Args&&... args) noexcept
: LogStreamProcessor(
category,
level,
filename,
lineNumber,
functionName,
INTERNAL,
formatLogString(fmt, std::forward<Args>(args)...)) {}
/*
* Versions of the above constructors for use in XLOG() statements.
*
* These are defined separately from the above constructor so that the work
* of initializing the XLOG LogCategory data is done in a separate function
* body defined in LogStreamProcessor.cpp. We intentionally want to avoid
* inlining this work at every XLOG() statement, to reduce the emitted code
* size.
*/
LogStreamProcessor(
XlogCategoryInfo<true>* categoryInfo,
LogLevel level,
folly::StringPiece categoryName,
bool isCategoryNameOverridden,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
AppendType) noexcept;
template <typename... Args>
LogStreamProcessor(
XlogCategoryInfo<true>* categoryInfo,
LogLevel level,
folly::StringPiece categoryName,
bool isCategoryNameOverridden,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
AppendType,
Args&&... args) noexcept
: LogStreamProcessor(
categoryInfo,
level,
categoryName,
isCategoryNameOverridden,
filename,
lineNumber,
functionName,
INTERNAL,
createLogString(std::forward<Args>(args)...)) {}
template <typename... Args>
LogStreamProcessor(
XlogCategoryInfo<true>* categoryInfo,
LogLevel level,
folly::StringPiece categoryName,
bool isCategoryNameOverridden,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
FormatType,
folly::StringPiece fmt,
Args&&... args) noexcept
: LogStreamProcessor(
categoryInfo,
level,
categoryName,
isCategoryNameOverridden,
filename,
lineNumber,
functionName,
INTERNAL,
formatLogString(fmt, std::forward<Args>(args)...)) {}
#ifdef __INCLUDE_LEVEL__
/*
* Versions of the above constructors to use in XLOG() macros that appear in
* .cpp files. These are only used if the compiler supports the
* __INCLUDE_LEVEL__ macro, which we need to determine that the XLOG()
* statement is not in a header file.
*
* These behave identically to the XlogCategoryInfo<true> versions of the
* APIs, but slightly more optimized, and allow the XLOG() code to avoid
* storing category information at each XLOG() call site.
*/
LogStreamProcessor(
XlogFileScopeInfo* fileScopeInfo,
LogLevel level,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
AppendType) noexcept;
LogStreamProcessor(
XlogFileScopeInfo* fileScopeInfo,
LogLevel level,
folly::StringPiece /* categoryName */,
bool /* isCategoryNameOverridden */,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
AppendType) noexcept
: LogStreamProcessor(
fileScopeInfo, level, filename, lineNumber, functionName, APPEND) {}
template <typename... Args>
LogStreamProcessor(
XlogFileScopeInfo* fileScopeInfo,
LogLevel level,
folly::StringPiece /* categoryName */,
bool /* isCategoryNameOverridden */,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
AppendType,
Args&&... args) noexcept
: LogStreamProcessor(
fileScopeInfo,
level,
filename,
lineNumber,
functionName,
INTERNAL,
createLogString(std::forward<Args>(args)...)) {}
template <typename... Args>
LogStreamProcessor(
XlogFileScopeInfo* fileScopeInfo,
LogLevel level,
folly::StringPiece /* categoryName */,
bool /* isCategoryNameOverridden */,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
FormatType,
folly::StringPiece fmt,
Args&&... args) noexcept
: LogStreamProcessor(
fileScopeInfo,
level,
filename,
lineNumber,
functionName,
INTERNAL,
formatLogString(fmt, std::forward<Args>(args)...)) {}
#endif
~LogStreamProcessor() noexcept;
/**
* This version of operator&() is typically used when the user specifies
* log arguments using the << stream operator. The operator<<() generally
* returns a std::ostream&
*/
void operator&(std::ostream& stream) noexcept;
/**
* This version of operator&() is used when no extra arguments are specified
* with the << operator. In this case the & operator is applied directly to
* the temporary LogStream object.
*/
void operator&(LogStream&& stream) noexcept;
std::ostream& stream() noexcept { return stream_; }
void logNow() noexcept;
private:
enum InternalType { INTERNAL };
LogStreamProcessor(
const LogCategory* category,
LogLevel level,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
InternalType,
std::string&& msg) noexcept;
LogStreamProcessor(
XlogCategoryInfo<true>* categoryInfo,
LogLevel level,
folly::StringPiece categoryName,
bool isCategoryNameOverridden,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
InternalType,
std::string&& msg) noexcept;
LogStreamProcessor(
XlogFileScopeInfo* fileScopeInfo,
LogLevel level,
folly::StringPiece filename,
unsigned int lineNumber,
folly::StringPiece functionName,
InternalType,
std::string&& msg) noexcept;
std::string extractMessageString(LogStream& stream) noexcept;
/**
* Construct a log message string using folly::to<std::string>()
*
* This function attempts to avoid throwing exceptions. If an error occurs
* during formatting, a message including the error details is returned
* instead. This is done to help ensure that log statements do not generate
* exceptions, but instead just log an error string when something goes wrong.
*/
template <typename... Args>
FOLLY_NOINLINE std::string createLogString(Args&&... args) noexcept {
return folly::catch_exception<const std::exception&>(
[&] { return folly::to<std::string>(std::forward<Args>(args)...); },
[&](const std::exception& ex) {
// This most likely means there was some error converting the
// arguments to strings. Handle the exception here, rather than
// letting it propagate up, since callers generally do not expect log
// statements to throw.
//
// Just log an error message letting indicating that something went
// wrong formatting the log message.
return folly::to<std::string>(
"error constructing log message: ", exceptionStr(ex));
});
}
FOLLY_NOINLINE std::string vformatLogString(
folly::StringPiece fmt, fmt::format_args args, bool& failed) noexcept {
return folly::catch_exception<const std::exception&>(
[&] {
return fmt::vformat(fmt::string_view(fmt.data(), fmt.size()), args);
},
[&](const std::exception& ex) {
// This most likely means that the caller had a bug in their format
// string/arguments. Handle the exception here, rather than letting
// it propagate up, since callers generally do not expect log
// statements to throw.
//
// Log the format string and as much of the arguments as we can
// convert, to aid debugging.
failed = true;
std::string result;
result.append("error formatting log message: ");
result.append(exceptionStr(ex).c_str());
result.append("; format string: \"");
result.append(fmt.data(), fmt.size());
result.append("\", arguments: ");
return result;
});
}
/**
* Construct a log message string using fmt::format()
*
* This function attempts to avoid throwing exceptions. If an error occurs
* during formatting, a message including the error details is returned
* instead. This is done to help ensure that log statements do not generate
* exceptions, but instead just log an error string when something goes wrong.
*/
template <typename... Args>
FOLLY_NOINLINE std::string formatLogString(
folly::StringPiece fmt, const Args&... args) noexcept {
bool failed = false;
std::string result =
vformatLogString(fmt, fmt::make_format_args(args...), failed);
if (failed) {
folly::logging::appendToString(result, args...);
}
return result;
}
const LogCategory* const category_;
LogLevel const level_;
folly::StringPiece filename_;
unsigned int lineNumber_;
folly::StringPiece functionName_;
std::string message_;
LogStream stream_;
};
/**
* LogStreamVoidify() is a helper class used in the FB_LOG() and XLOG() macros.
*
* It's only purpose is to provide an & operator overload that returns void.
* This allows the log macros to expand roughly to:
*
* (logEnabled) ? (void)0
* : LogStreamVoidify{} & LogStreamProcessor{}.stream() << "msg";
*
* This enables the right hand (':') side of the ternary ? expression to have a
* void type, and allows various streaming operator expressions to be placed on
* the right hand side of the expression.
*
* Operator & is used since it has higher precedence than ?:, but lower
* precedence than <<.
*
* This class is templated on whether the log message is fatal so that the
* operator& can be declared [[noreturn]] for fatal log messages. This
* prevents the compiler from complaining about functions that do not return a
* value after a fatal log statement.
*/
template <bool Fatal>
class LogStreamVoidify {
public:
/**
* In the default (non-fatal) case, the & operator implementation is a no-op.
*
* We perform the actual logging in the LogStreamProcessor destructor. It
* feels slightly hacky to perform logging in the LogStreamProcessor
* destructor instead of here, since the LogStreamProcessor destructor is not
* evaluated until the very end of the statement. In practice log
* statements really shouldn't be in the middle of larger statements with
* other side effects, so this ordering distinction shouldn't make much
* difference.
*
* However, by keeping this function a no-op we reduce the amount of code
* generated for log statements. This function call can be completely
* eliminated by the compiler, leaving only the LogStreamProcessor destructor
* invocation, which cannot be eliminated.
*/
void operator&(std::ostream&) noexcept {}
};
template <>
class LogStreamVoidify<true> {
public:
/**
* A specialized noreturn version of operator&() for fatal log statements.
*/
[[noreturn]] void operator&(std::ostream&);
};
/**
* logDisabledHelper() is invoked in FB_LOG() and XLOG() statements if the log
* admittance check fails.
*
* This function exists solely to ensure that both sides of the log check are
* marked [[noreturn]] for fatal log messages. This allows the compiler to
* recognize that the full statement is noreturn, preventing warnings about
* missing return statements after fatal log messages.
*
* Unfortunately it does not appear possible to get the compiler to recognize
* that the disabled side of the log statement should never be reached for
* fatal messages. Even if we make the check something like
* `(isLogLevelFatal(level) || realCheck)`, where isLogLevelFatal() is
* constexpr, this is not sufficient for gcc or clang to recognize that the
* full expression is noreturn.
*
* Ideally this would just be a template function specialized on a boolean
* IsFatal parameter. Unfortunately this triggers a bug in clang, which does
* not like differing noreturn behavior for different template instantiations.
* Therefore we overload on integral_constant instead.
*
* clang-format also doesn't do a good job understanding this code and figuring
* out how to format it.
*/
// clang-format off
inline void logDisabledHelper(std::false_type) noexcept {}
[[noreturn]] void logDisabledHelper(std::true_type) noexcept;
// clang-format on
} // namespace folly