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);
203#ifdef DEBUG_LOCKCONTENTION
217 std::unordered_map<BCLog::LogFlags, std::string>
out;
218 for (
const auto& [
k, v] : in) {
219 const bool inserted{
out.emplace(v,
k).second};
228 if (str.empty() || str ==
"1" || str ==
"all") {
235 if (str ==
"libevent") {
236 LogWarning(
"The logging category `%s` is deprecated, does nothing, and will be removed in a future version", str);
245 case BCLog::Level::Trace:
247 case BCLog::Level::Debug:
269static std::optional<BCLog::Level>
GetLogLevel(std::string_view level_str)
271 if (level_str ==
"trace") {
272 return BCLog::Level::Trace;
273 }
else if (level_str ==
"debug") {
274 return BCLog::Level::Debug;
275 }
else if (level_str ==
"info") {
277 }
else if (level_str ==
"warning") {
279 }
else if (level_str ==
"error") {
288 std::vector<LogCategory>
ret;
305 return Join(std::vector<BCLog::Level>{levels.begin(), levels.end()},
", ", [](
BCLog::Level level) {
return LogLevelToStr(level); });
310 std::string strStamped;
312 if (!m_log_timestamps)
315 const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
317 if (m_log_time_micros && !strStamped.empty()) {
318 strStamped.pop_back();
319 strStamped +=
strprintf(
".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
339 for (
char ch_in : str) {
340 uint8_t ch = (uint8_t)ch_in;
341 if ((ch >= 32 || ch ==
'\n') && ch !=
'\x7f') {
355 const bool has_category{m_always_print_category_level || category !=
LogFlags::ALL};
358 if (!has_category && level ==
Level::Info)
return {};
365 if (m_always_print_category_level || !has_category || level != Level::Debug) {
369 if (has_category)
s +=
":";
385 : m_max_bytes{max_bytes}, m_reset_window{reset_window} {}
388 SchedulerFunction&& scheduler_func, uint64_t max_bytes, std::chrono::seconds reset_window)
390 auto limiter{std::shared_ptr<LogRateLimiter>(
new LogRateLimiter(max_bytes, reset_window))};
391 std::weak_ptr<LogRateLimiter> weak_limiter{limiter};
392 auto reset = [weak_limiter] {
393 if (
auto shared_limiter{weak_limiter.lock()}) shared_limiter->Reset();
395 scheduler_func(reset, limiter->m_reset_window);
401 const std::string& str)
404 auto& stats{m_source_locations.try_emplace(source_loc, m_max_bytes).first->second};
405 Status status{stats.m_dropped_bytes > 0 ? Status::STILL_SUPPRESSED : Status::UNSUPPRESSED};
407 if (!stats.Consume(str.size()) && status == Status::UNSUPPRESSED) {
408 status = Status::NEWLY_SUPPRESSED;
409 m_suppression_active =
true;
419 if (m_log_threadnames) {
423 if (m_log_sourcelocations) {
430 if (!result.ends_with(
'\n')) result +=
'\n';
437 return LogPrint_(std::move(entry));
445 m_cur_buffer_memusage +=
MemUsage(entry);
446 m_msgs_before_open.push_back(std::move(entry));
449 while (m_cur_buffer_memusage > m_max_buffer_memusage) {
450 if (m_msgs_before_open.empty()) {
451 m_cur_buffer_memusage = 0;
454 m_cur_buffer_memusage -=
MemUsage(m_msgs_before_open.front());
455 m_msgs_before_open.pop_front();
456 ++m_buffer_lines_discarded;
462 std::string str_prefixed{
Format(entry)};
463 bool ratelimit{
false};
465 auto status{m_limiter->Consume(entry.
source_loc, str_prefixed)};
471 .should_ratelimit =
false,
474 "Excessive logging detected from %s:%d (%s): >%d bytes logged during "
475 "the last time window of %is. Suppressing logging to disk from this "
476 "source location until time window resets. Console logging "
477 "unaffected. Last log entry.",
479 m_limiter->m_max_bytes,
480 Ticks<std::chrono::seconds>(m_limiter->m_reset_window)),
489 if (m_limiter && m_limiter->SuppressionsActive()) {
490 str_prefixed.insert(0,
"[*] ");
493 if (m_print_to_console) {
495 fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
498 for (
const auto& cb : m_print_callbacks) {
501 if (m_print_to_file && !ratelimit) {
502 assert(m_fileout !=
nullptr);
506 m_reopen_file =
false;
509 setbuf(new_fileout,
nullptr);
511 m_fileout = new_fileout;
523 constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
525 assert(!m_file_path.empty());
533 log_size = fs::file_size(m_file_path);
534 }
catch (
const fs::filesystem_error&) {}
538 if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
541 std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
542 if (fseek(file, -((
long)vch.size()), SEEK_END)) {
547 .should_ratelimit =
true,
549 .message =
"Failed to shrink debug log file: fseek(...) failed",
554 int nBytes = fread(vch.data(), 1, vch.size(), file);
560 fwrite(vch.data(), 1, nBytes, file);
564 else if (file !=
nullptr)
570 decltype(m_source_locations) source_locations;
573 source_locations.swap(m_source_locations);
574 m_suppression_active =
false;
576 for (
const auto& [source_loc, stats] : source_locations) {
577 if (stats.m_dropped_bytes == 0)
continue;
579 "Restarting logging from %s:%d (%s): %d bytes were dropped during the last %ss.",
580 source_loc.file_name(), source_loc.line(), source_loc.function_name_short(),
581 stats.m_dropped_bytes, Ticks<std::chrono::seconds>(m_reset_window));
587 if (bytes > m_available_bytes) {
588 m_dropped_bytes += bytes;
589 m_available_bytes = 0;
593 m_available_bytes -= bytes;
601 m_log_level = level.value();
607 const auto flag{GetLogCategory(category_str)};
608 if (!flag)
return false;
615 m_category_log_levels[*flag] = level.value();
632 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)