diff options
Diffstat (limited to 'src/logging.h')
-rw-r--r-- | src/logging.h | 162 |
1 files changed, 90 insertions, 72 deletions
diff --git a/src/logging.h b/src/logging.h index fe6b7051ba..fdc12c79b3 100644 --- a/src/logging.h +++ b/src/logging.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2022 The Bitcoin Core developers +// Copyright (c) 2009-present The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -10,6 +10,7 @@ #include <tinyformat.h> #include <util/fs.h> #include <util/string.h> +#include <util/time.h> #include <atomic> #include <cstdint> @@ -36,40 +37,41 @@ struct LogCategory { }; namespace BCLog { - enum LogFlags : uint32_t { - NONE = 0, - NET = (1 << 0), - TOR = (1 << 1), - MEMPOOL = (1 << 2), - HTTP = (1 << 3), - BENCH = (1 << 4), - ZMQ = (1 << 5), - WALLETDB = (1 << 6), - RPC = (1 << 7), - ESTIMATEFEE = (1 << 8), - ADDRMAN = (1 << 9), - SELECTCOINS = (1 << 10), - REINDEX = (1 << 11), - CMPCTBLOCK = (1 << 12), - RAND = (1 << 13), - PRUNE = (1 << 14), - PROXY = (1 << 15), - MEMPOOLREJ = (1 << 16), - LIBEVENT = (1 << 17), - COINDB = (1 << 18), - QT = (1 << 19), - LEVELDB = (1 << 20), - VALIDATION = (1 << 21), - I2P = (1 << 22), - IPC = (1 << 23), + using CategoryMask = uint64_t; + enum LogFlags : CategoryMask { + NONE = CategoryMask{0}, + NET = (CategoryMask{1} << 0), + TOR = (CategoryMask{1} << 1), + MEMPOOL = (CategoryMask{1} << 2), + HTTP = (CategoryMask{1} << 3), + BENCH = (CategoryMask{1} << 4), + ZMQ = (CategoryMask{1} << 5), + WALLETDB = (CategoryMask{1} << 6), + RPC = (CategoryMask{1} << 7), + ESTIMATEFEE = (CategoryMask{1} << 8), + ADDRMAN = (CategoryMask{1} << 9), + SELECTCOINS = (CategoryMask{1} << 10), + REINDEX = (CategoryMask{1} << 11), + CMPCTBLOCK = (CategoryMask{1} << 12), + RAND = (CategoryMask{1} << 13), + PRUNE = (CategoryMask{1} << 14), + PROXY = (CategoryMask{1} << 15), + MEMPOOLREJ = (CategoryMask{1} << 16), + LIBEVENT = (CategoryMask{1} << 17), + COINDB = (CategoryMask{1} << 18), + QT = (CategoryMask{1} << 19), + LEVELDB = (CategoryMask{1} << 20), + VALIDATION = (CategoryMask{1} << 21), + I2P = (CategoryMask{1} << 22), + IPC = (CategoryMask{1} << 23), #ifdef DEBUG_LOCKCONTENTION - LOCK = (1 << 24), + LOCK = (CategoryMask{1} << 24), #endif - BLOCKSTORAGE = (1 << 25), - TXRECONCILIATION = (1 << 26), - SCAN = (1 << 27), - TXPACKAGES = (1 << 28), - ALL = ~(uint32_t)0, + BLOCKSTORAGE = (CategoryMask{1} << 25), + TXRECONCILIATION = (CategoryMask{1} << 26), + SCAN = (CategoryMask{1} << 27), + TXPACKAGES = (CategoryMask{1} << 28), + ALL = ~NONE, }; enum class Level { Trace = 0, // High-volume or detailed logging for development/debugging @@ -79,22 +81,29 @@ namespace BCLog { Error, }; constexpr auto DEFAULT_LOG_LEVEL{Level::Debug}; + constexpr size_t DEFAULT_MAX_LOG_BUFFER{1'000'000}; // buffer up to 1MB of log data prior to StartLogging class Logger { + public: + struct BufferedLog { + SystemClock::time_point now; + std::chrono::seconds mocktime; + std::string str, logging_function, source_file, threadname; + int source_line; + LogFlags category; + Level level; + }; + private: 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 FILE* m_fileout GUARDED_BY(m_cs) = nullptr; - std::list<std::string> m_msgs_before_open GUARDED_BY(m_cs); + std::list<BufferedLog> m_msgs_before_open GUARDED_BY(m_cs); bool m_buffering GUARDED_BY(m_cs) = true; //!< Buffer messages before logging can be started. - - /** - * m_started_new_line is a state variable that will suppress printing of - * the timestamp when multiple calls are made that don't end in a - * newline. - */ - std::atomic_bool m_started_new_line{true}; + size_t m_max_buffer_memusage GUARDED_BY(m_cs){DEFAULT_MAX_LOG_BUFFER}; + size_t m_cur_buffer_memusage GUARDED_BY(m_cs){0}; + size_t m_buffer_lines_discarded GUARDED_BY(m_cs){0}; //! Category-specific log level. Overrides `m_log_level`. std::unordered_map<LogFlags, Level> m_category_log_levels GUARDED_BY(m_cs); @@ -104,13 +113,21 @@ namespace BCLog { std::atomic<Level> m_log_level{DEFAULT_LOG_LEVEL}; /** Log categories bitfield. */ - std::atomic<uint32_t> m_categories{0}; + std::atomic<CategoryMask> m_categories{BCLog::NONE}; + + void FormatLogStrInPlace(std::string& str, LogFlags category, Level level, std::string_view source_file, int source_line, std::string_view logging_function, std::string_view threadname, SystemClock::time_point now, std::chrono::seconds mocktime) const; - std::string LogTimestampStr(const std::string& str); + std::string LogTimestampStr(SystemClock::time_point now, std::chrono::seconds mocktime) const; /** Slots that connect to the print signal */ std::list<std::function<void(const std::string&)>> m_print_callbacks GUARDED_BY(m_cs) {}; + /** Send a string to the log output (internal) */ + void LogPrintStr_(std::string_view str, std::string_view logging_function, std::string_view source_file, int source_line, BCLog::LogFlags category, BCLog::Level level) + EXCLUSIVE_LOCKS_REQUIRED(m_cs); + + std::string GetLogPrefix(LogFlags category, Level level) const; + public: bool m_print_to_console = false; bool m_print_to_file = false; @@ -124,20 +141,19 @@ namespace BCLog { fs::path m_file_path; std::atomic<bool> m_reopen_file{false}; - std::string GetLogPrefix(LogFlags category, Level level) const; - /** Send a string to the log output */ - void LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, int source_line, BCLog::LogFlags category, BCLog::Level level); + void LogPrintStr(std::string_view str, std::string_view logging_function, std::string_view source_file, int source_line, BCLog::LogFlags category, BCLog::Level level) + EXCLUSIVE_LOCKS_REQUIRED(!m_cs); /** Returns whether logs will be written to any output */ - bool Enabled() const + bool Enabled() const EXCLUSIVE_LOCKS_REQUIRED(!m_cs) { StdLockGuard scoped_lock(m_cs); return m_buffering || m_print_to_console || m_print_to_file || !m_print_callbacks.empty(); } /** Connect a slot to the print signal and return the connection */ - std::list<std::function<void(const std::string&)>>::iterator PushBackCallback(std::function<void(const std::string&)> fun) + std::list<std::function<void(const std::string&)>>::iterator PushBackCallback(std::function<void(const std::string&)> fun) EXCLUSIVE_LOCKS_REQUIRED(!m_cs) { StdLockGuard scoped_lock(m_cs); m_print_callbacks.push_back(std::move(fun)); @@ -145,44 +161,52 @@ namespace BCLog { } /** Delete a connection */ - void DeleteCallback(std::list<std::function<void(const std::string&)>>::iterator it) + void DeleteCallback(std::list<std::function<void(const std::string&)>>::iterator it) EXCLUSIVE_LOCKS_REQUIRED(!m_cs) { StdLockGuard scoped_lock(m_cs); m_print_callbacks.erase(it); } /** Start logging (and flush all buffered messages) */ - bool StartLogging(); + bool StartLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs); /** Only for testing */ - void DisconnectTestLogger(); + void DisconnectTestLogger() EXCLUSIVE_LOCKS_REQUIRED(!m_cs); + + /** Disable logging + * This offers a slight speedup and slightly smaller memory usage + * compared to leaving the logging system in its default state. + * Mostly intended for libbitcoin-kernel apps that don't want any logging. + * Should be used instead of StartLogging(). + */ + void DisableLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs); void ShrinkDebugFile(); - std::unordered_map<LogFlags, Level> CategoryLevels() const + std::unordered_map<LogFlags, Level> CategoryLevels() const EXCLUSIVE_LOCKS_REQUIRED(!m_cs) { StdLockGuard scoped_lock(m_cs); return m_category_log_levels; } - void SetCategoryLogLevel(const std::unordered_map<LogFlags, Level>& levels) + void SetCategoryLogLevel(const std::unordered_map<LogFlags, Level>& levels) EXCLUSIVE_LOCKS_REQUIRED(!m_cs) { StdLockGuard scoped_lock(m_cs); m_category_log_levels = levels; } - bool SetCategoryLogLevel(const std::string& category_str, const std::string& level_str); + bool SetCategoryLogLevel(std::string_view category_str, std::string_view level_str) EXCLUSIVE_LOCKS_REQUIRED(!m_cs); Level LogLevel() const { return m_log_level.load(); } void SetLogLevel(Level level) { m_log_level = level; } - bool SetLogLevel(const std::string& level); + bool SetLogLevel(std::string_view level); - uint32_t GetCategoryMask() const { return m_categories.load(); } + CategoryMask GetCategoryMask() const { return m_categories.load(); } void EnableCategory(LogFlags flag); - bool EnableCategory(const std::string& str); + bool EnableCategory(std::string_view str); void DisableCategory(LogFlags flag); - bool DisableCategory(const std::string& str); + bool DisableCategory(std::string_view str); bool WillLogCategory(LogFlags category) const; - bool WillLogCategoryLevel(LogFlags category, Level level) const; + bool WillLogCategoryLevel(LogFlags category, Level level) const EXCLUSIVE_LOCKS_REQUIRED(!m_cs); /** Returns a vector of the log categories in alphabetical order. */ std::vector<LogCategory> LogCategoriesList() const; @@ -212,37 +236,34 @@ static inline bool LogAcceptCategory(BCLog::LogFlags category, BCLog::Level leve } /** Return true if str parses as a log category and set the flag */ -bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str); - -// Be conservative when using functions that -// unconditionally log to debug.log! It should not be the case that an inbound -// peer can fill up a user's disk with debug.log entries. +bool GetLogCategory(BCLog::LogFlags& flag, std::string_view str); template <typename... Args> -static inline void LogPrintf_(const std::string& logging_function, const std::string& source_file, const int source_line, const BCLog::LogFlags flag, const BCLog::Level level, const char* fmt, const Args&... args) +inline void LogPrintFormatInternal(std::string_view logging_function, std::string_view source_file, const int source_line, const BCLog::LogFlags flag, const BCLog::Level level, util::ConstevalFormatString<sizeof...(Args)> fmt, const Args&... args) { if (LogInstance().Enabled()) { std::string log_msg; try { log_msg = tfm::format(fmt, args...); } catch (tinyformat::format_error& fmterr) { - /* Original format string will have newline so don't add one here */ - log_msg = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + fmt; + log_msg = "Error \"" + std::string{fmterr.what()} + "\" while formatting log message: " + fmt.fmt; } LogInstance().LogPrintStr(log_msg, logging_function, source_file, source_line, flag, level); } } -#define LogPrintLevel_(category, level, ...) LogPrintf_(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__) +#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__) // Log unconditionally. +// Be conservative when using functions that unconditionally log to debug.log! +// It should not be the case that an inbound peer can fill up a user's storage +// with debug.log entries. #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, __VA_ARGS__) #define LogWarning(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Warning, __VA_ARGS__) #define LogError(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Error, __VA_ARGS__) // Deprecated unconditional logging. #define LogPrintf(...) LogInfo(__VA_ARGS__) -#define LogPrintfCategory(category, ...) LogPrintLevel_(category, BCLog::Level::Info, __VA_ARGS__) // Use a macro instead of a function for conditional logging to prevent // evaluating arguments when logging for the category is not enabled. @@ -259,7 +280,4 @@ static inline void LogPrintf_(const std::string& logging_function, const std::st #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) #define LogTrace(category, ...) LogPrintLevel(category, BCLog::Level::Trace, __VA_ARGS__) -// Deprecated conditional logging -#define LogPrint(category, ...) LogDebug(category, __VA_ARGS__) - #endif // BITCOIN_LOGGING_H |