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;
136 EnableCategory(*flag);
144 m_categories &= ~flag;
150 DisableCategory(*flag);
158 return (m_categories.load(std::memory_order_relaxed) & category) != 0;
167 if (!WillLogCategory(category))
return false;
170 const auto it{m_category_log_levels.find(category)};
171 return level >= (it == m_category_log_levels.end() ?
LogLevel() : it->second);
204#ifdef DEBUG_LOCKCONTENTION
218 std::unordered_map<BCLog::LogFlags, std::string>
out;
219 for (
const auto& [
k, v] : in) {
220 const bool inserted{
out.emplace(v,
k).second};
229 if (str.empty() || str ==
"1" || str ==
"all") {
242 case BCLog::Level::Trace:
244 case BCLog::Level::Debug:
266static std::optional<BCLog::Level>
GetLogLevel(std::string_view level_str)
268 if (level_str ==
"trace") {
269 return BCLog::Level::Trace;
270 }
else if (level_str ==
"debug") {
271 return BCLog::Level::Debug;
272 }
else if (level_str ==
"info") {
274 }
else if (level_str ==
"warning") {
276 }
else if (level_str ==
"error") {
285 std::vector<LogCategory>
ret;
302 return Join(std::vector<BCLog::Level>{levels.begin(), levels.end()},
", ", [](
BCLog::Level level) {
return LogLevelToStr(level); });
307 std::string strStamped;
309 if (!m_log_timestamps)
312 const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
314 if (m_log_time_micros && !strStamped.empty()) {
315 strStamped.pop_back();
316 strStamped +=
strprintf(
".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
336 for (
char ch_in : str) {
337 uint8_t ch = (uint8_t)ch_in;
338 if ((ch >= 32 || ch ==
'\n') && ch !=
'\x7f') {
352 const bool has_category{m_always_print_category_level || category !=
LogFlags::ALL};
355 if (!has_category && level ==
Level::Info)
return {};
362 if (m_always_print_category_level || !has_category || level != Level::Debug) {
366 if (has_category)
s +=
":";
382 : m_max_bytes{max_bytes}, m_reset_window{reset_window} {}
385 SchedulerFunction&& scheduler_func, uint64_t max_bytes, std::chrono::seconds reset_window)
387 auto limiter{std::shared_ptr<LogRateLimiter>(
new LogRateLimiter(max_bytes, reset_window))};
388 std::weak_ptr<LogRateLimiter> weak_limiter{limiter};
389 auto reset = [weak_limiter] {
390 if (
auto shared_limiter{weak_limiter.lock()}) shared_limiter->Reset();
392 scheduler_func(reset, limiter->m_reset_window);
398 const std::string& str)
401 auto& stats{m_source_locations.try_emplace(source_loc, m_max_bytes).first->second};
402 Status status{stats.m_dropped_bytes > 0 ? Status::STILL_SUPPRESSED : Status::UNSUPPRESSED};
404 if (!stats.Consume(str.size()) && status == Status::UNSUPPRESSED) {
405 status = Status::NEWLY_SUPPRESSED;
406 m_suppression_active =
true;
416 if (m_log_threadnames) {
420 if (m_log_sourcelocations) {
427 if (!result.ends_with(
'\n')) result +=
'\n';
434 return LogPrint_(std::move(entry));
442 m_cur_buffer_memusage +=
MemUsage(entry);
443 m_msgs_before_open.push_back(std::move(entry));
446 while (m_cur_buffer_memusage > m_max_buffer_memusage) {
447 if (m_msgs_before_open.empty()) {
448 m_cur_buffer_memusage = 0;
451 m_cur_buffer_memusage -=
MemUsage(m_msgs_before_open.front());
452 m_msgs_before_open.pop_front();
453 ++m_buffer_lines_discarded;
459 std::string str_prefixed{
Format(entry)};
460 bool ratelimit{
false};
462 auto status{m_limiter->Consume(entry.
source_loc, str_prefixed)};
468 .should_ratelimit =
false,
471 "Excessive logging detected from %s:%d (%s): >%d bytes logged during "
472 "the last time window of %is. Suppressing logging to disk from this "
473 "source location until time window resets. Console logging "
474 "unaffected. Last log entry.",
476 m_limiter->m_max_bytes,
477 Ticks<std::chrono::seconds>(m_limiter->m_reset_window)),
486 if (m_limiter && m_limiter->SuppressionsActive()) {
487 str_prefixed.insert(0,
"[*] ");
490 if (m_print_to_console) {
492 fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
495 for (
const auto& cb : m_print_callbacks) {
498 if (m_print_to_file && !ratelimit) {
499 assert(m_fileout !=
nullptr);
503 m_reopen_file =
false;
506 setbuf(new_fileout,
nullptr);
508 m_fileout = new_fileout;
518 constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
520 assert(!m_file_path.empty());
528 log_size = fs::file_size(m_file_path);
529 }
catch (
const fs::filesystem_error&) {}
533 if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
536 std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
537 if (fseek(file, -((
long)vch.size()), SEEK_END)) {
538 LogWarning(
"Failed to shrink debug log file: fseek(...) failed");
542 int nBytes = fread(vch.data(), 1, vch.size(), file);
548 fwrite(vch.data(), 1, nBytes, file);
552 else if (file !=
nullptr)
558 decltype(m_source_locations) source_locations;
561 source_locations.swap(m_source_locations);
562 m_suppression_active =
false;
564 for (
const auto& [source_loc, stats] : source_locations) {
565 if (stats.m_dropped_bytes == 0)
continue;
568 "Restarting logging from %s:%d (%s): %d bytes were dropped during the last %ss.",
569 source_loc.file_name(), source_loc.line(), source_loc.function_name_short(),
570 stats.m_dropped_bytes, Ticks<std::chrono::seconds>(m_reset_window));
576 if (bytes > m_available_bytes) {
577 m_dropped_bytes += bytes;
578 m_available_bytes = 0;
582 m_available_bytes -= bytes;
590 m_log_level = level.value();
597 if (!flag)
return false;
603 m_category_log_levels[*flag] = level.value();
615 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)
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 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
#define LogPrintLevel_(category, level, should_ratelimit,...)
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)
std::optional< BCLog::LogFlags > GetLogCategory(std::string_view str)
Return log flag if str parses as a log category.
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.
uint64_t Category
Opaque to util::log; interpreted by consumers (e.g., BCLog::LogFlags).
bool ShouldLog(Category category, Level level)
Return whether messages with specified category and level should be logged.
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)