mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-03 02:06:18 +00:00
With this change, callers can use util/log.h to emit log messages and do not need to include the full logging implementation in logging.h. There's a potential performance impact with this change from an extra `strprintf` call in log statements where `Logger::WillLogCategoryLevel` returns true but `Logger::Enabled` returns false. This happens when bitcoind is run with `-noprinttoconsole -nodebuglogfile` options. For background, log macro arguments are supposed to be evaluated when `Logger::WillLogCategoryLevel` returns true, even if log output is not enabled. Changing this behavior would be reasonable but needs consideration in a separate PR since not evaluating arguments in log statements has the potential to change non-logging behavior. The extra `strprintf` call could have been avoided by expanding this change and making the `ShouldLog()` function return a tri-state DO_LOG / DO_NOT_LOG / DO_NOT_LOG_ONLY_EVALUATE_ARGS value instead of a bool, but this complexity did not seem warranted. Review with --color-moved=dimmed-zebra --color-moved-ws=ignore-all-space Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
119 lines
5.0 KiB
C++
119 lines
5.0 KiB
C++
// Copyright (c) The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef BITCOIN_UTIL_LOG_H
|
|
#define BITCOIN_UTIL_LOG_H
|
|
|
|
#include <logging/categories.h> // IWYU pragma: export
|
|
#include <tinyformat.h>
|
|
#include <util/check.h>
|
|
|
|
#include <cstdint>
|
|
#include <source_location>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
/// Like std::source_location, but allowing to override the function name.
|
|
class SourceLocation
|
|
{
|
|
public:
|
|
/// The func argument must be constructed from the C++11 __func__ macro.
|
|
/// Ref: https://en.cppreference.com/w/cpp/language/function.html#func
|
|
/// Non-static string literals are not supported.
|
|
SourceLocation(const char* func,
|
|
std::source_location loc = std::source_location::current())
|
|
: m_func{func}, m_loc{loc} {}
|
|
|
|
std::string_view file_name() const { return m_loc.file_name(); }
|
|
std::uint_least32_t line() const { return m_loc.line(); }
|
|
std::string_view function_name_short() const { return m_func; }
|
|
|
|
private:
|
|
std::string_view m_func;
|
|
std::source_location m_loc;
|
|
};
|
|
|
|
namespace util::log {
|
|
/** Opaque to util::log; interpreted by consumers (e.g., BCLog::LogFlags). */
|
|
using Category = uint64_t;
|
|
|
|
enum class Level {
|
|
Trace = 0, // High-volume or detailed logging for development/debugging
|
|
Debug, // Reasonably noisy logging, but still usable in production
|
|
Info, // Default
|
|
Warning,
|
|
Error,
|
|
};
|
|
|
|
struct Entry {
|
|
Category category;
|
|
Level level;
|
|
bool should_ratelimit{false}; //!< Hint for consumers if this entry should be ratelimited
|
|
SourceLocation source_loc;
|
|
std::string message;
|
|
};
|
|
|
|
/** Return whether messages with specified category and level should be logged. Applications using
|
|
* the logging library need to provide this. */
|
|
bool ShouldLog(Category category, Level level);
|
|
|
|
/** Send message to be logged. Applications using the logging library need to provide this. */
|
|
void Log(Entry entry);
|
|
} // namespace util::log
|
|
|
|
namespace BCLog {
|
|
//! Alias for compatibility. Prefer util::log::Level over BCLog::Level in new code.
|
|
using Level = util::log::Level;
|
|
} // namespace BCLog
|
|
|
|
template <typename... Args>
|
|
inline void LogPrintFormatInternal(SourceLocation&& source_loc, BCLog::LogFlags flag, BCLog::Level level, bool should_ratelimit, util::ConstevalFormatString<sizeof...(Args)> fmt, const Args&... args)
|
|
{
|
|
std::string log_msg;
|
|
try {
|
|
log_msg = tfm::format(fmt, args...);
|
|
} catch (tinyformat::format_error& fmterr) {
|
|
log_msg = "Error \"" + std::string{fmterr.what()} + "\" while formatting log message: " + fmt.fmt;
|
|
}
|
|
util::log::Log(util::log::Entry{
|
|
.category = flag,
|
|
.level = level,
|
|
.should_ratelimit = should_ratelimit,
|
|
.source_loc = std::move(source_loc),
|
|
.message = std::move(log_msg)});
|
|
}
|
|
|
|
// Allow __func__ to be used in any context without warnings:
|
|
// NOLINTNEXTLINE(bugprone-lambda-function-name)
|
|
#define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(SourceLocation{__func__}, category, level, should_ratelimit, __VA_ARGS__)
|
|
|
|
// Log unconditionally. Uses basic rate limiting to mitigate disk filling attacks.
|
|
// Be conservative when using functions that unconditionally log to debug.log!
|
|
// It should not be the case that an inbound peer can fill up a user's storage
|
|
// with debug.log entries.
|
|
#define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__)
|
|
#define LogWarning(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Warning, /*should_ratelimit=*/true, __VA_ARGS__)
|
|
#define LogError(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Error, /*should_ratelimit=*/true, __VA_ARGS__)
|
|
|
|
// Use a macro instead of a function for conditional logging to prevent
|
|
// evaluating arguments when logging for the category is not enabled.
|
|
|
|
// Log by prefixing the output with the passed category name and severity level. This logs conditionally if
|
|
// the category is allowed. No rate limiting is applied, because users specifying -debug are assumed to be
|
|
// developers or power users who are aware that -debug may cause excessive disk usage due to logging.
|
|
#define detail_LogIfCategoryAndLevelEnabled(category, level, ...) \
|
|
do { \
|
|
if (util::log::ShouldLog((category), (level))) { \
|
|
bool rate_limit{level >= BCLog::Level::Info}; \
|
|
Assume(!rate_limit); /*Only called with the levels below*/ \
|
|
LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \
|
|
} \
|
|
} while (0)
|
|
|
|
// Log conditionally, prefixing the output with the passed category name.
|
|
#define LogDebug(category, ...) detail_LogIfCategoryAndLevelEnabled(category, BCLog::Level::Debug, __VA_ARGS__)
|
|
#define LogTrace(category, ...) detail_LogIfCategoryAndLevelEnabled(category, BCLog::Level::Trace, __VA_ARGS__)
|
|
|
|
#endif // BITCOIN_UTIL_LOG_H
|