51    return fwrite(str.data(), 1, str.size(), fp);
 
   59    assert(m_fileout == 
nullptr);
 
   68        setbuf(m_fileout, 
nullptr); 
 
   77    if (m_buffer_lines_discarded > 0) {
 
   80    while (!m_msgs_before_open.empty()) {
 
   81        const auto& buflog = m_msgs_before_open.front();
 
   82        std::string 
s{buflog.str};
 
   83        FormatLogStrInPlace(
s, buflog.category, buflog.level, buflog.source_loc, buflog.threadname, buflog.now, buflog.mocktime);
 
   84        m_msgs_before_open.pop_front();
 
   88        for (
const auto& cb : m_print_callbacks) {
 
   92    m_cur_buffer_memusage = 0;
 
  102    if (m_fileout != 
nullptr) fclose(m_fileout);
 
  104    m_print_callbacks.clear();
 
  106    m_cur_buffer_memusage = 0;
 
  107    m_buffer_lines_discarded = 0;
 
  108    m_msgs_before_open.clear();
 
  116        assert(m_print_callbacks.empty());
 
  118    m_print_to_file = 
false;
 
  119    m_print_to_console = 
false;
 
  125    m_categories |= flag;
 
  132    EnableCategory(flag);
 
  138    m_categories &= ~flag;
 
  145    DisableCategory(flag);
 
  151    return (m_categories.load(std::memory_order_relaxed) & category) != 0;
 
  160    if (!WillLogCategory(category)) 
return false;
 
  163    const auto it{m_category_log_levels.find(category)};
 
  164    return level >= (it == m_category_log_levels.end() ? LogLevel() : it->second);
 
  197#ifdef DEBUG_LOCKCONTENTION 
  209        std::unordered_map<BCLog::LogFlags, std::string> 
out;
 
  210        for (
const auto& [
k, v] : in) {
 
  211            const bool inserted{
out.emplace(v, 
k).second};
 
  220    if (str.empty() || str == 
"1" || str == 
"all") {
 
  259static std::optional<BCLog::Level> 
GetLogLevel(std::string_view level_str)
 
  261    if (level_str == 
"trace") {
 
  263    } 
else if (level_str == 
"debug") {
 
  265    } 
else if (level_str == 
"info") {
 
  267    } 
else if (level_str == 
"warning") {
 
  269    } 
else if (level_str == 
"error") {
 
  278    std::vector<LogCategory> 
ret;
 
  295    return Join(std::vector<BCLog::Level>{levels.begin(), levels.end()}, 
", ", [](
BCLog::Level level) { 
return LogLevelToStr(level); });
 
  300    std::string strStamped;
 
  302    if (!m_log_timestamps)
 
  305    const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
 
  307    if (m_log_time_micros && !strStamped.empty()) {
 
  308        strStamped.pop_back();
 
  309        strStamped += 
strprintf(
".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
 
  329        for (
char ch_in : str) {
 
  330            uint8_t ch = (uint8_t)ch_in;
 
  331            if ((ch >= 32 || ch == 
'\n') && ch != 
'\x7f') {
 
  345    const bool has_category{m_always_print_category_level || category != 
LogFlags::ALL};
 
  348    if (!has_category && level == 
Level::Info) 
return {};
 
  355    if (m_always_print_category_level || !has_category || level != 
Level::Debug) {
 
  359        if (has_category) 
s += 
":";
 
  375    : m_max_bytes{max_bytes}, m_reset_window{reset_window} {}
 
  378    SchedulerFunction&& scheduler_func, uint64_t max_bytes, std::chrono::seconds reset_window)
 
  380    auto limiter{std::shared_ptr<LogRateLimiter>(
new LogRateLimiter(max_bytes, reset_window))};
 
  381    std::weak_ptr<LogRateLimiter> weak_limiter{limiter};
 
  382    auto reset = [weak_limiter] {
 
  383        if (
auto shared_limiter{weak_limiter.lock()}) shared_limiter->Reset();
 
  385    scheduler_func(reset, limiter->m_reset_window);
 
  390    const std::source_location& source_loc,
 
  391    const std::string& str)
 
  394    auto& stats{m_source_locations.try_emplace(source_loc, m_max_bytes).first->second};
 
  395    Status status{stats.m_dropped_bytes > 0 ? Status::STILL_SUPPRESSED : Status::UNSUPPRESSED};
 
  397    if (!stats.Consume(str.size()) && status == Status::UNSUPPRESSED) {
 
  398        status = Status::NEWLY_SUPPRESSED;
 
  399        m_suppression_active = 
true;
 
  407    if (!str.ends_with(
'\n')) str.push_back(
'\n');
 
  409    str.insert(0, GetLogPrefix(category, level));
 
  411    if (m_log_sourcelocations) {
 
  412        str.insert(0, 
strprintf(
"[%s:%d] [%s] ", 
RemovePrefixView(source_loc.file_name(), 
"./"), source_loc.line(), source_loc.function_name()));
 
  415    if (m_log_threadnames) {
 
  416        str.insert(0, 
strprintf(
"[%s] ", (threadname.empty() ? 
"unknown" : threadname)));
 
  419    str.insert(0, LogTimestampStr(now, mocktime));
 
  425    return LogPrintStr_(str, std::move(source_loc), category, level, should_ratelimit);
 
  436                .
now = SystemClock::now(),
 
  440                .source_loc = std::move(source_loc),
 
  441                .category = category,
 
  444            m_cur_buffer_memusage += 
MemUsage(buf);
 
  445            m_msgs_before_open.push_back(std::move(buf));
 
  448        while (m_cur_buffer_memusage > m_max_buffer_memusage) {
 
  449            if (m_msgs_before_open.empty()) {
 
  450                m_cur_buffer_memusage = 0;
 
  453            m_cur_buffer_memusage -= 
MemUsage(m_msgs_before_open.front());
 
  454            m_msgs_before_open.pop_front();
 
  455            ++m_buffer_lines_discarded;
 
  462    bool ratelimit{
false};
 
  463    if (should_ratelimit && m_limiter) {
 
  464        auto status{m_limiter->Consume(source_loc, str_prefixed)};
 
  468                             "Excessive logging detected from %s:%d (%s): >%d bytes logged during " 
  469                             "the last time window of %is. Suppressing logging to disk from this " 
  470                             "source location until time window resets. Console logging " 
  471                             "unaffected. Last log entry.",
 
  472                             source_loc.file_name(), source_loc.line(), source_loc.function_name(),
 
  473                             m_limiter->m_max_bytes,
 
  474                             Ticks<std::chrono::seconds>(m_limiter->m_reset_window)),
 
  483    if (m_limiter && m_limiter->SuppressionsActive()) {
 
  484        str_prefixed.insert(0, 
"[*] ");
 
  487    if (m_print_to_console) {
 
  489        fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
 
  492    for (
const auto& cb : m_print_callbacks) {
 
  495    if (m_print_to_file && !ratelimit) {
 
  496        assert(m_fileout != 
nullptr);
 
  500            m_reopen_file = 
false;
 
  503                setbuf(new_fileout, 
nullptr); 
 
  505                m_fileout = new_fileout;
 
  515    constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
 
  517    assert(!m_file_path.empty());
 
  525        log_size = fs::file_size(m_file_path);
 
  526    } 
catch (
const fs::filesystem_error&) {}
 
  530    if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
 
  533        std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
 
  534        if (fseek(file, -((
long)vch.size()), SEEK_END)) {
 
  535            LogPrintf(
"Failed to shrink debug log file: fseek(...) failed\n");
 
  539        int nBytes = fread(vch.data(), 1, vch.size(), file);
 
  545            fwrite(vch.data(), 1, nBytes, file);
 
  549    else if (file != 
nullptr)
 
  555    decltype(m_source_locations) source_locations;
 
  558        source_locations.swap(m_source_locations);
 
  559        m_suppression_active = 
false;
 
  561    for (
const auto& [source_loc, stats] : source_locations) {
 
  562        if (stats.m_dropped_bytes == 0) 
continue;
 
  565            "Restarting logging from %s:%d (%s): %d bytes were dropped during the last %ss.",
 
  566            source_loc.file_name(), source_loc.line(), source_loc.function_name(),
 
  567            stats.m_dropped_bytes, Ticks<std::chrono::seconds>(m_reset_window));
 
  573    if (bytes > m_available_bytes) {
 
  574        m_dropped_bytes += bytes;
 
  575        m_available_bytes = 0;
 
  579    m_available_bytes -= bytes;
 
  587    m_log_level = level.value();
 
  600    m_category_log_levels[flag] = level.value();
 
Fixed window rate limiter for logging.
 
static std::shared_ptr< LogRateLimiter > Create(SchedulerFunction &&scheduler_func, uint64_t max_bytes, std::chrono::seconds reset_window)
 
std::function< void(std::function< void()>, std::chrono::milliseconds)> SchedulerFunction
 
LogRateLimiter(uint64_t max_bytes, std::chrono::seconds reset_window)
 
Status Consume(const std::source_location &source_loc, const std::string &str) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Consumes source_loc's available bytes corresponding to the size of the (formatted) str and returns it...
 
Status
Suppression status of a source log location.
 
void Reset() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Resets all usage to zero. Called periodically by the scheduler.
 
static std::string LogLevelToStr(BCLog::Level level)
Returns the string representation of a log level.
 
bool WillLogCategory(LogFlags category) const
 
void LogPrintStr(std::string_view str, std::source_location &&source_loc, BCLog::LogFlags category, BCLog::Level level, bool should_ratelimit) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Send a string to the log output.
 
std::string LogTimestampStr(SystemClock::time_point now, std::chrono::seconds mocktime) const
 
bool DefaultShrinkDebugFile() const
 
void SetCategoryLogLevel(const std::unordered_map< LogFlags, Level > &levels) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
 
void SetLogLevel(Level level)
 
bool WillLogCategoryLevel(LogFlags category, Level level) const EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
 
void DisableLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Disable logging This offers a slight speedup and slightly smaller memory usage compared to leaving th...
 
std::vector< LogCategory > LogCategoriesList() const
Returns a vector of the log categories in alphabetical order.
 
bool StartLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Start logging (and flush all buffered messages)
 
void EnableCategory(LogFlags flag)
 
std::string GetLogPrefix(LogFlags category, Level level) const
 
void LogPrintStr_(std::string_view str, std::source_location &&source_loc, BCLog::LogFlags category, BCLog::Level level, bool should_ratelimit) EXCLUSIVE_LOCKS_REQUIRED(m_cs)
Send a string to the log output (internal)
 
void DisconnectTestLogger() EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Only for testing.
 
std::string LogLevelsString() const
Returns a string with all user-selectable log levels.
 
void FormatLogStrInPlace(std::string &str, LogFlags category, Level level, const std::source_location &source_loc, std::string_view threadname, SystemClock::time_point now, std::chrono::seconds mocktime) const
 
void DisableCategory(LogFlags flag)
 
static constexpr std::array< BCLog::Level, 3 > LogLevelsList()
Log severity levels that can be selected by the user.
 
static int FileWriteStr(std::string_view str, FILE *fp)
 
static std::string LogCategoryToStr(BCLog::LogFlags category)
 
bool GetLogCategory(BCLog::LogFlags &flag, std::string_view str)
Return true if str parses as a log category and set the flag.
 
static size_t MemUsage(const BCLog::Logger::BufferedLog &buflog)
 
static const std::map< std::string, BCLog::LogFlags, std::less<> > LOG_CATEGORIES_BY_STR
 
BCLog::Logger & LogInstance()
 
static const std::unordered_map< BCLog::LogFlags, std::string > LOG_CATEGORIES_BY_FLAG
 
const char *const DEFAULT_DEBUGLOGFILE
 
static std::optional< BCLog::Level > GetLogLevel(std::string_view level_str)
 
constexpr auto MAX_USER_SETABLE_SEVERITY_LEVEL
 
#define LogPrintLevel_(category, level, should_ratelimit,...)
 
static const bool DEFAULT_LOGIPS
 
std::string LogEscapeMessage(std::string_view str)
Belts and suspenders: make sure outgoing log messages don't contain potentially suspicious characters...
 
constexpr size_t DEFAULT_MAX_LOG_BUFFER
 
FILE * fopen(const fs::path &p, const char *mode)
 
bool StartLogging(const ArgsManager &args)
 
static size_t DynamicUsage(const int8_t &v)
Dynamic memory usage for built-in types is zero.
 
static size_t MallocUsage(size_t alloc)
Compute the total memory used by allocating alloc bytes.
 
std::string ThreadGetInternalName()
Get the thread's internal (in-memory) name; used e.g.
 
std::string_view RemovePrefixView(std::string_view str, std::string_view prefix)
 
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
 
bool Consume(uint64_t bytes)
Updates internal accounting and returns true if enough available_bytes were remaining.
 
SystemClock::time_point now
 
std::chrono::seconds GetMockTime()
For testing.
 
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.
 
constexpr int64_t count_seconds(std::chrono::seconds t)