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();
88 std::string s{
Format(buflog)};
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());
158 return (
m_categories.load(std::memory_order_relaxed) & category) != 0;
165 if (level >= BCLog::Level::Info)
return true;
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
204 {
"lock", BCLog::LOCK},
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") {
236 if (str ==
"libevent") {
237 LogWarning(
"The logging category `%s` is deprecated, does nothing, and will be removed in a future version", str);
246 case BCLog::Level::Trace:
248 case BCLog::Level::Debug:
250 case BCLog::Level::Info:
252 case BCLog::Level::Warning:
254 case BCLog::Level::Error:
270static std::optional<BCLog::Level>
GetLogLevel(std::string_view level_str)
272 if (level_str ==
"trace") {
273 return BCLog::Level::Trace;
274 }
else if (level_str ==
"debug") {
275 return BCLog::Level::Debug;
276 }
else if (level_str ==
"info") {
277 return BCLog::Level::Info;
278 }
else if (level_str ==
"warning") {
279 return BCLog::Level::Warning;
280 }
else if (level_str ==
"error") {
281 return BCLog::Level::Error;
289 std::vector<LogCategory>
ret;
300 return {BCLog::Level::Info, BCLog::Level::Debug, BCLog::Level::Trace};
311 std::string strStamped;
316 const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
319 strStamped.pop_back();
340 for (
char ch_in : str) {
341 uint8_t ch = (uint8_t)ch_in;
342 if ((ch >= 32 || ch ==
'\n') && ch !=
'\x7f') {
359 if (!has_category && level == Level::Info)
return {};
370 if (has_category) s +=
":";
380 return memusage::DynamicUsage(log.
message) +
389 SchedulerFunction&& scheduler_func, uint64_t max_bytes, std::chrono::seconds reset_window)
391 auto limiter{std::shared_ptr<LogRateLimiter>(
new LogRateLimiter(max_bytes, reset_window))};
392 std::weak_ptr<LogRateLimiter> weak_limiter{limiter};
393 auto reset = [weak_limiter] {
394 if (
auto shared_limiter{weak_limiter.lock()}) shared_limiter->Reset();
396 scheduler_func(reset, limiter->m_reset_window);
402 const std::string& str)
405 auto& stats{m_source_locations.try_emplace(source_loc,
m_max_bytes).first->second};
431 if (!result.ends_with(
'\n')) result +=
'\n';
446 m_cur_buffer_memusage +=
MemUsage(entry);
447 m_msgs_before_open.push_back(std::move(entry));
450 while (m_cur_buffer_memusage > m_max_buffer_memusage) {
451 if (m_msgs_before_open.empty()) {
452 m_cur_buffer_memusage = 0;
455 m_cur_buffer_memusage -=
MemUsage(m_msgs_before_open.front());
456 m_msgs_before_open.pop_front();
457 ++m_buffer_lines_discarded;
463 std::string str_prefixed{
Format(entry)};
464 bool ratelimit{
false};
466 auto status{m_limiter->Consume(entry.
source_loc, str_prefixed)};
471 .level = Level::Warning,
472 .should_ratelimit =
false,
475 "Excessive logging detected from %s:%d (%s): >%d bytes logged during "
476 "the last time window of %is. Suppressing logging to disk from this "
477 "source location until time window resets. Console logging "
478 "unaffected. Last log entry.",
480 m_limiter->m_max_bytes,
490 if (m_limiter && m_limiter->SuppressionsActive()) {
491 str_prefixed.insert(0,
"[*] ");
496 fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
499 for (
const auto& cb : m_print_callbacks) {
503 assert(m_fileout !=
nullptr);
510 setbuf(new_fileout,
nullptr);
512 m_fileout = new_fileout;
524 constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
535 }
catch (
const fs::filesystem_error&) {}
539 if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
542 std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
543 if (fseek(file, -((
long)vch.size()), SEEK_END)) {
547 .level = Level::Warning,
548 .should_ratelimit =
true,
550 .message =
"Failed to shrink debug log file: fseek(...) failed",
555 int nBytes = fread(vch.data(), 1, vch.size(), file);
561 fwrite(vch.data(), 1, nBytes, file);
565 else if (file !=
nullptr)
571 decltype(m_source_locations) source_locations;
574 source_locations.swap(m_source_locations);
577 for (
const auto& [source_loc, stats] : source_locations) {
578 if (stats.m_dropped_bytes == 0)
continue;
580 "Restarting logging from %s:%d (%s): %d bytes were dropped during the last %ss.",
581 source_loc.file_name(), source_loc.line(), source_loc.function_name_short(),
609 if (!flag)
return false;
616 m_category_log_levels[*flag] = level.value();
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
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
const uint64_t m_max_bytes
Maximum number of bytes logged per location per window.
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)
const std::chrono::seconds m_reset_window
Interval after which the window is reset.
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.
std::atomic< bool > m_suppression_active
Whether any log locations are suppressed. Cached view on m_source_locations for performance reasons.
static std::string LogLevelToStr(BCLog::Level level)
Returns the string representation of a log level.
bool m_always_print_category_level
bool WillLogCategory(LogFlags category) const
std::atomic< CategoryMask > m_categories
Log categories bitfield.
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).
bool m_log_sourcelocations
void SetCategoryLogLevel(const std::unordered_map< LogFlags, Level > &levels) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
void SetLogLevel(Level level)
std::atomic< Level > m_log_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.
std::atomic< bool > m_reopen_file
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
bool Enabled() const EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Returns whether logs will be written to any output.
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...
util::log::Level Level
Alias for compatibility. Prefer util::log::Level over BCLog::Level in new code.
constexpr size_t DEFAULT_MAX_LOG_BUFFER
FILE * fopen(const fs::path &p, const char *mode)
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.
constexpr NoRateLimitTag NO_RATE_LIMIT
uint64_t Category
Opaque to util::log; interpreted by consumers (e.g., BCLog::LogFlags).
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)
std::string_view RemovePrefixView(std::string_view str LIFETIMEBOUND, std::string_view prefix)
uint64_t m_available_bytes
Remaining bytes.
bool Consume(uint64_t bytes)
Updates internal accounting and returns true if enough available_bytes were remaining.
uint64_t m_dropped_bytes
Number of bytes that were consumed but didn't fit in the available bytes.
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 auto TicksSinceEpoch(Timepoint t)
constexpr int64_t count_seconds(std::chrono::seconds t)
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.