52 return fwrite(str.data(), 1, str.size(), fp);
60 assert(m_fileout ==
nullptr);
69 setbuf(m_fileout,
nullptr);
78 if (m_buffer_lines_discarded > 0) {
82 .should_ratelimit =
false,
84 .message =
strprintf(
"Early logging buffer overflowed, %d log lines discarded.", m_buffer_lines_discarded),
87 while (!m_msgs_before_open.empty()) {
88 const auto& buflog = m_msgs_before_open.front();
90 m_msgs_before_open.pop_front();
94 for (
const auto& cb : m_print_callbacks) {
98 m_cur_buffer_memusage = 0;
108 if (m_fileout !=
nullptr) fclose(m_fileout);
110 m_print_callbacks.clear();
112 m_cur_buffer_memusage = 0;
113 m_buffer_lines_discarded = 0;
114 m_msgs_before_open.clear();
122 assert(m_print_callbacks.empty());
124 m_print_to_file =
false;
125 m_print_to_console =
false;
131 m_categories |= flag;
136 if (
const auto flag{GetLogCategory(str)}) {
137 EnableCategory(*flag);
145 m_categories &= ~flag;
150 if (
const auto flag{GetLogCategory(str)}) {
151 DisableCategory(*flag);
159 return (m_categories.load(std::memory_order_relaxed) & category) != 0;
168 if (!WillLogCategory(category))
return false;
171 const auto it{m_category_log_levels.find(category)};
172 return level >= (it == m_category_log_levels.end() ?
LogLevel() : it->second);
204#ifdef DEBUG_LOCKCONTENTION
205 {
"lock", BCLog::LOCK},
219 std::unordered_map<BCLog::LogFlags, std::string>
out;
220 for (
const auto& [
k, v] : in) {
221 const bool inserted{
out.emplace(v,
k).second};
230 if (str.empty() || str ==
"1" || str ==
"all") {
237 if (str ==
"libevent") {
238 LogWarning(
"The logging category `%s` is deprecated, does nothing, and will be removed in a future version", str);
247 case BCLog::Level::Trace:
249 case BCLog::Level::Debug:
271static std::optional<BCLog::Level>
GetLogLevel(std::string_view level_str)
273 if (level_str ==
"trace") {
274 return BCLog::Level::Trace;
275 }
else if (level_str ==
"debug") {
276 return BCLog::Level::Debug;
277 }
else if (level_str ==
"info") {
279 }
else if (level_str ==
"warning") {
281 }
else if (level_str ==
"error") {
290 std::vector<LogCategory>
ret;
307 return Join(std::vector<BCLog::Level>{levels.begin(), levels.end()},
", ", [](
BCLog::Level level) {
return LogLevelToStr(level); });
312 std::string strStamped;
314 if (!m_log_timestamps)
317 const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
319 if (m_log_time_micros && !strStamped.empty()) {
320 strStamped.pop_back();
321 strStamped +=
strprintf(
".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
342 for (
char ch_in : str) {
343 uint8_t ch = (uint8_t)ch_in;
344 if (ch >= 32 && ch !=
'\x7f') {
358 const bool has_category{m_always_print_category_level || category !=
LogFlags::ALL};
361 if (!has_category && level ==
Level::Info)
return {};
368 if (m_always_print_category_level || !has_category || level != Level::Debug) {
372 if (has_category)
s +=
":";
388 : m_max_bytes{max_bytes}, m_reset_window{reset_window} {}
391 SchedulerFunction&& scheduler_func, uint64_t max_bytes, std::chrono::seconds reset_window)
393 auto limiter{std::shared_ptr<LogRateLimiter>(
new LogRateLimiter(max_bytes, reset_window))};
394 std::weak_ptr<LogRateLimiter> weak_limiter{limiter};
395 auto reset = [weak_limiter] {
396 if (
auto shared_limiter{weak_limiter.lock()}) shared_limiter->Reset();
398 scheduler_func(reset, limiter->m_reset_window);
404 const std::string& str)
407 auto& stats{m_source_locations.try_emplace(source_loc, m_max_bytes).first->second};
408 Status status{stats.m_dropped_bytes > 0 ? Status::STILL_SUPPRESSED : Status::UNSUPPRESSED};
410 if (!stats.Consume(str.size()) && status == Status::UNSUPPRESSED) {
411 status = Status::NEWLY_SUPPRESSED;
412 m_suppression_active =
true;
422 if (m_log_threadnames) {
426 if (m_log_sourcelocations) {
440 return LogPrint_(std::move(entry));
448 m_cur_buffer_memusage +=
MemUsage(entry);
449 m_msgs_before_open.push_back(std::move(entry));
452 while (m_cur_buffer_memusage > m_max_buffer_memusage) {
453 if (m_msgs_before_open.empty()) {
454 m_cur_buffer_memusage = 0;
457 m_cur_buffer_memusage -=
MemUsage(m_msgs_before_open.front());
458 m_msgs_before_open.pop_front();
459 ++m_buffer_lines_discarded;
465 std::string str_prefixed{
Format(entry)};
466 bool ratelimit{
false};
468 auto status{m_limiter->Consume(entry.
source_loc, str_prefixed)};
474 .should_ratelimit =
false,
477 "Excessive logging detected from %s:%d (%s): >%d bytes logged during "
478 "the last time window of %is. Suppressing logging to disk from this "
479 "source location until time window resets. Console logging "
480 "unaffected. Last log entry.",
482 m_limiter->m_max_bytes,
483 Ticks<std::chrono::seconds>(m_limiter->m_reset_window)),
492 if (m_limiter && m_limiter->SuppressionsActive()) {
493 str_prefixed.insert(0,
"[*] ");
496 if (m_print_to_console) {
498 fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
501 for (
const auto& cb : m_print_callbacks) {
504 if (m_print_to_file && !ratelimit) {
505 assert(m_fileout !=
nullptr);
509 m_reopen_file =
false;
512 setbuf(new_fileout,
nullptr);
514 m_fileout = new_fileout;
526 constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
528 assert(!m_file_path.empty());
536 log_size = fs::file_size(m_file_path);
537 }
catch (
const fs::filesystem_error&) {}
541 if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
544 std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
545 if (fseek(file, -((
long)vch.size()), SEEK_END)) {
550 .should_ratelimit =
true,
552 .message =
"Failed to shrink debug log file: fseek(...) failed",
557 int nBytes = fread(vch.data(), 1, vch.size(), file);
563 fwrite(vch.data(), 1, nBytes, file);
567 else if (file !=
nullptr)
573 decltype(m_source_locations) source_locations;
576 source_locations.swap(m_source_locations);
577 m_suppression_active =
false;
579 for (
const auto& [source_loc, stats] : source_locations) {
580 if (stats.m_dropped_bytes == 0)
continue;
582 "Restarting logging from %s:%d (%s): %d bytes were dropped during the last %ss.",
583 source_loc.file_name(), source_loc.line(), source_loc.function_name_short(),
584 stats.m_dropped_bytes, Ticks<std::chrono::seconds>(m_reset_window));
590 if (bytes > m_available_bytes) {
591 m_dropped_bytes += bytes;
592 m_available_bytes = 0;
596 m_available_bytes -= bytes;
604 m_log_level = level.value();
610 const auto flag{GetLogCategory(category_str)};
611 if (!flag)
return false;
618 m_category_log_levels[*flag] = level.value();
635 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
constexpr 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)
Return whether messages with specified category should be debug logged.
bool ShouldTraceLog(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 RemoveSuffixView(std::string_view str LIFETIMEBOUND, std::string_view suffix)
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
std::string_view RemovePrefixView(std::string_view str LIFETIMEBOUND, std::string_view prefix)
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)