Skip to content

Commit 30cef53

Browse files
committed
Merge bitcoin/bitcoin#30386: Early logging improvements
b4dd7ab logging: use std::string_view (Anthony Towns) 558df5c logging: Apply formatting to early log messages (Anthony Towns) 6cf9b34 logging: Limit early logging buffer (Anthony Towns) 0b1960f logging: Add DisableLogging() (Anthony Towns) 6bbc2dd logging: Add thread safety annotations (Anthony Towns) Pull request description: In order to cope gracefully with `Log*()` calls that are invoked prior to logging being fully configured (indicated by calling `StartLogging()` we buffer early log messages in `m_msgs_before_open`. This has a couple of minor issues: * if there are many such log messages the buffer can become arbitrarily large; this can be a problem for users of libkernel that might not wish to worry about logging at all, and as a result never invoke `StartLogging()` * early log messages are formatted before the formatting options are configured, leading to inconsistent output Fix those issues by buffering the log info prior to formatting it, and setting a limit on the size of the buffer (dropping the oldest lines, and reporting the number of lines skipped). Also adds some thread safety annotations, and the ability to invoke `LogInstance().DisableLogging()` if you want to disable logging entirely, for a minor efficiency improvement. ACKs for top commit: maflcko: re-ACK b4dd7ab 🕴 ryanofsky: Code review ACK b4dd7ab TheCharlatan: Nice, ACK b4dd7ab Tree-SHA512: 966660181276939225a9f776de6ee0665e44577d2ee9cc76b06c8937297217482e6e426bdc5772d1ce533a0ba093a8556b6a50857d4c876ad8923e432a200440
2 parents 123888d + b4dd7ab commit 30cef53

File tree

4 files changed

+169
-65
lines changed

4 files changed

+169
-65
lines changed

src/bitcoin-chainstate.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include <consensus/validation.h>
2121
#include <core_io.h>
22+
#include <logging.h>
2223
#include <node/blockstorage.h>
2324
#include <node/caches.h>
2425
#include <node/chainstate.h>
@@ -41,6 +42,12 @@
4142

4243
int main(int argc, char* argv[])
4344
{
45+
// We do not enable logging for this app, so explicitly disable it.
46+
// To enable logging instead, replace with:
47+
// LogInstance().m_print_to_console = true;
48+
// LogInstance().StartLogging();
49+
LogInstance().DisableLogging();
50+
4451
// SETUP: Argument parsing and handling
4552
if (argc != 2) {
4653
std::cerr

src/logging.cpp

Lines changed: 114 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55

66
#include <logging.h>
7+
#include <memusage.h>
78
#include <util/fs.h>
89
#include <util/string.h>
910
#include <util/threadnames.h>
@@ -14,8 +15,7 @@
1415
#include <optional>
1516

1617
using util::Join;
17-
using util::RemovePrefix;
18-
using util::ToString;
18+
using util::RemovePrefixView;
1919

2020
const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
2121
constexpr auto MAX_USER_SETABLE_SEVERITY_LEVEL{BCLog::Level::Info};
@@ -43,7 +43,7 @@ BCLog::Logger& LogInstance()
4343

4444
bool fLogIPs = DEFAULT_LOGIPS;
4545

46-
static int FileWriteStr(const std::string &str, FILE *fp)
46+
static int FileWriteStr(std::string_view str, FILE *fp)
4747
{
4848
return fwrite(str.data(), 1, str.size(), fp);
4949
}
@@ -71,17 +71,22 @@ bool BCLog::Logger::StartLogging()
7171

7272
// dump buffered messages from before we opened the log
7373
m_buffering = false;
74+
if (m_buffer_lines_discarded > 0) {
75+
LogPrintStr_(strprintf("Early logging buffer overflowed, %d log lines discarded.\n", m_buffer_lines_discarded), __func__, __FILE__, __LINE__, BCLog::ALL, Level::Info);
76+
}
7477
while (!m_msgs_before_open.empty()) {
75-
const std::string& s = m_msgs_before_open.front();
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();
7682

7783
if (m_print_to_file) FileWriteStr(s, m_fileout);
7884
if (m_print_to_console) fwrite(s.data(), 1, s.size(), stdout);
7985
for (const auto& cb : m_print_callbacks) {
8086
cb(s);
8187
}
82-
83-
m_msgs_before_open.pop_front();
8488
}
89+
m_cur_buffer_memusage = 0;
8590
if (m_print_to_console) fflush(stdout);
8691

8792
return true;
@@ -94,14 +99,31 @@ void BCLog::Logger::DisconnectTestLogger()
9499
if (m_fileout != nullptr) fclose(m_fileout);
95100
m_fileout = nullptr;
96101
m_print_callbacks.clear();
102+
m_max_buffer_memusage = DEFAULT_MAX_LOG_BUFFER;
103+
m_cur_buffer_memusage = 0;
104+
m_buffer_lines_discarded = 0;
105+
m_msgs_before_open.clear();
106+
107+
}
108+
109+
void BCLog::Logger::DisableLogging()
110+
{
111+
{
112+
StdLockGuard scoped_lock(m_cs);
113+
assert(m_buffering);
114+
assert(m_print_callbacks.empty());
115+
}
116+
m_print_to_file = false;
117+
m_print_to_console = false;
118+
StartLogging();
97119
}
98120

99121
void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
100122
{
101123
m_categories |= flag;
102124
}
103125

104-
bool BCLog::Logger::EnableCategory(const std::string& str)
126+
bool BCLog::Logger::EnableCategory(std::string_view str)
105127
{
106128
BCLog::LogFlags flag;
107129
if (!GetLogCategory(flag, str)) return false;
@@ -114,7 +136,7 @@ void BCLog::Logger::DisableCategory(BCLog::LogFlags flag)
114136
m_categories &= ~flag;
115137
}
116138

117-
bool BCLog::Logger::DisableCategory(const std::string& str)
139+
bool BCLog::Logger::DisableCategory(std::string_view str)
118140
{
119141
BCLog::LogFlags flag;
120142
if (!GetLogCategory(flag, str)) return false;
@@ -145,7 +167,7 @@ bool BCLog::Logger::DefaultShrinkDebugFile() const
145167
return m_categories == BCLog::NONE;
146168
}
147169

148-
static const std::map<std::string, BCLog::LogFlags> LOG_CATEGORIES_BY_STR{
170+
static const std::map<std::string, BCLog::LogFlags, std::less<>> LOG_CATEGORIES_BY_STR{
149171
{"0", BCLog::NONE},
150172
{"", BCLog::NONE},
151173
{"net", BCLog::NET},
@@ -185,7 +207,7 @@ static const std::map<std::string, BCLog::LogFlags> LOG_CATEGORIES_BY_STR{
185207

186208
static const std::unordered_map<BCLog::LogFlags, std::string> LOG_CATEGORIES_BY_FLAG{
187209
// Swap keys and values from LOG_CATEGORIES_BY_STR.
188-
[](const std::map<std::string, BCLog::LogFlags>& in) {
210+
[](const auto& in) {
189211
std::unordered_map<BCLog::LogFlags, std::string> out;
190212
for (const auto& [k, v] : in) {
191213
switch (v) {
@@ -198,7 +220,7 @@ static const std::unordered_map<BCLog::LogFlags, std::string> LOG_CATEGORIES_BY_
198220
}(LOG_CATEGORIES_BY_STR)
199221
};
200222

201-
bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
223+
bool GetLogCategory(BCLog::LogFlags& flag, std::string_view str)
202224
{
203225
if (str.empty()) {
204226
flag = BCLog::ALL;
@@ -236,7 +258,7 @@ std::string LogCategoryToStr(BCLog::LogFlags category)
236258
return it->second;
237259
}
238260

239-
static std::optional<BCLog::Level> GetLogLevel(const std::string& level_str)
261+
static std::optional<BCLog::Level> GetLogLevel(std::string_view level_str)
240262
{
241263
if (level_str == "trace") {
242264
return BCLog::Level::Trace;
@@ -276,28 +298,23 @@ std::string BCLog::Logger::LogLevelsString() const
276298
return Join(std::vector<BCLog::Level>{levels.begin(), levels.end()}, ", ", [](BCLog::Level level) { return LogLevelToStr(level); });
277299
}
278300

279-
std::string BCLog::Logger::LogTimestampStr(const std::string& str)
301+
std::string BCLog::Logger::LogTimestampStr(SystemClock::time_point now, std::chrono::seconds mocktime) const
280302
{
281303
std::string strStamped;
282304

283305
if (!m_log_timestamps)
284-
return str;
285-
286-
if (m_started_new_line) {
287-
const auto now{SystemClock::now()};
288-
const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
289-
strStamped = FormatISO8601DateTime(TicksSinceEpoch<std::chrono::seconds>(now_seconds));
290-
if (m_log_time_micros && !strStamped.empty()) {
291-
strStamped.pop_back();
292-
strStamped += strprintf(".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
293-
}
294-
std::chrono::seconds mocktime = GetMockTime();
295-
if (mocktime > 0s) {
296-
strStamped += " (mocktime: " + FormatISO8601DateTime(count_seconds(mocktime)) + ")";
297-
}
298-
strStamped += ' ' + str;
299-
} else
300-
strStamped = str;
306+
return strStamped;
307+
308+
const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
309+
strStamped = FormatISO8601DateTime(TicksSinceEpoch<std::chrono::seconds>(now_seconds));
310+
if (m_log_time_micros && !strStamped.empty()) {
311+
strStamped.pop_back();
312+
strStamped += strprintf(".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
313+
}
314+
if (mocktime > 0s) {
315+
strStamped += " (mocktime: " + FormatISO8601DateTime(count_seconds(mocktime)) + ")";
316+
}
317+
strStamped += ' ';
301318

302319
return strStamped;
303320
}
@@ -310,7 +327,7 @@ namespace BCLog {
310327
* It escapes instead of removes them to still allow for troubleshooting
311328
* issues where they accidentally end up in strings.
312329
*/
313-
std::string LogEscapeMessage(const std::string& str) {
330+
std::string LogEscapeMessage(std::string_view str) {
314331
std::string ret;
315332
for (char ch_in : str) {
316333
uint8_t ch = (uint8_t)ch_in;
@@ -350,34 +367,84 @@ std::string BCLog::Logger::GetLogPrefix(BCLog::LogFlags category, BCLog::Level l
350367
return s;
351368
}
352369

353-
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)
370+
static size_t MemUsage(const BCLog::Logger::BufferedLog& buflog)
354371
{
355-
StdLockGuard scoped_lock(m_cs);
356-
std::string str_prefixed = LogEscapeMessage(str);
372+
return buflog.str.size() + buflog.logging_function.size() + buflog.source_file.size() + buflog.threadname.size() + memusage::MallocUsage(sizeof(memusage::list_node<BCLog::Logger::BufferedLog>));
373+
}
357374

358-
if (m_started_new_line) {
359-
str_prefixed.insert(0, GetLogPrefix(category, level));
360-
}
375+
void BCLog::Logger::FormatLogStrInPlace(std::string& str, BCLog::LogFlags category, BCLog::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
376+
{
377+
str.insert(0, GetLogPrefix(category, level));
361378

362-
if (m_log_sourcelocations && m_started_new_line) {
363-
str_prefixed.insert(0, "[" + RemovePrefix(source_file, "./") + ":" + ToString(source_line) + "] [" + logging_function + "] ");
379+
if (m_log_sourcelocations) {
380+
str.insert(0, strprintf("[%s:%d] [%s] ", RemovePrefixView(source_file, "./"), source_line, logging_function));
364381
}
365382

366-
if (m_log_threadnames && m_started_new_line) {
367-
const auto& threadname = util::ThreadGetInternalName();
368-
str_prefixed.insert(0, "[" + (threadname.empty() ? "unknown" : threadname) + "] ");
383+
if (m_log_threadnames) {
384+
str.insert(0, strprintf("[%s] ", (threadname.empty() ? "unknown" : threadname)));
369385
}
370386

371-
str_prefixed = LogTimestampStr(str_prefixed);
387+
str.insert(0, LogTimestampStr(now, mocktime));
388+
}
389+
390+
void BCLog::Logger::LogPrintStr(std::string_view str, std::string_view logging_function, std::string_view source_file, int source_line, BCLog::LogFlags category, BCLog::Level level)
391+
{
392+
StdLockGuard scoped_lock(m_cs);
393+
return LogPrintStr_(str, logging_function, source_file, source_line, category, level);
394+
}
372395

396+
void BCLog::Logger::LogPrintStr_(std::string_view str, std::string_view logging_function, std::string_view source_file, int source_line, BCLog::LogFlags category, BCLog::Level level)
397+
{
398+
std::string str_prefixed = LogEscapeMessage(str);
399+
400+
const bool starts_new_line = m_started_new_line;
373401
m_started_new_line = !str.empty() && str[str.size()-1] == '\n';
374402

375403
if (m_buffering) {
376-
// buffer if we haven't started logging yet
377-
m_msgs_before_open.push_back(str_prefixed);
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();
408+
return;
409+
} else {
410+
// unlikely edge case; add a marker that something was trimmed
411+
str_prefixed.insert(0, "[...] ");
412+
}
413+
}
414+
415+
{
416+
BufferedLog buf{
417+
.now=SystemClock::now(),
418+
.mocktime=GetMockTime(),
419+
.str=str_prefixed,
420+
.logging_function=std::string(logging_function),
421+
.source_file=std::string(source_file),
422+
.threadname=util::ThreadGetInternalName(),
423+
.source_line=source_line,
424+
.category=category,
425+
.level=level,
426+
};
427+
m_cur_buffer_memusage += MemUsage(buf);
428+
m_msgs_before_open.push_back(std::move(buf));
429+
}
430+
431+
while (m_cur_buffer_memusage > m_max_buffer_memusage) {
432+
if (m_msgs_before_open.empty()) {
433+
m_cur_buffer_memusage = 0;
434+
break;
435+
}
436+
m_cur_buffer_memusage -= MemUsage(m_msgs_before_open.front());
437+
m_msgs_before_open.pop_front();
438+
++m_buffer_lines_discarded;
439+
}
440+
378441
return;
379442
}
380443

444+
if (starts_new_line) {
445+
FormatLogStrInPlace(str_prefixed, category, level, source_file, source_line, logging_function, util::ThreadGetInternalName(), SystemClock::now(), GetMockTime());
446+
}
447+
381448
if (m_print_to_console) {
382449
// print to console
383450
fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
@@ -444,15 +511,15 @@ void BCLog::Logger::ShrinkDebugFile()
444511
fclose(file);
445512
}
446513

447-
bool BCLog::Logger::SetLogLevel(const std::string& level_str)
514+
bool BCLog::Logger::SetLogLevel(std::string_view level_str)
448515
{
449516
const auto level = GetLogLevel(level_str);
450517
if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false;
451518
m_log_level = level.value();
452519
return true;
453520
}
454521

455-
bool BCLog::Logger::SetCategoryLogLevel(const std::string& category_str, const std::string& level_str)
522+
bool BCLog::Logger::SetCategoryLogLevel(std::string_view category_str, std::string_view level_str)
456523
{
457524
BCLog::LogFlags flag;
458525
if (!GetLogCategory(flag, category_str)) return false;

0 commit comments

Comments
 (0)