Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251return "info";252case BCLog::Level::Warning:253return "warning";254case BCLog::Level::Error:255return "error";256}257assert(false);258}260static std::string LogCategoryToStr(BCLog::LogFlags category)261{262if (category == BCLog::ALL) {263return "all";264}265auto it = LOG_CATEGORIES_BY_FLAG.find(category);266assert(it != LOG_CATEGORIES_BY_FLAG.end());267return it->second;268}270static std::optional<BCLog::Level> GetLogLevel(std::string_view level_str)271{272if (level_str == "trace") {273return BCLog::Level::Trace;274} else if (level_str == "debug") {275return BCLog::Level::Debug;276} else if (level_str == "info") {277return BCLog::Level::Info;278} else if (level_str == "warning") {279return BCLog::Level::Warning;280} else if (level_str == "error") {281return BCLog::Level::Error;282} else {283return std::nullopt;284}285}287std::vector<LogCategory> BCLog::Logger::LogCategoriesList() const288{289std::vector<LogCategory> ret;290ret.reserve(LOG_CATEGORIES_BY_STR.size());291for (const auto& [category, flag] : LOG_CATEGORIES_BY_STR) {292ret.push_back(LogCategory{.category = category, .active = WillLogCategory(flag)});293}294return ret;295}297/** Log severity levels that can be selected by the user. */298static constexpr std::array<BCLog::Level, 3> LogLevelsList()299{300return {BCLog::Level::Info, BCLog::Level::Debug, BCLog::Level::Trace};301}303std::string BCLog::Logger::LogLevelsString() const304{305const auto& levels = LogLevelsList();306return Join(std::vector<BCLog::Level>{levels.begin(), levels.end()}, ", ", [](BCLog::Level level) { return LogLevelToStr(level); });307}309std::string BCLog::Logger::LogTimestampStr(SystemClock::time_point now, std::chrono::seconds mocktime) const310{311std::string strStamped;313if (!m_log_timestamps)314return strStamped;316const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};317strStamped = FormatISO8601DateTime(TicksSinceEpoch<std::chrono::seconds>(now_seconds));318if (m_log_time_micros && !strStamped.empty()) {319strStamped.pop_back();320strStamped += strprintf(".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));321}322if (mocktime > 0s) {323strStamped += " (mocktime: " + FormatISO8601DateTime(count_seconds(mocktime)) + ")";324}325strStamped += ' ';327return strStamped;328}330namespace BCLog {331/** Belts and suspenders: make sure outgoing log messages don't contain332* potentially suspicious characters, such as terminal control codes.333*334* This escapes control characters except newline ('\n') in C syntax.335* It escapes instead of removes them to still allow for troubleshooting336* issues where they accidentally end up in strings.337*/338std::string LogEscapeMessage(std::string_view str) {339std::string ret;340for (char ch_in : str) {341uint8_t ch = (uint8_t)ch_in;342if ((ch >= 32 || ch == '\n') && ch != '\x7f') {343ret += ch_in;344} else {345ret += strprintf("\\x%02x", ch);346}347}348return ret;349}350} // namespace BCLog352std::string BCLog::Logger::GetLogPrefix(BCLog::LogFlags category, BCLog::Level level) const353{354if (category == LogFlags::NONE) category = LogFlags::ALL;356const bool has_category{m_always_print_category_level || category != LogFlags::ALL};358// If there is no category, Info is implied359if (!has_category && level == Level::Info) return {};361std::string s{"["};362if (has_category) {363s += LogCategoryToStr(category);364}366if (m_always_print_category_level || !has_category || level != Level::Debug) {367// If there is a category, Debug is implied, so don't add the level369// Only add separator if we have a category370if (has_category) s += ":";371s += Logger::LogLevelToStr(level);372}374s += "] ";375return s;376}378static size_t MemUsage(const util::log::Entry& log)379{380return memusage::DynamicUsage(log.message) +381memusage::DynamicUsage(log.thread_name) +382memusage::MallocUsage(sizeof(memusage::list_node<util::log::Entry>));383}385BCLog::LogRateLimiter::LogRateLimiter(uint64_t max_bytes, std::chrono::seconds reset_window)386: m_max_bytes{max_bytes}, m_reset_window{reset_window} {}388std::shared_ptr<BCLog::LogRateLimiter> BCLog::LogRateLimiter::Create(389SchedulerFunction&& scheduler_func, uint64_t max_bytes, std::chrono::seconds reset_window)390{391auto limiter{std::shared_ptr<LogRateLimiter>(new LogRateLimiter(max_bytes, reset_window))};392std::weak_ptr<LogRateLimiter> weak_limiter{limiter};393auto reset = [weak_limiter] {394if (auto shared_limiter{weak_limiter.lock()}) shared_limiter->Reset();395};396scheduler_func(reset, limiter->m_reset_window);397return limiter;398}400BCLog::LogRateLimiter::Status BCLog::LogRateLimiter::Consume(401const SourceLocation& source_loc,402const std::string& str)403{404STDLOCK(m_mutex);405auto& stats{m_source_locations.try_emplace(source_loc, m_max_bytes).first->second};406Status status{stats.m_dropped_bytes > 0 ? Status::STILL_SUPPRESSED : Status::UNSUPPRESSED};408if (!stats.Consume(str.size()) && status == Status::UNSUPPRESSED) {409status = Status::NEWLY_SUPPRESSED;410m_suppression_active = true;411}413return status;414}416std::string BCLog::Logger::Format(const util::log::Entry& entry) const417{418std::string result{LogTimestampStr(entry.timestamp, entry.mocktime)};420if (m_log_threadnames) {421result += strprintf("[%s] ", (entry.thread_name.empty() ? "unknown" : entry.thread_name));422}424if (m_log_sourcelocations) {425result += strprintf("[%s:%d] [%s] ", RemovePrefixView(entry.source_loc.file_name(), "./"), entry.source_loc.line(), entry.source_loc.function_name_short());426}428result += GetLogPrefix(static_cast<LogFlags>(entry.category), entry.level);429result += LogEscapeMessage(entry.message);431if (!result.ends_with('\n')) result += '\n';432return result;433}435void BCLog::Logger::LogPrint(util::log::Entry entry)436{437STDLOCK(m_cs);438return LogPrint_(std::move(entry));439}441// NOLINTNEXTLINE(misc-no-recursion)442void BCLog::Logger::LogPrint_(util::log::Entry entry)443{444if (m_buffering) {445{446m_cur_buffer_memusage += MemUsage(entry);447m_msgs_before_open.push_back(std::move(entry));448}450while (m_cur_buffer_memusage > m_max_buffer_memusage) {451if (m_msgs_before_open.empty()) {452m_cur_buffer_memusage = 0;453break;454}455m_cur_buffer_memusage -= MemUsage(m_msgs_before_open.front());456m_msgs_before_open.pop_front();457++m_buffer_lines_discarded;458}460return;461}463std::string str_prefixed{Format(entry)};464bool ratelimit{false};465if (entry.should_ratelimit && m_limiter) {466auto status{m_limiter->Consume(entry.source_loc, str_prefixed)};467if (status == LogRateLimiter::Status::NEWLY_SUPPRESSED) {468// NOLINTNEXTLINE(misc-no-recursion)469LogPrint_({470.category = LogFlags::ALL,471.level = Level::Warning,472.should_ratelimit = false, // with should_ratelimit=false, this cannot lead to infinite recursion473.source_loc = SourceLocation{__func__},474.message = strprintf(475"Excessive logging detected from %s:%d (%s): >%d bytes logged during "476"the last time window of %is. Suppressing logging to disk from this "477"source location until time window resets. Console logging "478"unaffected. Last log entry.",479entry.source_loc.file_name(), entry.source_loc.line(), entry.source_loc.function_name_short(),480m_limiter->m_max_bytes,481Ticks<std::chrono::seconds>(m_limiter->m_reset_window)),482});483} else if (status == LogRateLimiter::Status::STILL_SUPPRESSED) {484ratelimit = true;485}486}488// To avoid confusion caused by dropped log messages when debugging an issue,489// we prefix log lines with "[*]" when there are any suppressed source locations.490if (m_limiter && m_limiter->SuppressionsActive()) {491str_prefixed.insert(0, "[*] ");492}494if (m_print_to_console) {495// print to console496fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);497fflush(stdout);498}499for (const auto& cb : m_print_callbacks) {500cb(str_prefixed);
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.