log.h raw

   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