logging.h raw

   1  // Copyright (c) 2009-2010 Satoshi Nakamoto
   2  // Copyright (c) 2009-present The Bitcoin Core developers
   3  // Distributed under the MIT software license, see the accompanying
   4  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  
   6  #ifndef BITCOIN_LOGGING_H
   7  #define BITCOIN_LOGGING_H
   8  
   9  #include <crypto/siphash.h>
  10  #include <logging/categories.h> // IWYU pragma: export
  11  #include <span.h>
  12  #include <util/byte_units.h>
  13  #include <util/fs.h>
  14  #include <util/log.h> // IWYU pragma: export
  15  #include <util/stdmutex.h>
  16  #include <util/string.h>
  17  #include <util/time.h>
  18  
  19  #include <atomic>
  20  #include <cstdint>
  21  #include <cstring>
  22  #include <functional>
  23  #include <list>
  24  #include <memory>
  25  #include <optional>
  26  #include <string>
  27  #include <unordered_map>
  28  #include <vector>
  29  
  30  static const bool DEFAULT_LOGTIMEMICROS = false;
  31  static const bool DEFAULT_LOGIPS        = false;
  32  static const bool DEFAULT_LOGTIMESTAMPS = true;
  33  static const bool DEFAULT_LOGTHREADNAMES = false;
  34  static const bool DEFAULT_LOGSOURCELOCATIONS = false;
  35  static constexpr bool DEFAULT_LOGLEVELALWAYS = false;
  36  extern const char * const DEFAULT_DEBUGLOGFILE;
  37  
  38  extern bool fLogIPs;
  39  
  40  struct SourceLocationEqual {
  41      bool operator()(const SourceLocation& lhs, const SourceLocation& rhs) const noexcept
  42      {
  43          return lhs.line() == rhs.line() && std::string_view(lhs.file_name()) == std::string_view(rhs.file_name());
  44      }
  45  };
  46  
  47  struct SourceLocationHasher {
  48      size_t operator()(const SourceLocation& s) const noexcept
  49      {
  50          // Use CSipHasher(0, 0) as a simple way to get uniform distribution.
  51          return size_t(CSipHasher(0, 0)
  52                        .Write(s.line())
  53                        .Write(MakeUCharSpan(std::string_view{s.file_name()}))
  54                        .Finalize());
  55      }
  56  };
  57  
  58  struct LogCategory {
  59      std::string category;
  60      bool active;
  61  };
  62  
  63  namespace BCLog {
  64      constexpr auto DEFAULT_LOG_LEVEL{Level::Debug};
  65      constexpr size_t DEFAULT_MAX_LOG_BUFFER{1'000'000}; // buffer up to 1MB of log data prior to StartLogging
  66      constexpr uint64_t RATELIMIT_MAX_BYTES{1_MiB}; // maximum number of bytes per source location that can be logged within the RATELIMIT_WINDOW
  67      constexpr auto RATELIMIT_WINDOW{1h}; // time window after which log ratelimit stats are reset
  68      constexpr bool DEFAULT_LOGRATELIMIT{true};
  69  
  70      //! Fixed window rate limiter for logging.
  71      class LogRateLimiter
  72      {
  73      public:
  74          //! Keeps track of an individual source location and how many available bytes are left for logging from it.
  75          struct Stats {
  76              //! Remaining bytes
  77              uint64_t m_available_bytes;
  78              //! Number of bytes that were consumed but didn't fit in the available bytes.
  79              uint64_t m_dropped_bytes{0};
  80  
  81              Stats(uint64_t max_bytes) : m_available_bytes{max_bytes} {}
  82              //! Updates internal accounting and returns true if enough available_bytes were remaining
  83              bool Consume(uint64_t bytes);
  84          };
  85  
  86      private:
  87          mutable StdMutex m_mutex;
  88  
  89          //! Stats for each source location that has attempted to log something.
  90          std::unordered_map<SourceLocation, Stats, SourceLocationHasher, SourceLocationEqual> m_source_locations GUARDED_BY(m_mutex);
  91          //! Whether any log locations are suppressed. Cached view on m_source_locations for performance reasons.
  92          std::atomic<bool> m_suppression_active{false};
  93          LogRateLimiter(uint64_t max_bytes, std::chrono::seconds reset_window);
  94  
  95      public:
  96          using SchedulerFunction = std::function<void(std::function<void()>, std::chrono::milliseconds)>;
  97          /**
  98           * @param scheduler_func    Callable object used to schedule resetting the window. The first
  99           *                          parameter is the function to be executed, and the second is the
 100           *                          reset_window interval.
 101           * @param max_bytes         Maximum number of bytes that can be logged for each source
 102           *                          location.
 103           * @param reset_window      Time window after which the stats are reset.
 104           */
 105          static std::shared_ptr<LogRateLimiter> Create(
 106              SchedulerFunction&& scheduler_func,
 107              uint64_t max_bytes,
 108              std::chrono::seconds reset_window);
 109          //! Maximum number of bytes logged per location per window.
 110          const uint64_t m_max_bytes;
 111          //! Interval after which the window is reset.
 112          const std::chrono::seconds m_reset_window;
 113          //! Suppression status of a source log location.
 114          enum class Status {
 115              UNSUPPRESSED,     // string fits within the limit
 116              NEWLY_SUPPRESSED, // suppression has started since this string
 117              STILL_SUPPRESSED, // suppression is still ongoing
 118          };
 119          //! Consumes `source_loc`'s available bytes corresponding to the size of the (formatted)
 120          //! `str` and returns its status.
 121          [[nodiscard]] Status Consume(
 122              const SourceLocation& source_loc,
 123              const std::string& str) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
 124          //! Resets all usage to zero. Called periodically by the scheduler.
 125          void Reset() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
 126          //! Returns true if any log locations are currently being suppressed.
 127          bool SuppressionsActive() const { return m_suppression_active; }
 128      };
 129  
 130      class Logger
 131      {
 132      private:
 133          mutable StdMutex m_cs; // Can not use Mutex from sync.h because in debug mode it would cause a deadlock when a potential deadlock was detected
 134  
 135          FILE* m_fileout GUARDED_BY(m_cs) = nullptr;
 136          std::list<util::log::Entry> m_msgs_before_open GUARDED_BY(m_cs);
 137          bool m_buffering GUARDED_BY(m_cs) = true; //!< Buffer messages before logging can be started.
 138          size_t m_max_buffer_memusage GUARDED_BY(m_cs){DEFAULT_MAX_LOG_BUFFER};
 139          size_t m_cur_buffer_memusage GUARDED_BY(m_cs){0};
 140          size_t m_buffer_lines_discarded GUARDED_BY(m_cs){0};
 141  
 142          //! Manages the rate limiting of each log location.
 143          std::shared_ptr<LogRateLimiter> m_limiter GUARDED_BY(m_cs);
 144  
 145          //! Category-specific log level. Overrides `m_log_level`.
 146          std::unordered_map<LogFlags, Level> m_category_log_levels GUARDED_BY(m_cs);
 147  
 148          //! If there is no category-specific log level, all logs with a severity
 149          //! level lower than `m_log_level` will be ignored.
 150          std::atomic<Level> m_log_level{DEFAULT_LOG_LEVEL};
 151  
 152          /** Log categories bitfield. */
 153          std::atomic<CategoryMask> m_categories{BCLog::NONE};
 154  
 155          std::string Format(const util::log::Entry& entry) const;
 156  
 157          std::string LogTimestampStr(SystemClock::time_point now, std::chrono::seconds mocktime) const;
 158  
 159          /** Slots that connect to the print signal */
 160          std::list<std::function<void(const std::string&)>> m_print_callbacks GUARDED_BY(m_cs){};
 161  
 162          /** Send an entry to the log output (internal) */
 163          void LogPrint_(util::log::Entry log_entry) EXCLUSIVE_LOCKS_REQUIRED(m_cs);
 164  
 165          std::string GetLogPrefix(LogFlags category, Level level) const;
 166  
 167      public:
 168          bool m_print_to_console = false;
 169          bool m_print_to_file = false;
 170  
 171          bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS;
 172          bool m_log_time_micros = DEFAULT_LOGTIMEMICROS;
 173          bool m_log_threadnames = DEFAULT_LOGTHREADNAMES;
 174          bool m_log_sourcelocations = DEFAULT_LOGSOURCELOCATIONS;
 175          bool m_always_print_category_level = DEFAULT_LOGLEVELALWAYS;
 176  
 177          fs::path m_file_path;
 178          std::atomic<bool> m_reopen_file{false};
 179  
 180          /** Send an entry to the log output */
 181          void LogPrint(util::log::Entry log_entry) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
 182  
 183          /** Returns whether logs will be written to any output */
 184          bool Enabled() const EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
 185          {
 186              STDLOCK(m_cs);
 187              return m_buffering || m_print_to_console || m_print_to_file || !m_print_callbacks.empty();
 188          }
 189  
 190          /** Connect a slot to the print signal and return the connection */
 191          std::list<std::function<void(const std::string&)>>::iterator PushBackCallback(std::function<void(const std::string&)> fun) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
 192          {
 193              STDLOCK(m_cs);
 194              m_print_callbacks.push_back(std::move(fun));
 195              return --m_print_callbacks.end();
 196          }
 197  
 198          /** Delete a connection */
 199          void DeleteCallback(std::list<std::function<void(const std::string&)>>::iterator it) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
 200          {
 201              STDLOCK(m_cs);
 202              m_print_callbacks.erase(it);
 203          }
 204  
 205          size_t NumConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
 206          {
 207              STDLOCK(m_cs);
 208              return m_print_callbacks.size();
 209          }
 210  
 211          /** Start logging (and flush all buffered messages) */
 212          bool StartLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
 213          /** Only for testing */
 214          void DisconnectTestLogger() EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
 215  
 216          void SetRateLimiting(std::shared_ptr<LogRateLimiter> limiter) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
 217          {
 218              STDLOCK(m_cs);
 219              m_limiter = std::move(limiter);
 220          }
 221  
 222          /** Disable logging
 223           * This offers a slight speedup and slightly smaller memory usage
 224           * compared to leaving the logging system in its default state.
 225           * Mostly intended for libbitcoin-kernel apps that don't want any logging.
 226           * Should be used instead of StartLogging().
 227           */
 228          void DisableLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
 229  
 230          void ShrinkDebugFile() EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
 231  
 232          std::unordered_map<LogFlags, Level> CategoryLevels() const EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
 233          {
 234              STDLOCK(m_cs);
 235              return m_category_log_levels;
 236          }
 237          void SetCategoryLogLevel(const std::unordered_map<LogFlags, Level>& levels) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
 238          {
 239              STDLOCK(m_cs);
 240              m_category_log_levels = levels;
 241          }
 242          void AddCategoryLogLevel(LogFlags category, Level level) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
 243          {
 244              STDLOCK(m_cs);
 245              m_category_log_levels[category] = level;
 246          }
 247          bool SetCategoryLogLevel(std::string_view category_str, std::string_view level_str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
 248  
 249          Level LogLevel() const { return m_log_level.load(); }
 250          void SetLogLevel(Level level) { m_log_level = level; }
 251          bool SetLogLevel(std::string_view level);
 252  
 253          CategoryMask GetCategoryMask() const { return m_categories.load(); }
 254  
 255          void EnableCategory(LogFlags flag);
 256          bool EnableCategory(std::string_view str);
 257          void DisableCategory(LogFlags flag);
 258          bool DisableCategory(std::string_view str);
 259  
 260          bool WillLogCategory(LogFlags category) const;
 261          bool WillLogCategoryLevel(LogFlags category, Level level) const EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
 262  
 263          /** Returns a vector of the log categories in alphabetical order. */
 264          std::vector<LogCategory> LogCategoriesList() const;
 265          /** Returns a string with the log categories in alphabetical order. */
 266          std::string LogCategoriesString() const
 267          {
 268              return util::Join(LogCategoriesList(), ", ", [&](const LogCategory& i) { return i.category; });
 269          };
 270  
 271          //! Returns a string with all user-selectable log levels.
 272          std::string LogLevelsString() const;
 273  
 274          //! Returns the string representation of a log level.
 275          static std::string LogLevelToStr(BCLog::Level level);
 276  
 277          bool DefaultShrinkDebugFile() const;
 278  
 279          //! Return log flag if str parses as a log category.
 280          static std::optional<BCLog::LogFlags> GetLogCategory(std::string_view str);
 281      };
 282  } // namespace BCLog
 283  
 284  BCLog::Logger& LogInstance();
 285  
 286  #endif // BITCOIN_LOGGING_H
 287