Skip to content

Commit 558df5c

Browse files
committed
logging: Apply formatting to early log messages
The formatting of log messages isn't defined until StartLogging() is called; so can't be correctly applied to early log messages from prior to that call. Instead of saving the output log message, save the inputs to the logging invocation (including time, mocktime and thread name), and format those inputs into a log message when StartLogging() is called.
1 parent 6cf9b34 commit 558df5c

File tree

2 files changed

+81
-43
lines changed

2 files changed

+81
-43
lines changed

src/logging.cpp

Lines changed: 66 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,16 @@ bool BCLog::Logger::StartLogging()
7676
LogPrintStr_(strprintf("Early logging buffer overflowed, %d log lines discarded.\n", m_buffer_lines_discarded), __func__, __FILE__, __LINE__, BCLog::ALL, Level::Info);
7777
}
7878
while (!m_msgs_before_open.empty()) {
79-
const std::string& s = m_msgs_before_open.front();
79+
const auto& buflog = m_msgs_before_open.front();
80+
std::string s{buflog.str};
81+
FormatLogStrInPlace(s, buflog.category, buflog.level, buflog.source_file, buflog.source_line, buflog.logging_function, buflog.threadname, buflog.now, buflog.mocktime);
82+
m_msgs_before_open.pop_front();
8083

8184
if (m_print_to_file) FileWriteStr(s, m_fileout);
8285
if (m_print_to_console) fwrite(s.data(), 1, s.size(), stdout);
8386
for (const auto& cb : m_print_callbacks) {
8487
cb(s);
8588
}
86-
87-
m_msgs_before_open.pop_front();
8889
}
8990
m_cur_buffer_memusage = 0;
9091
if (m_print_to_console) fflush(stdout);
@@ -298,28 +299,23 @@ std::string BCLog::Logger::LogLevelsString() const
298299
return Join(std::vector<BCLog::Level>{levels.begin(), levels.end()}, ", ", [](BCLog::Level level) { return LogLevelToStr(level); });
299300
}
300301

301-
std::string BCLog::Logger::LogTimestampStr(const std::string& str)
302+
std::string BCLog::Logger::LogTimestampStr(SystemClock::time_point now, std::chrono::seconds mocktime) const
302303
{
303304
std::string strStamped;
304305

305306
if (!m_log_timestamps)
306-
return str;
307-
308-
if (m_started_new_line) {
309-
const auto now{SystemClock::now()};
310-
const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
311-
strStamped = FormatISO8601DateTime(TicksSinceEpoch<std::chrono::seconds>(now_seconds));
312-
if (m_log_time_micros && !strStamped.empty()) {
313-
strStamped.pop_back();
314-
strStamped += strprintf(".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
315-
}
316-
std::chrono::seconds mocktime = GetMockTime();
317-
if (mocktime > 0s) {
318-
strStamped += " (mocktime: " + FormatISO8601DateTime(count_seconds(mocktime)) + ")";
319-
}
320-
strStamped += ' ' + str;
321-
} else
322-
strStamped = str;
307+
return strStamped;
308+
309+
const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
310+
strStamped = FormatISO8601DateTime(TicksSinceEpoch<std::chrono::seconds>(now_seconds));
311+
if (m_log_time_micros && !strStamped.empty()) {
312+
strStamped.pop_back();
313+
strStamped += strprintf(".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
314+
}
315+
if (mocktime > 0s) {
316+
strStamped += " (mocktime: " + FormatISO8601DateTime(count_seconds(mocktime)) + ")";
317+
}
318+
strStamped += ' ';
323319

324320
return strStamped;
325321
}
@@ -372,9 +368,24 @@ std::string BCLog::Logger::GetLogPrefix(BCLog::LogFlags category, BCLog::Level l
372368
return s;
373369
}
374370

375-
static size_t MemUsage(const std::string& str)
371+
static size_t MemUsage(const BCLog::Logger::BufferedLog& buflog)
376372
{
377-
return str.size() + memusage::MallocUsage(sizeof(memusage::list_node<std::string>));
373+
return buflog.str.size() + buflog.logging_function.size() + buflog.source_file.size() + buflog.threadname.size() + memusage::MallocUsage(sizeof(memusage::list_node<BCLog::Logger::BufferedLog>));
374+
}
375+
376+
void BCLog::Logger::FormatLogStrInPlace(std::string& str, BCLog::LogFlags category, BCLog::Level level, const std::string& source_file, int source_line, const std::string& logging_function, const std::string& threadname, SystemClock::time_point now, std::chrono::seconds mocktime) const
377+
{
378+
str.insert(0, GetLogPrefix(category, level));
379+
380+
if (m_log_sourcelocations) {
381+
str.insert(0, "[" + RemovePrefix(source_file, "./") + ":" + ToString(source_line) + "] [" + logging_function + "] ");
382+
}
383+
384+
if (m_log_threadnames) {
385+
str.insert(0, "[" + (threadname.empty() ? "unknown" : threadname) + "] ");
386+
}
387+
388+
str.insert(0, LogTimestampStr(now, mocktime));
378389
}
379390

380391
void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, int source_line, BCLog::LogFlags category, BCLog::Level level)
@@ -387,28 +398,37 @@ void BCLog::Logger::LogPrintStr_(const std::string& str, const std::string& logg
387398
{
388399
std::string str_prefixed = LogEscapeMessage(str);
389400

390-
if (m_started_new_line) {
391-
str_prefixed.insert(0, GetLogPrefix(category, level));
392-
}
393-
394-
if (m_log_sourcelocations && m_started_new_line) {
395-
str_prefixed.insert(0, "[" + RemovePrefix(source_file, "./") + ":" + ToString(source_line) + "] [" + logging_function + "] ");
396-
}
397-
398-
if (m_log_threadnames && m_started_new_line) {
399-
const auto& threadname = util::ThreadGetInternalName();
400-
str_prefixed.insert(0, "[" + (threadname.empty() ? "unknown" : threadname) + "] ");
401-
}
402-
403-
str_prefixed = LogTimestampStr(str_prefixed);
404-
401+
const bool starts_new_line = m_started_new_line;
405402
m_started_new_line = !str.empty() && str[str.size()-1] == '\n';
406403

407404
if (m_buffering) {
408-
// buffer if we haven't started logging yet
409-
m_msgs_before_open.push_back(str_prefixed);
405+
if (!starts_new_line) {
406+
if (!m_msgs_before_open.empty()) {
407+
m_msgs_before_open.back().str += str_prefixed;
408+
m_cur_buffer_memusage += str_prefixed.size();
409+
return;
410+
} else {
411+
// unlikely edge case; add a marker that something was trimmed
412+
str_prefixed.insert(0, "[...] ");
413+
}
414+
}
415+
416+
{
417+
BufferedLog buf{
418+
.now=SystemClock::now(),
419+
.mocktime=GetMockTime(),
420+
.str=str_prefixed,
421+
.logging_function=logging_function,
422+
.source_file=source_file,
423+
.threadname=util::ThreadGetInternalName(),
424+
.source_line=source_line,
425+
.category=category,
426+
.level=level,
427+
};
428+
m_cur_buffer_memusage += MemUsage(buf);
429+
m_msgs_before_open.push_back(std::move(buf));
430+
}
410431

411-
m_cur_buffer_memusage += MemUsage(str_prefixed);
412432
while (m_cur_buffer_memusage > m_max_buffer_memusage) {
413433
if (m_msgs_before_open.empty()) {
414434
m_cur_buffer_memusage = 0;
@@ -418,9 +438,14 @@ void BCLog::Logger::LogPrintStr_(const std::string& str, const std::string& logg
418438
m_msgs_before_open.pop_front();
419439
++m_buffer_lines_discarded;
420440
}
441+
421442
return;
422443
}
423444

445+
if (starts_new_line) {
446+
FormatLogStrInPlace(str_prefixed, category, level, source_file, source_line, logging_function, util::ThreadGetInternalName(), SystemClock::now(), GetMockTime());
447+
}
448+
424449
if (m_print_to_console) {
425450
// print to console
426451
fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);

src/logging.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <tinyformat.h>
1111
#include <util/fs.h>
1212
#include <util/string.h>
13+
#include <util/time.h>
1314

1415
#include <atomic>
1516
#include <cstdint>
@@ -83,11 +84,21 @@ namespace BCLog {
8384

8485
class Logger
8586
{
87+
public:
88+
struct BufferedLog {
89+
SystemClock::time_point now;
90+
std::chrono::seconds mocktime;
91+
std::string str, logging_function, source_file, threadname;
92+
int source_line;
93+
LogFlags category;
94+
Level level;
95+
};
96+
8697
private:
8798
mutable StdMutex m_cs; // Can not use Mutex from sync.h because in debug mode it would cause a deadlock when a potential deadlock was detected
8899

89100
FILE* m_fileout GUARDED_BY(m_cs) = nullptr;
90-
std::list<std::string> m_msgs_before_open GUARDED_BY(m_cs);
101+
std::list<BufferedLog> m_msgs_before_open GUARDED_BY(m_cs);
91102
bool m_buffering GUARDED_BY(m_cs) = true; //!< Buffer messages before logging can be started.
92103
size_t m_max_buffer_memusage GUARDED_BY(m_cs){DEFAULT_MAX_LOG_BUFFER};
93104
size_t m_cur_buffer_memusage GUARDED_BY(m_cs){0};
@@ -110,7 +121,9 @@ namespace BCLog {
110121
/** Log categories bitfield. */
111122
std::atomic<uint32_t> m_categories{0};
112123

113-
std::string LogTimestampStr(const std::string& str);
124+
void FormatLogStrInPlace(std::string& str, LogFlags category, Level level, const std::string& source_file, int source_line, const std::string& logging_function, const std::string& threadname, SystemClock::time_point now, std::chrono::seconds mocktime) const;
125+
126+
std::string LogTimestampStr(SystemClock::time_point now, std::chrono::seconds mocktime) const;
114127

115128
/** Slots that connect to the print signal */
116129
std::list<std::function<void(const std::string&)>> m_print_callbacks GUARDED_BY(m_cs) {};

0 commit comments

Comments
 (0)