// 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 // IWYU pragma: export #include #include #include #include #include #include /// 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 inline void LogPrintFormatInternal(SourceLocation&& source_loc, BCLog::LogFlags flag, BCLog::Level level, bool should_ratelimit, util::ConstevalFormatString 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