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) {
81 .should_ratelimit =
false,
83 .message =
strprintf(
"Early logging buffer overflowed, %d log lines discarded.", m_buffer_lines_discarded),
86 while (!m_msgs_before_open.empty()) {
87 const auto& buflog = m_msgs_before_open.front();
89 m_msgs_before_open.pop_front();
93 for (
const auto& cb : m_print_callbacks) {
97 m_cur_buffer_memusage = 0;
107 if (m_fileout !=
nullptr) fclose(m_fileout);
109 m_print_callbacks.clear();
111 m_cur_buffer_memusage = 0;
112 m_buffer_lines_discarded = 0;
113 m_msgs_before_open.clear();
121 assert(m_print_callbacks.empty());
123 m_print_to_file =
false;
124 m_print_to_console =
false;
130 m_categories |= flag;
135 if (
const auto flag{GetLogCategory(str)}) {
137 LogWarning(
"The logging category `%s` is deprecated, can not be enabled, and will be removed in a future version", str);
141 EnableCategory(*flag);
149 m_categories &= ~flag;
154 if (
const auto flag{GetLogCategory(str)}) {
156 LogWarning(
"The logging category `%s` is deprecated and will be removed in a future version", str);
160 DisableCategory(*flag);
168 return (m_categories.load(std::memory_order_relaxed) & category) != 0;
177 if (!WillLogCategory(category))
return false;
180 const auto it{m_category_log_levels.find(category)};
181 return level >= (it == m_category_log_levels.end() ?
LogLevel() : it->second);
214#ifdef DEBUG_LOCKCONTENTION
228 std::unordered_map<BCLog::LogFlags, std::string>
out;
229 for (
const auto& [
k, v] : in) {
230 const bool inserted{
out.emplace(v,
k).second};
239 if (str.empty() || str ==
"1" || str ==
"all") {
252 case BCLog::Level::Trace:
254 case BCLog::Level::Debug:
276static std::optional<BCLog::Level>
GetLogLevel(std::string_view level_str)
278 if (level_str ==
"trace") {
279 return BCLog::Level::Trace;
280 }
else if (level_str ==
"debug") {
281 return BCLog::Level::Debug;
282 }
else if (level_str ==
"info") {
284 }
else if (level_str ==
"warning") {
286 }
else if (level_str ==
"error") {
295 std::vector<LogCategory>
ret;
312 return Join(std::vector<BCLog::Level>{levels.begin(), levels.end()},
", ", [](
BCLog::Level level) {
return LogLevelToStr(level); });
317 std::string strStamped;
319 if (!m_log_timestamps)
322 const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
324 if (m_log_time_micros && !strStamped.empty()) {
325 strStamped.pop_back();
326 strStamped +=
strprintf(
".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
346 for (
char ch_in : str) {
347 uint8_t ch = (uint8_t)ch_in;
348 if ((ch >= 32 || ch ==
'\n') && ch !=
'\x7f') {
362 const bool has_category{m_always_print_category_level || category !=
LogFlags::ALL};
365 if (!has_category && level ==
Level::Info)
return {};
372 if (m_always_print_category_level || !has_category || level != Level::Debug) {
376 if (has_category)
s +=
":";
392 : m_max_bytes{max_bytes}, m_reset_window{reset_window} {}
395 SchedulerFunction&& scheduler_func, uint64_t max_bytes, std::chrono::seconds reset_window)
397 auto limiter{std::shared_ptr<LogRateLimiter>(
new LogRateLimiter(max_bytes, reset_window))};
398 std::weak_ptr<LogRateLimiter> weak_limiter{limiter};
399 auto reset = [weak_limiter] {
400 if (
auto shared_limiter{weak_limiter.lock()}) shared_limiter->Reset();
402 scheduler_func(reset, limiter->m_reset_window);
408 const std::string& str)
411 auto& stats{m_source_locations.try_emplace(source_loc, m_max_bytes).first->second};
412 Status status{stats.m_dropped_bytes > 0 ? Status::STILL_SUPPRESSED : Status::UNSUPPRESSED};
414 if (!stats.Consume(str.size()) && status == Status::UNSUPPRESSED) {
415 status = Status::NEWLY_SUPPRESSED;
416 m_suppression_active =
true;
426 if (m_log_threadnames) {
430 if (m_log_sourcelocations) {
437 if (!result.ends_with(
'\n')) result +=
'\n';
444 return LogPrint_(std::move(entry));
452 m_cur_buffer_memusage +=
MemUsage(entry);
453 m_msgs_before_open.push_back(std::move(entry));
456 while (m_cur_buffer_memusage > m_max_buffer_memusage) {
457 if (m_msgs_before_open.empty()) {
458 m_cur_buffer_memusage = 0;
461 m_cur_buffer_memusage -=
MemUsage(m_msgs_before_open.front());
462 m_msgs_before_open.pop_front();
463 ++m_buffer_lines_discarded;
469 std::string str_prefixed{
Format(entry)};
470 bool ratelimit{
false};
472 auto status{m_limiter->Consume(entry.
source_loc, str_prefixed)};
478 .should_ratelimit =
false,
481 "Excessive logging detected from %s:%d (%s): >%d bytes logged during "
482 "the last time window of %is. Suppressing logging to disk from this "
483 "source location until time window resets. Console logging "
484 "unaffected. Last log entry.",
486 m_limiter->m_max_bytes,
487 Ticks<std::chrono::seconds>(m_limiter->m_reset_window)),
496 if (m_limiter && m_limiter->SuppressionsActive()) {
497 str_prefixed.insert(0,
"[*] ");
500 if (m_print_to_console) {
502 fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
505 for (
const auto& cb : m_print_callbacks) {
508 if (m_print_to_file && !ratelimit) {
509 assert(m_fileout !=
nullptr);
513 m_reopen_file =
false;
516 setbuf(new_fileout,
nullptr);
518 m_fileout = new_fileout;
530 constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
532 assert(!m_file_path.empty());
540 log_size = fs::file_size(m_file_path);
541 }
catch (
const fs::filesystem_error&) {}
545 if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
548 std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
549 if (fseek(file, -((
long)vch.size()), SEEK_END)) {
554 .should_ratelimit =
true,
556 .message =
"Failed to shrink debug log file: fseek(...) failed",
561 int nBytes = fread(vch.data(), 1, vch.size(), file);
567 fwrite(vch.data(), 1, nBytes, file);
571 else if (file !=
nullptr)
577 decltype(m_source_locations) source_locations;
580 source_locations.swap(m_source_locations);
581 m_suppression_active =
false;
583 for (
const auto& [source_loc, stats] : source_locations) {
584 if (stats.m_dropped_bytes == 0)
continue;
586 "Restarting logging from %s:%d (%s): %d bytes were dropped during the last %ss.",
587 source_loc.file_name(), source_loc.line(), source_loc.function_name_short(),
588 stats.m_dropped_bytes, Ticks<std::chrono::seconds>(m_reset_window));
594 if (bytes > m_available_bytes) {
595 m_dropped_bytes += bytes;
596 m_available_bytes = 0;
600 m_available_bytes -= bytes;
608 m_log_level = level.value();
614 const auto flag{GetLogCategory(category_str)};
615 if (!flag)
return false;
621 m_category_log_levels[*flag] = level.value();
638 if (logger.Enabled()) {
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 SourceLocation &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
std::string LogTimestampStr(SystemClock::time_point now, std::chrono::seconds mocktime) const
bool DefaultShrinkDebugFile() const
void LogPrint_(util::log::Entry log_entry) EXCLUSIVE_LOCKS_REQUIRED(m_cs)
Send an entry to the log output (internal)
void SetCategoryLogLevel(const std::unordered_map< LogFlags, Level > &levels) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
void SetLogLevel(Level level)
void ShrinkDebugFile() EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
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.
static std::optional< BCLog::LogFlags > GetLogCategory(std::string_view str)
Return log flag if str parses as a log category.
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 DisconnectTestLogger() EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Only for testing.
std::string LogLevelsString() const
Returns a string with all user-selectable log levels.
void LogPrint(util::log::Entry log_entry) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Send an entry to the log output.
std::string Format(const util::log::Entry &entry) const
void DisableCategory(LogFlags flag)
Like std::source_location, but allowing to override the function name.
std::string_view function_name_short() const
std::string_view file_name() const
std::uint_least32_t line() const
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)
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)
static size_t MemUsage(const util::log::Entry &log)
constexpr auto MAX_USER_SETABLE_SEVERITY_LEVEL
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.
void Log(Entry entry)
Send message to be logged.
bool ShouldDebugLog(Category category)
Return whether messages with specified category should be debug logged.
bool ShouldTraceLog(Category category)
Return whether messages with specified category should be trace logged.
uint64_t Category
Opaque to util::log; interpreted by consumers (e.g., BCLog::LogFlags).
constexpr NoRateLimitTag NO_RATE_LIMIT
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.
SourceLocation source_loc
SystemClock::time_point timestamp
bool should_ratelimit
Hint for consumers if this entry should be ratelimited.
std::chrono::seconds mocktime
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.
constexpr int64_t count_seconds(std::chrono::seconds t)