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 // This header works in tandem with `logging/categories.h`
9 // to expose the complete logging interface.
10 #include <logging/categories.h> // IWYU pragma: export
11 #include <tinyformat.h>
12 #include <util/check.h>
13 #include <util/threadnames.h>
14 #include <util/time.h>
15
16 #include <cstdint>
17 #include <source_location>
18 #include <string>
19 #include <string_view>
20
21 /// Like std::source_location, but allowing to override the function name.
22 class SourceLocation
23 {
24 public:
25 /// The func argument must be constructed from the C++11 __func__ macro.
26 /// Ref: https://en.cppreference.com/w/cpp/language/function.html#func
27 /// Non-static string literals are not supported.
28 explicit SourceLocation(
29 const char* func,
30 std::source_location loc = std::source_location::current())
31 : m_func{func}, m_loc{loc} {}
32
33 std::string_view file_name() const { return m_loc.file_name(); }
34 std::uint_least32_t line() const { return m_loc.line(); }
35 std::string_view function_name_short() const { return m_func; }
36
37 private:
38 std::string_view m_func;
39 std::source_location m_loc;
40 };
41
42 namespace util::log {
43 /** Opaque to util::log; interpreted by consumers (e.g., BCLog::LogFlags). */
44 using Category = uint64_t;
45
46 //! Structure and constant for tagging not to rate limit.
47 struct NoRateLimitTag {
48 explicit NoRateLimitTag() = default;
49 };
50 inline constexpr NoRateLimitTag NO_RATE_LIMIT{};
51
52 enum class Level {
53 Trace = 0, // High-volume or detailed logging for development/debugging
54 Debug, // Reasonably noisy logging, but still usable in production
55 Info, // Default
56 Warning,
57 Error,
58 };
59
60 struct Entry {
61 Category category;
62 Level level;
63 bool should_ratelimit{false}; //!< Hint for consumers if this entry should be ratelimited
64 SystemClock::time_point timestamp{SystemClock::now()};
65 std::chrono::seconds mocktime{GetMockTime()};
66 std::string thread_name{util::ThreadGetInternalName()};
67 SourceLocation source_loc;
68 std::string message;
69 };
70
71 /// Return whether messages with specified category should be debug logged.
72 /// Applications using the logging library need to provide this.
73 bool ShouldDebugLog(Category category);
74
75 /// Return whether messages with specified category should be trace logged.
76 /// Applications using the logging library need to provide this.
77 bool ShouldTraceLog(Category category);
78
79 /** Send message to be logged. Applications using the logging library need to provide this. */
80 void Log(Entry entry);
81
82 template <typename... Args>
83 inline void LogPrintFormatInternal_(SourceLocation&& source_loc, BCLog::LogFlags flag, util::log::Level level, bool should_ratelimit, util::ConstevalFormatString<sizeof...(Args)> fmt, const Args&... args)
84 {
85 std::string log_msg;
86 try {
87 log_msg = tfm::format(fmt, args...);
88 } catch (tinyformat::format_error& fmterr) {
89 log_msg = "Error \"" + std::string{fmterr.what()} + "\" while formatting log message: " + fmt.fmt;
90 }
91 util::log::Log(util::log::Entry{
92 .category = flag,
93 .level = level,
94 .should_ratelimit = should_ratelimit,
95 .source_loc = std::move(source_loc),
96 .message = std::move(log_msg)});
97 }
98
99 template <typename... Args>
100 inline void LogPrintFormatInternal(SourceLocation&& source_loc, BCLog::LogFlags flag, util::log::Level level, util::ConstevalFormatString<sizeof...(Args)> fmt, const Args&... args)
101 {
102 return LogPrintFormatInternal_(std::move(source_loc), flag, level, /*should_ratelimit=*/true, fmt, args...);
103 }
104
105 template <typename... Args>
106 inline void LogPrintFormatInternal(SourceLocation&& source_loc, BCLog::LogFlags flag, util::log::Level level, util::log::NoRateLimitTag, util::ConstevalFormatString<sizeof...(Args)> fmt, const Args&... args)
107 {
108 return LogPrintFormatInternal_(std::move(source_loc), flag, level, /*should_ratelimit=*/false, fmt, args...);
109 }
110 } // namespace util::log
111
112 namespace BCLog {
113 //! Alias for compatibility. Prefer util::log::Level over BCLog::Level in new code.
114 using Level = util::log::Level;
115 } // namespace BCLog
116
117 // Allow __func__ to be used in any context without warnings:
118 // NOLINTNEXTLINE(bugprone-lambda-function-name)
119 #define detail_LogWithSrcLoc(category, level, ...) util::log::LogPrintFormatInternal(SourceLocation{__func__}, category, level, __VA_ARGS__)
120
121 // Log unconditionally. Uses basic rate limiting to mitigate disk filling attacks.
122 // Be conservative when using functions that unconditionally log to debug.log!
123 // It should not be the case that an inbound peer can fill up a user's storage
124 // with debug.log entries.
125 #define LogInfo(...) detail_LogWithSrcLoc(BCLog::LogFlags::ALL, util::log::Level::Info, __VA_ARGS__)
126 #define LogWarning(...) detail_LogWithSrcLoc(BCLog::LogFlags::ALL, util::log::Level::Warning, __VA_ARGS__)
127 #define LogError(...) detail_LogWithSrcLoc(BCLog::LogFlags::ALL, util::log::Level::Error, __VA_ARGS__)
128
129 // Use a macro instead of a function for conditional logging to prevent
130 // evaluating arguments when logging for the category is not enabled.
131
132 // Log by prefixing the output with the passed category name and severity level. This logs conditionally if
133 // the category is allowed. No rate limiting is applied, because users specifying -debug are assumed to be
134 // developers or power users who are aware that -debug may cause excessive disk usage due to logging.
135 #define detail_LogIfCategoryAndLevelEnabled(category, shouldlog, level, ...) \
136 do { \
137 if (shouldlog(category)) { \
138 detail_LogWithSrcLoc((category), (level), util::log::NO_RATE_LIMIT, __VA_ARGS__); \
139 } \
140 } while (0)
141
142 // Log conditionally, prefixing the output with the passed category name.
143 #define LogDebug(category, ...) detail_LogIfCategoryAndLevelEnabled(category, util::log::ShouldDebugLog, util::log::Level::Debug, __VA_ARGS__)
144 #define LogTrace(category, ...) detail_LogIfCategoryAndLevelEnabled(category, util::log::ShouldTraceLog, util::log::Level::Trace, __VA_ARGS__)
145
146 #endif // BITCOIN_UTIL_LOG_H
147