/ src / util / log.h
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  // 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  enum class Level {
 47      Trace = 0, // High-volume or detailed logging for development/debugging
 48      Debug,     // Reasonably noisy logging, but still usable in production
 49      Info,      // Default
 50      Warning,
 51      Error,
 52  };
 53  
 54  struct Entry {
 55      Category category;
 56      Level level;
 57      bool should_ratelimit{false}; //!< Hint for consumers if this entry should be ratelimited
 58      SystemClock::time_point timestamp{SystemClock::now()};
 59      std::chrono::seconds mocktime{GetMockTime()};
 60      std::string thread_name{util::ThreadGetInternalName()};
 61      SourceLocation source_loc;
 62      std::string message;
 63  };
 64  
 65  /** Return whether messages with specified category and level should be logged. Applications using
 66   * the logging library need to provide this. */
 67  bool ShouldLog(Category category, Level level);
 68  
 69  /** Send message to be logged. Applications using the logging library need to provide this. */
 70  void Log(Entry entry);
 71  } // namespace util::log
 72  
 73  namespace BCLog {
 74  //! Alias for compatibility. Prefer util::log::Level over BCLog::Level in new code.
 75  using Level = util::log::Level;
 76  } // namespace BCLog
 77  
 78  template <typename... Args>
 79  inline void LogPrintFormatInternal(SourceLocation&& source_loc, BCLog::LogFlags flag, BCLog::Level level, bool should_ratelimit, util::ConstevalFormatString<sizeof...(Args)> fmt, const Args&... args)
 80  {
 81      std::string log_msg;
 82      try {
 83          log_msg = tfm::format(fmt, args...);
 84      } catch (tinyformat::format_error& fmterr) {
 85          log_msg = "Error \"" + std::string{fmterr.what()} + "\" while formatting log message: " + fmt.fmt;
 86      }
 87      util::log::Log(util::log::Entry{
 88          .category = flag,
 89          .level = level,
 90          .should_ratelimit = should_ratelimit,
 91          .source_loc = std::move(source_loc),
 92          .message = std::move(log_msg)});
 93  }
 94  
 95  // Allow __func__ to be used in any context without warnings:
 96  // NOLINTNEXTLINE(bugprone-lambda-function-name)
 97  #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(SourceLocation{__func__}, category, level, should_ratelimit, __VA_ARGS__)
 98  
 99  // Log unconditionally. Uses basic rate limiting to mitigate disk filling attacks.
100  // Be conservative when using functions that unconditionally log to debug.log!
101  // It should not be the case that an inbound peer can fill up a user's storage
102  // with debug.log entries.
103  #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__)
104  #define LogWarning(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Warning, /*should_ratelimit=*/true, __VA_ARGS__)
105  #define LogError(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Error, /*should_ratelimit=*/true, __VA_ARGS__)
106  
107  // Use a macro instead of a function for conditional logging to prevent
108  // evaluating arguments when logging for the category is not enabled.
109  
110  // Log by prefixing the output with the passed category name and severity level. This logs conditionally if
111  // the category is allowed. No rate limiting is applied, because users specifying -debug are assumed to be
112  // developers or power users who are aware that -debug may cause excessive disk usage due to logging.
113  #define detail_LogIfCategoryAndLevelEnabled(category, level, ...)      \
114      do {                                                               \
115          if (util::log::ShouldLog((category), (level))) {               \
116              bool rate_limit{level >= BCLog::Level::Info};              \
117              Assume(!rate_limit); /*Only called with the levels below*/ \
118              LogPrintLevel_(category, level, rate_limit, __VA_ARGS__);  \
119          }                                                              \
120      } while (0)
121  
122  // Log conditionally, prefixing the output with the passed category name.
123  #define LogDebug(category, ...) detail_LogIfCategoryAndLevelEnabled(category, BCLog::Level::Debug, __VA_ARGS__)
124  #define LogTrace(category, ...) detail_LogIfCategoryAndLevelEnabled(category, BCLog::Level::Trace, __VA_ARGS__)
125  
126  #endif // BITCOIN_UTIL_LOG_H