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.
251args.AddArg("-loglevel", "...", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);252const char* argv_test[] = {"bitcoind", "-loglevel=debug", "-loglevel=net:trace", "-loglevel=http:info"};253std::string err;254BOOST_REQUIRE(args.ParseParameters(4, argv_test, err));256auto result = init::SetLoggingLevel(args);257BOOST_REQUIRE(result);258BOOST_CHECK_EQUAL(LogInstance().LogLevel(), BCLog::Level::Debug);260const auto& category_levels{LogInstance().CategoryLevels()};261BOOST_CHECK_EQUAL(category_levels.size(), 2);263const auto net_it{category_levels.find(BCLog::LogFlags::NET)};264BOOST_CHECK(net_it != category_levels.end());265BOOST_CHECK_EQUAL(net_it->second, BCLog::Level::Trace);267const auto http_it{category_levels.find(BCLog::LogFlags::HTTP)};268BOOST_CHECK(http_it != category_levels.end());269BOOST_CHECK_EQUAL(http_it->second, BCLog::Level::Info);270}272// Removed categories (like "libevent") should not store a category-specific level273{274ResetLogger();275BOOST_CHECK(LogInstance().SetCategoryLogLevel(/*category_str=*/"libevent", /*level_str=*/"trace"));276BOOST_CHECK(LogInstance().CategoryLevels().empty());277}278}280struct ScopedScheduler {281CScheduler scheduler{};283ScopedScheduler()284{285scheduler.m_service_thread = std::thread([this] { scheduler.serviceQueue(); });286}287~ScopedScheduler()288{289scheduler.stop();290}291void MockForwardAndSync(std::chrono::seconds duration)292{293scheduler.MockForward(duration);294std::promise<void> promise;295scheduler.scheduleFromNow([&promise] { promise.set_value(); }, 0ms);296promise.get_future().wait();297}298std::shared_ptr<BCLog::LogRateLimiter> GetLimiter(size_t max_bytes, std::chrono::seconds window)299{300auto sched_func = [this](auto func, auto w) {301scheduler.scheduleEvery(std::move(func), w);302};303return BCLog::LogRateLimiter::Create(sched_func, max_bytes, window);304}305};307BOOST_AUTO_TEST_CASE(logging_log_rate_limiter)308{309uint64_t max_bytes{1024};310auto reset_window{1min};311ScopedScheduler scheduler{};312auto limiter_{scheduler.GetLimiter(max_bytes, reset_window)};313auto& limiter{*Assert(limiter_)};315using Status = BCLog::LogRateLimiter::Status;316auto source_loc_1{SourceLocation{__func__}};317auto source_loc_2{SourceLocation{__func__}};319// A fresh limiter should not have any suppressions320BOOST_CHECK(!limiter.SuppressionsActive());322// Resetting an unused limiter is fine323limiter.Reset();324BOOST_CHECK(!limiter.SuppressionsActive());326// No suppression should happen until more than max_bytes have been consumed327BOOST_CHECK_EQUAL(limiter.Consume(source_loc_1, std::string(max_bytes - 1, 'a')), Status::UNSUPPRESSED);328BOOST_CHECK_EQUAL(limiter.Consume(source_loc_1, "a"), Status::UNSUPPRESSED);329BOOST_CHECK(!limiter.SuppressionsActive());330BOOST_CHECK_EQUAL(limiter.Consume(source_loc_1, "a"), Status::NEWLY_SUPPRESSED);331BOOST_CHECK(limiter.SuppressionsActive());332BOOST_CHECK_EQUAL(limiter.Consume(source_loc_1, "a"), Status::STILL_SUPPRESSED);333BOOST_CHECK(limiter.SuppressionsActive());335// Location 2 should not be affected by location 1's suppression336BOOST_CHECK_EQUAL(limiter.Consume(source_loc_2, std::string(max_bytes, 'a')), Status::UNSUPPRESSED);337BOOST_CHECK_EQUAL(limiter.Consume(source_loc_2, "a"), Status::NEWLY_SUPPRESSED);338BOOST_CHECK(limiter.SuppressionsActive());340// After reset_window time has passed, all suppressions should be cleared.341scheduler.MockForwardAndSync(reset_window);343BOOST_CHECK(!limiter.SuppressionsActive());344BOOST_CHECK_EQUAL(limiter.Consume(source_loc_1, std::string(max_bytes, 'a')), Status::UNSUPPRESSED);345BOOST_CHECK_EQUAL(limiter.Consume(source_loc_2, std::string(max_bytes, 'a')), Status::UNSUPPRESSED);346}348BOOST_AUTO_TEST_CASE(logging_log_limit_stats)349{350BCLog::LogRateLimiter::Stats stats(BCLog::RATELIMIT_MAX_BYTES);352// Check that stats gets initialized correctly.353BOOST_CHECK_EQUAL(stats.m_available_bytes, BCLog::RATELIMIT_MAX_BYTES);354BOOST_CHECK_EQUAL(stats.m_dropped_bytes, uint64_t{0});356const uint64_t MESSAGE_SIZE{BCLog::RATELIMIT_MAX_BYTES / 2};357BOOST_CHECK(stats.Consume(MESSAGE_SIZE));358BOOST_CHECK_EQUAL(stats.m_available_bytes, BCLog::RATELIMIT_MAX_BYTES - MESSAGE_SIZE);359BOOST_CHECK_EQUAL(stats.m_dropped_bytes, uint64_t{0});361BOOST_CHECK(stats.Consume(MESSAGE_SIZE));362BOOST_CHECK_EQUAL(stats.m_available_bytes, BCLog::RATELIMIT_MAX_BYTES - MESSAGE_SIZE * 2);363BOOST_CHECK_EQUAL(stats.m_dropped_bytes, uint64_t{0});365// Consuming more bytes after already having consumed RATELIMIT_MAX_BYTES should fail.366BOOST_CHECK(!stats.Consume(500));367BOOST_CHECK_EQUAL(stats.m_available_bytes, uint64_t{0});368BOOST_CHECK_EQUAL(stats.m_dropped_bytes, uint64_t{500});369}371namespace {373enum class Location {374INFO_1,375INFO_2,376DEBUG_LOG,377INFO_NOLIMIT,378};380void LogFromLocation(Location location, const std::string& message) {381switch (location) {382case Location::INFO_1:383LogInfo("%s\n", message);384return;385case Location::INFO_2:386LogInfo("%s\n", message);387return;388case Location::DEBUG_LOG:389LogDebug(BCLog::LogFlags::HTTP, "%s\n", message);390return;391case Location::INFO_NOLIMIT:392LogInfo(util::log::NO_RATE_LIMIT, "%s\n", message);393return;394} // no default case, so the compiler can warn about missing cases395assert(false);396}398/**399* For a given `location` and `message`, ensure that the on-disk debug log behaviour resembles what400* we'd expect it to be for `status` and `suppressions_active`.401*/402void TestLogFromLocation(Location location, const std::string& message,403BCLog::LogRateLimiter::Status status, bool suppressions_active,404std::source_location source = std::source_location::current())405{406BOOST_TEST_INFO_SCOPE("TestLogFromLocation called from " << source.file_name() << ":" << source.line());407using Status = BCLog::LogRateLimiter::Status;408if (!suppressions_active) assert(status == Status::UNSUPPRESSED); // developer error410std::ofstream ofs(LogInstance().m_file_path.std_path(), std::ios::out | std::ios::trunc); // clear debug log411LogFromLocation(location, message);412auto log_lines{ReadDebugLogLines()};413BOOST_TEST_INFO_SCOPE(log_lines.size() << " log_lines read: \n" << util::Join(log_lines, "\n"));415if (status == Status::STILL_SUPPRESSED) {416BOOST_CHECK_EQUAL(log_lines.size(), 0);417return;418}420if (status == Status::NEWLY_SUPPRESSED) {421BOOST_REQUIRE_EQUAL(log_lines.size(), 2);422BOOST_CHECK(log_lines[0].starts_with("[*] [warning] Excessive logging detected"));423log_lines.erase(log_lines.begin());424}425BOOST_REQUIRE_EQUAL(log_lines.size(), 1);426auto& payload{log_lines.back()};427BOOST_CHECK_EQUAL(suppressions_active, payload.starts_with("[*]"));428BOOST_CHECK(payload.ends_with(message));429}431} // namespace433BOOST_FIXTURE_TEST_CASE(logging_filesize_rate_limit, LogSetup)434{435using Status = BCLog::LogRateLimiter::Status;436LogInstance().m_log_timestamps = false;437LogInstance().m_log_sourcelocations = false;438LogInstance().m_log_threadnames = false;439LogInstance().EnableCategory(BCLog::LogFlags::HTTP);441constexpr int64_t line_length{1024};442constexpr int64_t num_lines{10};443constexpr int64_t bytes_quota{line_length * num_lines};444constexpr auto time_window{1h};446ScopedScheduler scheduler{};447auto limiter{scheduler.GetLimiter(bytes_quota, time_window)};448LogInstance().SetRateLimiting(limiter);450const std::string log_message(line_length - 1, 'a'); // subtract one for newline452for (int i = 0; i < num_lines; ++i) {453TestLogFromLocation(Location::INFO_1, log_message, Status::UNSUPPRESSED, /*suppressions_active=*/false);454}455TestLogFromLocation(Location::INFO_1, "a", Status::NEWLY_SUPPRESSED, /*suppressions_active=*/true);456TestLogFromLocation(Location::INFO_1, "b", Status::STILL_SUPPRESSED, /*suppressions_active=*/true);457TestLogFromLocation(Location::INFO_2, "c", Status::UNSUPPRESSED, /*suppressions_active=*/true);458{459scheduler.MockForwardAndSync(time_window);460BOOST_CHECK(ReadDebugLogLines().back().starts_with("[warning] Restarting logging"));461}462// Check that logging from previously suppressed location is unsuppressed again.463TestLogFromLocation(Location::INFO_1, log_message, Status::UNSUPPRESSED, /*suppressions_active=*/false);464// Check that conditional logging, and unconditional logging with should_ratelimit=false is465// not being ratelimited.466for (Location location : {Location::DEBUG_LOG, Location::INFO_NOLIMIT}) {467for (int i = 0; i < num_lines + 2; ++i) {468TestLogFromLocation(location, log_message, Status::UNSUPPRESSED, /*suppressions_active=*/false);469}470}471}473BOOST_AUTO_TEST_SUITE_END()
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.