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)}) {
136 EnableCategory(*flag);
144 m_categories &= ~flag;
149 if (
const auto flag{GetLogCategory(str)}) {
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;
520 constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
522 assert(!m_file_path.empty());
530 log_size = fs::file_size(m_file_path);
531 }
catch (
const fs::filesystem_error&) {}
535 if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
538 std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
539 if (fseek(file, -((
long)vch.size()), SEEK_END)) {
544 .should_ratelimit =
true,
546 .message =
"Failed to shrink debug log file: fseek(...) failed",
551 int nBytes = fread(vch.data(), 1, vch.size(), file);
557 fwrite(vch.data(), 1, nBytes, file);
561 else if (file !=
nullptr)
567 decltype(m_source_locations) source_locations;
570 source_locations.swap(m_source_locations);
571 m_suppression_active =
false;
573 for (
const auto& [source_loc, stats] : source_locations) {
574 if (stats.m_dropped_bytes == 0)
continue;
576 "Restarting logging from %s:%d (%s): %d bytes were dropped during the last %ss.",
577 source_loc.file_name(), source_loc.line(), source_loc.function_name_short(),
578 stats.m_dropped_bytes, Ticks<std::chrono::seconds>(m_reset_window));
584 if (bytes > m_available_bytes) {
585 m_dropped_bytes += bytes;
586 m_available_bytes = 0;
590 m_available_bytes -= bytes;
598 m_log_level = level.value();
604 const auto flag{GetLogCategory(category_str)};
605 if (!flag)
return false;
611 m_category_log_levels[*flag] = level.value();
628 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)