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