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