/ src / logging.h
logging.h
  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();
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  
280  } // namespace BCLog
281  
282  BCLog::Logger& LogInstance();
283  
284  /** Return true if log accepts specified category, at the specified level. */
285  static inline bool LogAcceptCategory(BCLog::LogFlags category, BCLog::Level level)
286  {
287      return LogInstance().WillLogCategoryLevel(category, level);
288  }
289  
290  /// Return log flag if str parses as a log category.
291  std::optional<BCLog::LogFlags> GetLogCategory(std::string_view str);
292  
293  #endif // BITCOIN_LOGGING_H