852 lines
34 KiB
C++
852 lines
34 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 <folly/Likely.h>
|
|
#include <folly/Portability.h>
|
|
#include <folly/Range.h>
|
|
#include <folly/logging/LogStream.h>
|
|
#include <folly/logging/Logger.h>
|
|
#include <folly/logging/LoggerDB.h>
|
|
#include <folly/logging/ObjectToString.h>
|
|
#include <folly/logging/RateLimiter.h>
|
|
|
|
/*
|
|
* This file contains the XLOG() and XLOGF() macros.
|
|
*
|
|
* These macros make it easy to use the logging library without having to
|
|
* manually pick log category names. All XLOG() and XLOGF() statements in a
|
|
* given file automatically use a LogCategory based on the current file name.
|
|
*
|
|
* For instance, in src/foo/bar.cpp, the default log category name will be
|
|
* "src.foo.bar"
|
|
*
|
|
* If desired, the log category name used by XLOG() in a .cpp file may be
|
|
* overridden using XLOG_SET_CATEGORY_NAME() macro.
|
|
*/
|
|
|
|
/**
|
|
* Log a message to this file's default log category.
|
|
*
|
|
* By default the log category name is automatically picked based on the
|
|
* current filename. In src/foo/bar.cpp the log category name "src.foo.bar"
|
|
* will be used. In "lib/stuff/foo.h" the log category name will be
|
|
* "lib.stuff.foo"
|
|
*
|
|
* Note that the filename is based on the __FILE__ macro defined by the
|
|
* compiler. This is typically dependent on the filename argument that you
|
|
* give to the compiler. For example, if you compile src/foo/bar.cpp by
|
|
* invoking the compiler inside src/foo and only give it "bar.cpp" as an
|
|
* argument, the category name will simply be "bar". In general XLOG() works
|
|
* best if you always invoke the compiler from the root directory of your
|
|
* project repository.
|
|
*/
|
|
#define XLOG(level, ...) \
|
|
XLOG_IMPL( \
|
|
::folly::LogLevel::level, \
|
|
::folly::LogStreamProcessor::APPEND, \
|
|
##__VA_ARGS__)
|
|
|
|
/**
|
|
* Log a message if and only if the specified condition predicate evaluates
|
|
* to true. Note that the condition is *only* evaluated if the log-level check
|
|
* passes.
|
|
*/
|
|
#define XLOG_IF(level, cond, ...) \
|
|
XLOG_IF_IMPL( \
|
|
::folly::LogLevel::level, \
|
|
cond, \
|
|
::folly::LogStreamProcessor::APPEND, \
|
|
##__VA_ARGS__)
|
|
/**
|
|
* Log a message to this file's default log category, using a format string.
|
|
*/
|
|
#define XLOGF(level, fmt, arg1, ...) \
|
|
XLOG_IMPL( \
|
|
::folly::LogLevel::level, \
|
|
::folly::LogStreamProcessor::FORMAT, \
|
|
fmt, \
|
|
arg1, \
|
|
##__VA_ARGS__)
|
|
|
|
/**
|
|
* Log a message using a format string if and only if the specified condition
|
|
* predicate evaluates to true. Note that the condition is *only* evaluated
|
|
* if the log-level check passes.
|
|
*/
|
|
#define XLOGF_IF(level, cond, fmt, arg1, ...) \
|
|
XLOG_IF_IMPL( \
|
|
::folly::LogLevel::level, \
|
|
cond, \
|
|
::folly::LogStreamProcessor::FORMAT, \
|
|
fmt, \
|
|
arg1, \
|
|
##__VA_ARGS__)
|
|
|
|
/**
|
|
* Similar to XLOG(...) except only log a message every @param ms
|
|
* milliseconds.
|
|
*
|
|
* Note that this is threadsafe.
|
|
*/
|
|
#define XLOG_EVERY_MS(level, ms, ...) \
|
|
XLOG_IF( \
|
|
level, \
|
|
[__folly_detail_xlog_ms = ms] { \
|
|
static ::folly::logging::IntervalRateLimiter \
|
|
folly_detail_xlog_limiter( \
|
|
1, std::chrono::milliseconds(__folly_detail_xlog_ms)); \
|
|
return folly_detail_xlog_limiter.check(); \
|
|
}(), \
|
|
##__VA_ARGS__)
|
|
|
|
/**
|
|
* Similar to XLOGF(...) except only log a message every @param ms
|
|
* milliseconds.
|
|
*
|
|
* Note that this is threadsafe.
|
|
*/
|
|
#define XLOGF_EVERY_MS(level, ms, fmt, arg1, ...) \
|
|
XLOGF_IF( \
|
|
level, \
|
|
[__folly_detail_xlog_ms = ms] { \
|
|
static ::folly::logging::IntervalRateLimiter \
|
|
folly_detail_xlog_limiter( \
|
|
1, std::chrono::milliseconds(__folly_detail_xlog_ms)); \
|
|
return folly_detail_xlog_limiter.check(); \
|
|
}(), \
|
|
fmt, \
|
|
arg1, \
|
|
##__VA_ARGS__)
|
|
|
|
namespace folly {
|
|
namespace detail {
|
|
|
|
template <typename Tag>
|
|
FOLLY_EXPORT FOLLY_ALWAYS_INLINE bool xlogEveryNImpl(size_t n) {
|
|
static std::atomic<size_t> count{0};
|
|
auto const value = count.load(std::memory_order_relaxed);
|
|
count.store(value + 1, std::memory_order_relaxed);
|
|
return FOLLY_UNLIKELY((value % n) == 0);
|
|
}
|
|
|
|
} // namespace detail
|
|
} // namespace folly
|
|
|
|
/**
|
|
* Similar to XLOG(...) except only log a message every @param n
|
|
* invocations, approximately.
|
|
*
|
|
* The internal counter is process-global and threadsafe but, to
|
|
* to avoid the performance degradation of atomic-rmw operations,
|
|
* increments are non-atomic. Some increments may be missed under
|
|
* contention, leading to possible over-logging or under-logging
|
|
* effects.
|
|
*/
|
|
#define XLOG_EVERY_N(level, n, ...) \
|
|
XLOG_IF( \
|
|
level, \
|
|
[&] { \
|
|
struct folly_detail_xlog_tag {}; \
|
|
return ::folly::detail::xlogEveryNImpl<folly_detail_xlog_tag>(n); \
|
|
}(), \
|
|
##__VA_ARGS__)
|
|
|
|
namespace folly {
|
|
namespace detail {
|
|
|
|
template <typename Tag>
|
|
FOLLY_EXPORT FOLLY_ALWAYS_INLINE bool xlogEveryNExactImpl(size_t n) {
|
|
static std::atomic<size_t> count{0};
|
|
auto const value = count.fetch_add(1, std::memory_order_relaxed);
|
|
return FOLLY_UNLIKELY((value % n) == 0);
|
|
}
|
|
|
|
} // namespace detail
|
|
} // namespace folly
|
|
|
|
/**
|
|
* Similar to XLOG(...) except only log a message every @param n
|
|
* invocations, exactly.
|
|
*
|
|
* The internal counter is process-global and threadsafe and
|
|
* increments are atomic. The over-logging and under-logging
|
|
* schenarios of XLOG_EVERY_N(...) are avoided, traded off for
|
|
* the performance degradation of atomic-rmw operations.
|
|
*/
|
|
#define XLOG_EVERY_N_EXACT(level, n, ...) \
|
|
XLOG_IF( \
|
|
level, \
|
|
[&] { \
|
|
struct folly_detail_xlog_tag {}; \
|
|
return ::folly::detail::xlogEveryNExactImpl<folly_detail_xlog_tag>(n); \
|
|
}(), \
|
|
##__VA_ARGS__)
|
|
|
|
namespace folly {
|
|
namespace detail {
|
|
|
|
size_t& xlogEveryNThreadEntry(void const* const key);
|
|
|
|
template <typename Tag>
|
|
FOLLY_EXPORT FOLLY_ALWAYS_INLINE bool xlogEveryNThreadImpl(size_t n) {
|
|
static char key;
|
|
auto& count = xlogEveryNThreadEntry(&key);
|
|
return FOLLY_UNLIKELY((count++ % n) == 0);
|
|
}
|
|
|
|
} // namespace detail
|
|
} // namespace folly
|
|
|
|
/**
|
|
* Similar to XLOG(...) except only log a message every @param n
|
|
* invocations per thread.
|
|
*
|
|
* The internal counter is thread-local, avoiding the contention
|
|
* which the XLOG_EVERY_N variations which use a global counter
|
|
* may suffer. If a given XLOG_EVERY_N or variation expansion is
|
|
* encountered concurrently by multiple threads in a hot code
|
|
* path and the global counter in the expansion is observed to
|
|
* be contended, then switching to XLOG_EVERY_N_THREAD can help.
|
|
*
|
|
* Every thread that invokes this expansion has a counter for
|
|
* this expansion. The internal counters are all stored in a
|
|
* single thread-local map to control TLS overhead, at the cost
|
|
* of a small runtime performance hit.
|
|
*/
|
|
#define XLOG_EVERY_N_THREAD(level, n, ...) \
|
|
XLOG_IF( \
|
|
level, \
|
|
[&] { \
|
|
struct folly_detail_xlog_tag {}; \
|
|
return ::folly::detail::xlogEveryNThreadImpl<folly_detail_xlog_tag>( \
|
|
n); \
|
|
}(), \
|
|
##__VA_ARGS__)
|
|
|
|
/**
|
|
* Similar to XLOG(...) except only log at most @param count messages
|
|
* per @param ms millisecond interval.
|
|
*
|
|
* The internal counters are process-global and threadsafe.
|
|
*/
|
|
#define XLOG_N_PER_MS(level, count, ms, ...) \
|
|
XLOG_IF( \
|
|
level, \
|
|
[] { \
|
|
static ::folly::logging::IntervalRateLimiter \
|
|
folly_detail_xlog_limiter((count), std::chrono::milliseconds(ms)); \
|
|
return folly_detail_xlog_limiter.check(); \
|
|
}(), \
|
|
##__VA_ARGS__)
|
|
|
|
namespace folly {
|
|
namespace detail {
|
|
|
|
template <typename Tag>
|
|
FOLLY_EXPORT FOLLY_ALWAYS_INLINE bool xlogFirstNExactImpl(std::size_t n) {
|
|
static std::atomic<std::size_t> counter{0};
|
|
auto const value = counter.load(std::memory_order_relaxed);
|
|
return value < n && counter.fetch_add(1, std::memory_order_relaxed) < n;
|
|
}
|
|
|
|
} // namespace detail
|
|
} // namespace folly
|
|
|
|
/**
|
|
* Similar to XLOG(...) except only log a message the first n times, exactly.
|
|
*
|
|
* The internal counter is process-global and threadsafe and exchanges are
|
|
* atomic.
|
|
*/
|
|
#define XLOG_FIRST_N(level, n, ...) \
|
|
XLOG_IF( \
|
|
level, \
|
|
[&] { \
|
|
struct folly_detail_xlog_tag {}; \
|
|
return ::folly::detail::xlogFirstNExactImpl<folly_detail_xlog_tag>(n); \
|
|
}(), \
|
|
##__VA_ARGS__)
|
|
|
|
/**
|
|
* FOLLY_XLOG_STRIP_PREFIXES can be defined to a string containing a
|
|
* colon-separated list of directory prefixes to strip off from the filename
|
|
* before using it to compute the log category name.
|
|
*
|
|
* If this is defined, use xlogStripFilename() to strip off directory prefixes;
|
|
* otherwise just use __FILE__ literally. xlogStripFilename() is a constexpr
|
|
* expression so that this stripping can be performed fully at compile time.
|
|
* (There is no guarantee that the compiler will evaluate it at compile time,
|
|
* though.)
|
|
*/
|
|
#ifdef FOLLY_XLOG_STRIP_PREFIXES
|
|
#define XLOG_FILENAME \
|
|
folly::xlogStripFilename(__FILE__, FOLLY_XLOG_STRIP_PREFIXES)
|
|
#else
|
|
#define XLOG_FILENAME __FILE__
|
|
#endif
|
|
|
|
#define XLOG_IMPL(level, type, ...) \
|
|
XLOG_ACTUAL_IMPL( \
|
|
level, true, ::folly::isLogLevelFatal(level), type, ##__VA_ARGS__)
|
|
|
|
#define XLOG_IF_IMPL(level, cond, type, ...) \
|
|
XLOG_ACTUAL_IMPL(level, cond, false, type, ##__VA_ARGS__)
|
|
|
|
/**
|
|
* Helper macro used to implement XLOG() and XLOGF()
|
|
*
|
|
* Beware that the level argument is evaluated twice.
|
|
*
|
|
* This macro is somewhat tricky:
|
|
*
|
|
* - In order to support streaming argument support (with the << operator),
|
|
* the macro must expand to a single ternary ? expression. This is the only
|
|
* way we can avoid evaluating the log arguments if the log check fails,
|
|
* and still have the macro behave as expected when used as the body of an if
|
|
* or else statement.
|
|
*
|
|
* - We need to store some static-scope local state in order to track the
|
|
* LogCategory to use. This is a bit tricky to do and still meet the
|
|
* requirements of being a single expression, but fortunately static
|
|
* variables inside a lambda work for this purpose.
|
|
*
|
|
* Inside header files, each XLOG() statement defines to static variables:
|
|
* - the LogLevel for this category
|
|
* - a pointer to the LogCategory
|
|
*
|
|
* If the __INCLUDE_LEVEL__ macro is available (both gcc and clang support
|
|
* this), then we we can detect when we are inside a .cpp file versus a
|
|
* header file. If we are inside a .cpp file, we can avoid declaring these
|
|
* variables once per XLOG() statement, and instead we only declare one copy
|
|
* of these variables for the entire file.
|
|
*
|
|
* - We want to make sure this macro is safe to use even from inside static
|
|
* initialization code that runs before main. We also want to make the log
|
|
* admittance check as cheap as possible, so that disabled debug logs have
|
|
* minimal overhead, and can be left in place even in performance senstive
|
|
* code.
|
|
*
|
|
* In order to do this, we rely on zero-initialization of variables with
|
|
* static storage duration. The LogLevel variable will always be
|
|
* 0-initialized before any code runs. Therefore the very first time an
|
|
* XLOG() statement is hit the initial log level check will always pass
|
|
* (since all level values are greater or equal to 0), and we then do a
|
|
* second check to see if the log level and category variables need to be
|
|
* initialized. On all subsequent calls, disabled log statements can be
|
|
* skipped with just a single check of the LogLevel.
|
|
*/
|
|
#define XLOG_ACTUAL_IMPL(level, cond, always_fatal, type, ...) \
|
|
(!XLOG_IS_ON_IMPL(level) || !(cond)) \
|
|
? ::folly::logDisabledHelper(::folly::bool_constant<always_fatal>{}) \
|
|
: ::folly::LogStreamVoidify<::folly::isLogLevelFatal(level)>{} & \
|
|
::folly::LogStreamProcessor( \
|
|
[] { \
|
|
static ::folly::XlogCategoryInfo<XLOG_IS_IN_HEADER_FILE> \
|
|
folly_detail_xlog_category; \
|
|
return folly_detail_xlog_category.getInfo( \
|
|
&xlog_detail::xlogFileScopeInfo); \
|
|
}(), \
|
|
(level), \
|
|
xlog_detail::getXlogCategoryName(XLOG_FILENAME, 0), \
|
|
xlog_detail::isXlogCategoryOverridden(0), \
|
|
XLOG_FILENAME, \
|
|
__LINE__, \
|
|
__func__, \
|
|
(type), \
|
|
##__VA_ARGS__) \
|
|
.stream()
|
|
|
|
/**
|
|
* Check if an XLOG() statement with the given log level would be enabled.
|
|
*
|
|
* The level parameter must be an unqualified LogLevel enum value.
|
|
*/
|
|
#define XLOG_IS_ON(level) XLOG_IS_ON_IMPL(::folly::LogLevel::level)
|
|
|
|
/**
|
|
* Helper macro to implement of XLOG_IS_ON()
|
|
*
|
|
* This macro is used in the XLOG() implementation, and therefore must be as
|
|
* cheap as possible. It stores the category's LogLevel as a local static
|
|
* variable. The very first time this macro is evaluated it will look up the
|
|
* correct LogCategory and initialize the LogLevel. Subsequent calls then
|
|
* are only a single conditional log level check.
|
|
*
|
|
* The LogCategory object keeps track of this local LogLevel variable and
|
|
* automatically keeps it up-to-date when the category's effective level is
|
|
* changed.
|
|
*
|
|
* See XlogLevelInfo for the implementation details.
|
|
*/
|
|
#define XLOG_IS_ON_IMPL(level) \
|
|
([] { \
|
|
static ::folly::XlogLevelInfo<XLOG_IS_IN_HEADER_FILE> \
|
|
folly_detail_xlog_level; \
|
|
return folly_detail_xlog_level.check( \
|
|
(level), \
|
|
xlog_detail::getXlogCategoryName(XLOG_FILENAME, 0), \
|
|
xlog_detail::isXlogCategoryOverridden(0), \
|
|
&xlog_detail::xlogFileScopeInfo); \
|
|
}())
|
|
|
|
/**
|
|
* Get the name of the log category that will be used by XLOG() statements
|
|
* in this file.
|
|
*/
|
|
#define XLOG_GET_CATEGORY_NAME() \
|
|
(xlog_detail::isXlogCategoryOverridden(0) \
|
|
? xlog_detail::getXlogCategoryName(XLOG_FILENAME, 0) \
|
|
: ::folly::getXlogCategoryNameForFile(XLOG_FILENAME))
|
|
|
|
/**
|
|
* Get a pointer to the LogCategory that will be used by XLOG() statements in
|
|
* this file.
|
|
*
|
|
* This is just a small wrapper around a LoggerDB::getCategory() call.
|
|
* This must be implemented as a macro since it uses __FILE__, and that must
|
|
* expand to the correct filename based on where the macro is used.
|
|
*/
|
|
#define XLOG_GET_CATEGORY() \
|
|
folly::LoggerDB::get().getCategory(XLOG_GET_CATEGORY_NAME())
|
|
|
|
/**
|
|
* XLOG_SET_CATEGORY_NAME() can be used to explicitly define the log category
|
|
* name used by all XLOG() and XLOGF() calls in this translation unit.
|
|
*
|
|
* This overrides the default behavior of picking a category name based on the
|
|
* current filename.
|
|
*
|
|
* This should be used at the top-level scope in a .cpp file, before any XLOG()
|
|
* or XLOGF() macros have been used in the file.
|
|
*
|
|
* XLOG_SET_CATEGORY_NAME() cannot be used inside header files.
|
|
*/
|
|
#ifdef __INCLUDE_LEVEL__
|
|
#define XLOG_SET_CATEGORY_CHECK \
|
|
static_assert( \
|
|
__INCLUDE_LEVEL__ == 0, \
|
|
"XLOG_SET_CATEGORY_NAME() should not be used in header files");
|
|
#else
|
|
#define XLOG_SET_CATEGORY_CHECK
|
|
#endif
|
|
|
|
#define XLOG_SET_CATEGORY_NAME(category) \
|
|
namespace xlog_detail { \
|
|
namespace { \
|
|
XLOG_SET_CATEGORY_CHECK \
|
|
constexpr inline folly::StringPiece getXlogCategoryName( \
|
|
folly::StringPiece, int) { \
|
|
return category; \
|
|
} \
|
|
constexpr inline bool isXlogCategoryOverridden(int) { return true; } \
|
|
} \
|
|
}
|
|
|
|
/**
|
|
* Assert that a condition is true.
|
|
*
|
|
* This crashes the program with an XLOG(FATAL) message if the condition is
|
|
* false. Unlike assert() CHECK statements are always enabled, regardless of
|
|
* the setting of NDEBUG.
|
|
*/
|
|
#define XCHECK(cond, ...) \
|
|
XLOG_IF(FATAL, UNLIKELY(!(cond)), "Check failed: " #cond " ", ##__VA_ARGS__)
|
|
|
|
namespace folly {
|
|
namespace detail {
|
|
template <typename Arg1, typename Arg2, typename CmpFn>
|
|
std::unique_ptr<std::string> XCheckOpImpl(
|
|
folly::StringPiece checkStr,
|
|
const Arg1& arg1,
|
|
const Arg2& arg2,
|
|
CmpFn&& cmp_fn) {
|
|
if (LIKELY(cmp_fn(arg1, arg2))) {
|
|
return nullptr;
|
|
}
|
|
return std::make_unique<std::string>(folly::to<std::string>(
|
|
"Check failed: ",
|
|
checkStr,
|
|
" (",
|
|
::folly::logging::objectToString(arg1),
|
|
" vs. ",
|
|
folly::logging::objectToString(arg2),
|
|
")"));
|
|
}
|
|
} // namespace detail
|
|
} // namespace folly
|
|
|
|
#define XCHECK_OP(op, arg1, arg2, ...) \
|
|
while (auto _folly_logging_check_result = ::folly::detail::XCheckOpImpl( \
|
|
#arg1 " " #op " " #arg2, \
|
|
(arg1), \
|
|
(arg2), \
|
|
[](const auto& _folly_check_arg1, const auto& _folly_check_arg2) \
|
|
-> bool { return _folly_check_arg1 op _folly_check_arg2; })) \
|
|
XLOG(FATAL, *_folly_logging_check_result, ##__VA_ARGS__)
|
|
|
|
/**
|
|
* Assert a comparison relationship between two arguments.
|
|
*
|
|
* If the comparison check fails the values of both expressions being compared
|
|
* will be included in the failure message. This is the main benefit of using
|
|
* these specific comparison macros over XCHECK(). XCHECK() will simply log
|
|
* that the expression evaluated was false, while these macros include the
|
|
* specific values being compared.
|
|
*/
|
|
#define XCHECK_EQ(arg1, arg2, ...) XCHECK_OP(==, arg1, arg2, ##__VA_ARGS__)
|
|
#define XCHECK_NE(arg1, arg2, ...) XCHECK_OP(!=, arg1, arg2, ##__VA_ARGS__)
|
|
#define XCHECK_LT(arg1, arg2, ...) XCHECK_OP(<, arg1, arg2, ##__VA_ARGS__)
|
|
#define XCHECK_GT(arg1, arg2, ...) XCHECK_OP(>, arg1, arg2, ##__VA_ARGS__)
|
|
#define XCHECK_LE(arg1, arg2, ...) XCHECK_OP(<=, arg1, arg2, ##__VA_ARGS__)
|
|
#define XCHECK_GE(arg1, arg2, ...) XCHECK_OP(>=, arg1, arg2, ##__VA_ARGS__)
|
|
|
|
/**
|
|
* Assert that a condition is true in debug builds only.
|
|
*
|
|
* When NDEBUG is not defined this behaves like XCHECK().
|
|
* When NDEBUG is defined XDCHECK statements are not evaluated and will never
|
|
* log.
|
|
*
|
|
* You can use `XLOG_IF(DFATAL, condition)` instead if you want the condition to
|
|
* be evaluated in release builds but log a message without crashing the
|
|
* program.
|
|
*/
|
|
#define XDCHECK(cond, ...) \
|
|
(!::folly::kIsDebug) ? static_cast<void>(0) : XCHECK(cond, ##__VA_ARGS__)
|
|
|
|
/*
|
|
* It would be nice to rely solely on folly::kIsDebug here rather than NDEBUG.
|
|
* However doing so would make the code substantially more complicated. It is
|
|
* much simpler to simply change the definition of XDCHECK_OP() based on NDEBUG.
|
|
*/
|
|
#ifdef NDEBUG
|
|
#define XDCHECK_OP(op, arg1, arg2, ...) \
|
|
while (false) \
|
|
XCHECK_OP(op, arg1, arg2, ##__VA_ARGS__)
|
|
#else
|
|
#define XDCHECK_OP(op, arg1, arg2, ...) XCHECK_OP(op, arg1, arg2, ##__VA_ARGS__)
|
|
#endif
|
|
|
|
/**
|
|
* Assert a comparison relationship between two arguments in debug builds.
|
|
*
|
|
* When NDEBUG is not set these macros behaves like the corresponding
|
|
* XCHECK_XX() versions (XCHECK_EQ(), XCHECK_NE(), etc).
|
|
*
|
|
* When NDEBUG is defined these statements are not evaluated and will never log.
|
|
*/
|
|
#define XDCHECK_EQ(arg1, arg2, ...) XDCHECK_OP(==, arg1, arg2, ##__VA_ARGS__)
|
|
#define XDCHECK_NE(arg1, arg2, ...) XDCHECK_OP(!=, arg1, arg2, ##__VA_ARGS__)
|
|
#define XDCHECK_LT(arg1, arg2, ...) XDCHECK_OP(<, arg1, arg2, ##__VA_ARGS__)
|
|
#define XDCHECK_GT(arg1, arg2, ...) XDCHECK_OP(>, arg1, arg2, ##__VA_ARGS__)
|
|
#define XDCHECK_LE(arg1, arg2, ...) XDCHECK_OP(<=, arg1, arg2, ##__VA_ARGS__)
|
|
#define XDCHECK_GE(arg1, arg2, ...) XDCHECK_OP(>=, arg1, arg2, ##__VA_ARGS__)
|
|
|
|
/**
|
|
* XLOG_IS_IN_HEADER_FILE evaluates to false if we can definitively tell if we
|
|
* are not in a header file. Otherwise, it evaluates to true.
|
|
*/
|
|
#ifdef __INCLUDE_LEVEL__
|
|
#define XLOG_IS_IN_HEADER_FILE bool(__INCLUDE_LEVEL__ > 0)
|
|
#else
|
|
// Without __INCLUDE_LEVEL__ we canot tell if we are in a header file or not,
|
|
// and must pessimstically assume we are always in a header file.
|
|
#define XLOG_IS_IN_HEADER_FILE true
|
|
#endif
|
|
|
|
namespace folly {
|
|
|
|
class XlogFileScopeInfo {
|
|
public:
|
|
#ifdef __INCLUDE_LEVEL__
|
|
std::atomic<::folly::LogLevel> level;
|
|
::folly::LogCategory* category;
|
|
#endif
|
|
};
|
|
|
|
/**
|
|
* A file-static XlogLevelInfo and XlogCategoryInfo object is declared for each
|
|
* XLOG() statement.
|
|
*
|
|
* We intentionally do not provide constructors for these structures, and rely
|
|
* on their members to be zero-initialized when the program starts. This
|
|
* ensures that everything will work as desired even if XLOG() statements are
|
|
* used during dynamic object initialization before main().
|
|
*/
|
|
template <bool IsInHeaderFile>
|
|
class XlogLevelInfo {
|
|
public:
|
|
bool check(
|
|
LogLevel levelToCheck,
|
|
folly::StringPiece categoryName,
|
|
bool isOverridden,
|
|
XlogFileScopeInfo*) {
|
|
// Do an initial relaxed check. If this fails we know the category level
|
|
// is initialized and the log admittance check failed.
|
|
// Use LIKELY() to optimize for the case of disabled debug statements:
|
|
// we disabled debug statements to be cheap. If the log message is
|
|
// enabled then this check will still be minimal perf overhead compared to
|
|
// the overall cost of logging it.
|
|
if (LIKELY(levelToCheck < level_.load(std::memory_order_relaxed))) {
|
|
return false;
|
|
}
|
|
|
|
// If we are still here, then either:
|
|
// - The log level check actually passed, or
|
|
// - level_ has not been initialized yet, and we have to initialize it and
|
|
// then re-perform the check.
|
|
//
|
|
// Do this work in a separate helper method. It is intentionally defined
|
|
// in the xlog.cpp file to avoid inlining, to reduce the amount of code
|
|
// emitted for each XLOG() statement.
|
|
auto currentLevel = loadLevelFull(categoryName, isOverridden);
|
|
return levelToCheck >= currentLevel;
|
|
}
|
|
|
|
private:
|
|
LogLevel loadLevelFull(folly::StringPiece categoryName, bool isOverridden);
|
|
|
|
// XlogLevelInfo objects are always defined with static storage.
|
|
// This member will always be zero-initialized on program start.
|
|
std::atomic<LogLevel> level_;
|
|
};
|
|
|
|
template <bool IsInHeaderFile>
|
|
class XlogCategoryInfo {
|
|
public:
|
|
bool isInitialized() const {
|
|
return isInitialized_.load(std::memory_order_acquire);
|
|
}
|
|
|
|
LogCategory* init(folly::StringPiece categoryName, bool isOverridden);
|
|
|
|
LogCategory* getCategory(XlogFileScopeInfo*) { return category_; }
|
|
|
|
/**
|
|
* Get a pointer to pass into the LogStreamProcessor constructor,
|
|
* so that it is able to look up the LogCategory information.
|
|
*/
|
|
XlogCategoryInfo<IsInHeaderFile>* getInfo(XlogFileScopeInfo*) { return this; }
|
|
|
|
private:
|
|
// These variables will always be zero-initialized on program start.
|
|
std::atomic<bool> isInitialized_;
|
|
LogCategory* category_;
|
|
};
|
|
|
|
#ifdef __INCLUDE_LEVEL__
|
|
/**
|
|
* Specialization of XlogLevelInfo for XLOG() statements in the .cpp file being
|
|
* compiled. In this case we only define a single file-static LogLevel object
|
|
* for the entire file, rather than defining one for each XLOG() statement.
|
|
*/
|
|
template <>
|
|
class XlogLevelInfo<false> {
|
|
public:
|
|
static bool check(
|
|
LogLevel levelToCheck,
|
|
folly::StringPiece categoryName,
|
|
bool isOverridden,
|
|
XlogFileScopeInfo* fileScopeInfo) {
|
|
// As above in the non-specialized XlogFileScopeInfo code, do a simple
|
|
// relaxed check first.
|
|
if (LIKELY(
|
|
levelToCheck <
|
|
fileScopeInfo->level.load(::std::memory_order_relaxed))) {
|
|
return false;
|
|
}
|
|
|
|
// If we are still here we the file-scope log level either needs to be
|
|
// initalized, or the log level check legitimately passed.
|
|
auto currentLevel =
|
|
loadLevelFull(categoryName, isOverridden, fileScopeInfo);
|
|
return levelToCheck >= currentLevel;
|
|
}
|
|
|
|
private:
|
|
static LogLevel loadLevelFull(
|
|
folly::StringPiece categoryName,
|
|
bool isOverridden,
|
|
XlogFileScopeInfo* fileScopeInfo);
|
|
};
|
|
|
|
/**
|
|
* Specialization of XlogCategoryInfo for XLOG() statements in the .cpp file
|
|
* being compiled. In this case we only define a single file-static LogLevel
|
|
* object for the entire file, rather than defining one for each XLOG()
|
|
* statement.
|
|
*/
|
|
template <>
|
|
class XlogCategoryInfo<false> {
|
|
public:
|
|
/**
|
|
* Get a pointer to pass into the LogStreamProcessor constructor,
|
|
* so that it is able to look up the LogCategory information.
|
|
*/
|
|
XlogFileScopeInfo* getInfo(XlogFileScopeInfo* fileScopeInfo) {
|
|
return fileScopeInfo;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
/**
|
|
* Get the default XLOG() category name for the given filename.
|
|
*
|
|
* This function returns the category name that will be used by XLOG() if
|
|
* XLOG_SET_CATEGORY_NAME() has not been used.
|
|
*/
|
|
folly::StringPiece getXlogCategoryNameForFile(folly::StringPiece filename);
|
|
|
|
constexpr bool xlogIsDirSeparator(char c) {
|
|
return c == '/' || (kIsWindows && c == '\\');
|
|
}
|
|
|
|
namespace detail {
|
|
constexpr const char* xlogStripFilenameRecursive(
|
|
const char* filename,
|
|
const char* prefixes,
|
|
size_t prefixIdx,
|
|
size_t filenameIdx,
|
|
bool match);
|
|
constexpr const char* xlogStripFilenameMatchFound(
|
|
const char* filename,
|
|
const char* prefixes,
|
|
size_t prefixIdx,
|
|
size_t filenameIdx) {
|
|
return (filename[filenameIdx] == '\0')
|
|
? xlogStripFilenameRecursive(filename, prefixes, prefixIdx + 1, 0, true)
|
|
: (xlogIsDirSeparator(filename[filenameIdx])
|
|
? xlogStripFilenameMatchFound(
|
|
filename, prefixes, prefixIdx, filenameIdx + 1)
|
|
: (filename + filenameIdx));
|
|
}
|
|
constexpr const char* xlogStripFilenameRecursive(
|
|
const char* filename,
|
|
const char* prefixes,
|
|
size_t prefixIdx,
|
|
size_t filenameIdx,
|
|
bool match) {
|
|
// This would be much easier to understand if written as a while loop.
|
|
// However, in order to maintain compatibility with pre-C++14 compilers we
|
|
// have implemented it recursively to adhere to C++11 restrictions for
|
|
// constexpr functions.
|
|
return ((prefixes[prefixIdx] == ':' && filename[filenameIdx] != ':') ||
|
|
prefixes[prefixIdx] == '\0')
|
|
? ((match && filenameIdx > 0 &&
|
|
(xlogIsDirSeparator(prefixes[filenameIdx - 1]) ||
|
|
xlogIsDirSeparator(filename[filenameIdx])))
|
|
? (xlogStripFilenameMatchFound(
|
|
filename, prefixes, prefixIdx, filenameIdx))
|
|
: ((prefixes[prefixIdx] == '\0')
|
|
? filename
|
|
: xlogStripFilenameRecursive(
|
|
filename, prefixes, prefixIdx + 1, 0, true)))
|
|
: ((match &&
|
|
((prefixes[prefixIdx] == filename[filenameIdx]) ||
|
|
(xlogIsDirSeparator(prefixes[prefixIdx]) &&
|
|
xlogIsDirSeparator(filename[filenameIdx]))))
|
|
? xlogStripFilenameRecursive(
|
|
filename, prefixes, prefixIdx + 1, filenameIdx + 1, true)
|
|
: xlogStripFilenameRecursive(
|
|
filename, prefixes, prefixIdx + 1, 0, false));
|
|
}
|
|
} // namespace detail
|
|
|
|
/**
|
|
* Strip directory prefixes from a filename before using it in XLOG macros.
|
|
*
|
|
* This is primarily used to strip off the initial project directory path for
|
|
* projects that invoke the compiler with absolute path names.
|
|
*
|
|
* The filename argument is the filename to process. This is normally the
|
|
* contents of the __FILE__ macro from the invoking file.
|
|
*
|
|
* prefixes is a colon-separated list of directory prefixes to strip off if
|
|
* present at the beginning of the filename. The prefix list is searched in
|
|
* order, and only the first match found will be stripped.
|
|
*
|
|
* e.g., xlogStripFilename("/my/project/src/foo.cpp", "/tmp:/my/project")
|
|
* would return "src/foo.cpp"
|
|
*/
|
|
constexpr const char* xlogStripFilename(
|
|
const char* filename, const char* prefixes) {
|
|
return detail::xlogStripFilenameRecursive(filename, prefixes, 0, 0, true);
|
|
}
|
|
} // namespace folly
|
|
|
|
/*
|
|
* We intentionally use an unnamed namespace inside a header file here.
|
|
*
|
|
* We want each .cpp file that uses xlog.h to get its own separate
|
|
* implementation of the following functions and variables.
|
|
*/
|
|
namespace xlog_detail {
|
|
namespace {
|
|
/**
|
|
* The default getXlogCategoryName() function.
|
|
*
|
|
* By default this simply returns the filename argument passed in.
|
|
* The default isXlogCategoryOverridden() function returns false, indicating
|
|
* that the return value from getXlogCategoryName() needs to be converted
|
|
* using getXlogCategoryNameForFile().
|
|
*
|
|
* These are two separate steps because getXlogCategoryName() itself needs to
|
|
* remain constexpr--it is always evaluated in XLOG() statements, but we only
|
|
* want to call getXlogCategoryNameForFile() the very first time through, when
|
|
* we have to initialize the LogCategory object.
|
|
*
|
|
* This is a template function purely so that XLOG_SET_CATEGORY_NAME() can
|
|
* define a more specific version of this function that will take precedence
|
|
* over this one.
|
|
*/
|
|
template <typename T>
|
|
constexpr inline folly::StringPiece getXlogCategoryName(
|
|
folly::StringPiece filename, T) {
|
|
return filename;
|
|
}
|
|
|
|
/**
|
|
* The default isXlogCategoryOverridden() function.
|
|
*
|
|
* This returns false indicating that the category name has not been
|
|
* overridden, so getXlogCategoryName() returns a raw filename that needs
|
|
* to be translated with getXlogCategoryNameForFile().
|
|
*
|
|
* This is a template function purely so that XLOG_SET_CATEGORY_NAME() can
|
|
* define a more specific version of this function that will take precedence
|
|
* over this one.
|
|
*/
|
|
template <typename T>
|
|
constexpr inline bool isXlogCategoryOverridden(T) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* File-scope LogLevel and LogCategory data for XLOG() statements,
|
|
* if __INCLUDE_LEVEL__ is supported.
|
|
*
|
|
* This allows us to only have one LogLevel and LogCategory pointer for the
|
|
* entire .cpp file, rather than needing a separate copy for each XLOG()
|
|
* statement.
|
|
*/
|
|
::folly::XlogFileScopeInfo xlogFileScopeInfo;
|
|
} // namespace
|
|
} // namespace xlog_detail
|