48 return fwrite(str.data(), 1, str.size(), fp);
56 assert(m_fileout ==
nullptr);
65 setbuf(m_fileout,
nullptr);
74 if (m_buffer_lines_discarded > 0) {
77 while (!m_msgs_before_open.empty()) {
78 const auto& buflog = m_msgs_before_open.front();
79 std::string s{buflog.str};
80 FormatLogStrInPlace(s, buflog.category, buflog.level, buflog.source_file, buflog.source_line, buflog.logging_function, buflog.threadname, buflog.now, buflog.mocktime);
81 m_msgs_before_open.pop_front();
85 for (
const auto& cb : m_print_callbacks) {
89 m_cur_buffer_memusage = 0;
99 if (m_fileout !=
nullptr) fclose(m_fileout);
101 m_print_callbacks.clear();
103 m_cur_buffer_memusage = 0;
104 m_buffer_lines_discarded = 0;
105 m_msgs_before_open.clear();
114 assert(m_print_callbacks.empty());
116 m_print_to_file =
false;
117 m_print_to_console =
false;
123 m_categories |= flag;
130 EnableCategory(flag);
136 m_categories &= ~flag;
143 DisableCategory(flag);
149 return (m_categories.load(std::memory_order_relaxed) & category) != 0;
158 if (!WillLogCategory(category))
return false;
161 const auto it{m_category_log_levels.find(category)};
162 return level >= (it == m_category_log_levels.end() ? LogLevel() : it->second);
197 #ifdef DEBUG_LOCKCONTENTION
211 std::unordered_map<BCLog::LogFlags, std::string>
out;
212 for (
const auto& [
k, v] : in) {
216 default:
out.emplace(v,
k);
261 static std::optional<BCLog::Level>
GetLogLevel(std::string_view level_str)
263 if (level_str ==
"trace") {
265 }
else if (level_str ==
"debug") {
267 }
else if (level_str ==
"info") {
269 }
else if (level_str ==
"warning") {
271 }
else if (level_str ==
"error") {
280 std::vector<LogCategory>
ret;
298 return Join(std::vector<BCLog::Level>{levels.begin(), levels.end()},
", ", [](
BCLog::Level level) {
return LogLevelToStr(level); });
303 std::string strStamped;
305 if (!m_log_timestamps)
308 const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
310 if (m_log_time_micros && !strStamped.empty()) {
311 strStamped.pop_back();
312 strStamped +=
strprintf(
".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
332 for (
char ch_in : str) {
333 uint8_t ch = (uint8_t)ch_in;
334 if ((ch >= 32 || ch ==
'\n') && ch !=
'\x7f') {
348 const bool has_category{m_always_print_category_level || category !=
LogFlags::ALL};
351 if (!has_category && level ==
Level::Info)
return {};
358 if (m_always_print_category_level || !has_category || level !=
Level::Debug) {
362 if (has_category) s +=
":";
377 str.insert(0, GetLogPrefix(category, level));
379 if (m_log_sourcelocations) {
383 if (m_log_threadnames) {
384 str.insert(0,
strprintf(
"[%s] ", (threadname.empty() ?
"unknown" : threadname)));
387 str.insert(0, LogTimestampStr(now, mocktime));
393 return LogPrintStr_(str, logging_function, source_file, source_line, category, level);
400 const bool starts_new_line = m_started_new_line;
401 m_started_new_line = !str.empty() && str[str.size()-1] ==
'\n';
404 if (!starts_new_line) {
405 if (!m_msgs_before_open.empty()) {
406 m_msgs_before_open.back().str += str_prefixed;
407 m_cur_buffer_memusage += str_prefixed.size();
411 str_prefixed.insert(0,
"[...] ");
417 .
now=SystemClock::now(),
420 .logging_function=std::string(logging_function),
421 .source_file=std::string(source_file),
423 .source_line=source_line,
427 m_cur_buffer_memusage +=
MemUsage(buf);
428 m_msgs_before_open.push_back(std::move(buf));
431 while (m_cur_buffer_memusage > m_max_buffer_memusage) {
432 if (m_msgs_before_open.empty()) {
433 m_cur_buffer_memusage = 0;
436 m_cur_buffer_memusage -=
MemUsage(m_msgs_before_open.front());
437 m_msgs_before_open.pop_front();
438 ++m_buffer_lines_discarded;
444 if (starts_new_line) {
448 if (m_print_to_console) {
450 fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
453 for (
const auto& cb : m_print_callbacks) {
456 if (m_print_to_file) {
457 assert(m_fileout !=
nullptr);
461 m_reopen_file =
false;
464 setbuf(new_fileout,
nullptr);
466 m_fileout = new_fileout;
476 constexpr
size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
478 assert(!m_file_path.empty());
486 log_size = fs::file_size(m_file_path);
487 }
catch (
const fs::filesystem_error&) {}
491 if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
494 std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
495 if (fseek(file, -((
long)vch.size()), SEEK_END)) {
496 LogPrintf(
"Failed to shrink debug log file: fseek(...) failed\n");
500 int nBytes = fread(vch.data(), 1, vch.size(), file);
506 fwrite(vch.data(), 1, nBytes, file);
510 else if (file !=
nullptr)
518 m_log_level = level.value();
531 m_category_log_levels[flag] = level.value();
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 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)
void FormatLogStrInPlace(std::string &str, LogFlags category, Level level, std::string_view source_file, int source_line, std::string_view logging_function, std::string_view threadname, SystemClock::time_point now, std::chrono::seconds mocktime) const
std::string GetLogPrefix(LogFlags category, Level level) const
void LogPrintStr(std::string_view str, std::string_view logging_function, std::string_view source_file, int source_line, BCLog::LogFlags category, BCLog::Level level) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Send a string to the log output.
void DisconnectTestLogger() EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Only for testing.
std::string LogLevelsString() const
Returns a string with all user-selectable log levels.
void DisableCategory(LogFlags flag)
void LogPrintStr_(std::string_view str, std::string_view logging_function, std::string_view source_file, int source_line, BCLog::LogFlags category, BCLog::Level level) EXCLUSIVE_LOCKS_REQUIRED(m_cs)
Send a string to the log output (internal)
static int FileWriteStr(std::string_view str, FILE *fp)
bool GetLogCategory(BCLog::LogFlags &flag, std::string_view str)
Return true if str parses as a log category and set the flag.
static constexpr std::array< BCLog::Level, 3 > LogLevelsList()
Log severity levels that can be selected by the user.
static size_t MemUsage(const BCLog::Logger::BufferedLog &buflog)
static std::optional< BCLog::Level > GetLogLevel(std::string_view level_str)
std::string LogCategoryToStr(BCLog::LogFlags category)
static const std::map< std::string, BCLog::LogFlags, std::less<> > LOG_CATEGORIES_BY_STR
static const std::unordered_map< BCLog::LogFlags, std::string > LOG_CATEGORIES_BY_FLAG
const char *const DEFAULT_DEBUGLOGFILE
constexpr auto MAX_USER_SETABLE_SEVERITY_LEVEL
BCLog::Logger & LogInstance()
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 MallocUsage(size_t alloc)
Compute the total memory used by allocating alloc bytes.
std::string ThreadGetInternalName()
Get the thread's internal (in-memory) name; used e.g.
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.
SystemClock::time_point now
std::string logging_function
std::chrono::seconds GetMockTime()
For testing.
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.
constexpr int64_t count_seconds(std::chrono::seconds t)