log.h
1 // Copyright (c) The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 5 #ifndef BITCOIN_UTIL_LOG_H 6 #define BITCOIN_UTIL_LOG_H 7 8 #include <logging/categories.h> // IWYU pragma: export 9 #include <tinyformat.h> 10 #include <util/check.h> 11 12 #include <cstdint> 13 #include <source_location> 14 #include <string> 15 #include <string_view> 16 17 /// Like std::source_location, but allowing to override the function name. 18 class SourceLocation 19 { 20 public: 21 /// The func argument must be constructed from the C++11 __func__ macro. 22 /// Ref: https://en.cppreference.com/w/cpp/language/function.html#func 23 /// Non-static string literals are not supported. 24 SourceLocation(const char* func, 25 std::source_location loc = std::source_location::current()) 26 : m_func{func}, m_loc{loc} {} 27 28 std::string_view file_name() const { return m_loc.file_name(); } 29 std::uint_least32_t line() const { return m_loc.line(); } 30 std::string_view function_name_short() const { return m_func; } 31 32 private: 33 std::string_view m_func; 34 std::source_location m_loc; 35 }; 36 37 namespace util::log { 38 /** Opaque to util::log; interpreted by consumers (e.g., BCLog::LogFlags). */ 39 using Category = uint64_t; 40 41 enum class Level { 42 Trace = 0, // High-volume or detailed logging for development/debugging 43 Debug, // Reasonably noisy logging, but still usable in production 44 Info, // Default 45 Warning, 46 Error, 47 }; 48 49 struct Entry { 50 Category category; 51 Level level; 52 bool should_ratelimit{false}; //!< Hint for consumers if this entry should be ratelimited 53 SourceLocation source_loc; 54 std::string message; 55 }; 56 57 /** Return whether messages with specified category and level should be logged. Applications using 58 * the logging library need to provide this. */ 59 bool ShouldLog(Category category, Level level); 60 61 /** Send message to be logged. Applications using the logging library need to provide this. */ 62 void Log(Entry entry); 63 } // namespace util::log 64 65 namespace BCLog { 66 //! Alias for compatibility. Prefer util::log::Level over BCLog::Level in new code. 67 using Level = util::log::Level; 68 } // namespace BCLog 69 70 template <typename... Args> 71 inline void LogPrintFormatInternal(SourceLocation&& source_loc, BCLog::LogFlags flag, BCLog::Level level, bool should_ratelimit, util::ConstevalFormatString<sizeof...(Args)> fmt, const Args&... args) 72 { 73 std::string log_msg; 74 try { 75 log_msg = tfm::format(fmt, args...); 76 } catch (tinyformat::format_error& fmterr) { 77 log_msg = "Error \"" + std::string{fmterr.what()} + "\" while formatting log message: " + fmt.fmt; 78 } 79 util::log::Log(util::log::Entry{ 80 .category = flag, 81 .level = level, 82 .should_ratelimit = should_ratelimit, 83 .source_loc = std::move(source_loc), 84 .message = std::move(log_msg)}); 85 } 86 87 // Allow __func__ to be used in any context without warnings: 88 // NOLINTNEXTLINE(bugprone-lambda-function-name) 89 #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(SourceLocation{__func__}, category, level, should_ratelimit, __VA_ARGS__) 90 91 // Log unconditionally. Uses basic rate limiting to mitigate disk filling attacks. 92 // Be conservative when using functions that unconditionally log to debug.log! 93 // It should not be the case that an inbound peer can fill up a user's storage 94 // with debug.log entries. 95 #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) 96 #define LogWarning(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Warning, /*should_ratelimit=*/true, __VA_ARGS__) 97 #define LogError(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Error, /*should_ratelimit=*/true, __VA_ARGS__) 98 99 // Use a macro instead of a function for conditional logging to prevent 100 // evaluating arguments when logging for the category is not enabled. 101 102 // Log by prefixing the output with the passed category name and severity level. This logs conditionally if 103 // the category is allowed. No rate limiting is applied, because users specifying -debug are assumed to be 104 // developers or power users who are aware that -debug may cause excessive disk usage due to logging. 105 #define detail_LogIfCategoryAndLevelEnabled(category, level, ...) \ 106 do { \ 107 if (util::log::ShouldLog((category), (level))) { \ 108 bool rate_limit{level >= BCLog::Level::Info}; \ 109 Assume(!rate_limit); /*Only called with the levels below*/ \ 110 LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ 111 } \ 112 } while (0) 113 114 // Log conditionally, prefixing the output with the passed category name. 115 #define LogDebug(category, ...) detail_LogIfCategoryAndLevelEnabled(category, BCLog::Level::Debug, __VA_ARGS__) 116 #define LogTrace(category, ...) detail_LogIfCategoryAndLevelEnabled(category, BCLog::Level::Trace, __VA_ARGS__) 117 118 #endif // BITCOIN_UTIL_LOG_H