4
4
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
5
6
6
#include < logging.h>
7
+ #include < memusage.h>
7
8
#include < util/fs.h>
8
9
#include < util/string.h>
9
10
#include < util/threadnames.h>
14
15
#include < optional>
15
16
16
17
using util::Join;
17
- using util::RemovePrefix;
18
- using util::ToString;
18
+ using util::RemovePrefixView;
19
19
20
20
const char * const DEFAULT_DEBUGLOGFILE = " debug.log" ;
21
21
constexpr auto MAX_USER_SETABLE_SEVERITY_LEVEL{BCLog::Level::Info};
@@ -43,7 +43,7 @@ BCLog::Logger& LogInstance()
43
43
44
44
bool fLogIPs = DEFAULT_LOGIPS;
45
45
46
- static int FileWriteStr (const std::string & str, FILE *fp)
46
+ static int FileWriteStr (std::string_view str, FILE *fp)
47
47
{
48
48
return fwrite (str.data (), 1 , str.size (), fp);
49
49
}
@@ -71,17 +71,22 @@ bool BCLog::Logger::StartLogging()
71
71
72
72
// dump buffered messages from before we opened the log
73
73
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
+ }
74
77
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 ();
76
82
77
83
if (m_print_to_file) FileWriteStr (s, m_fileout);
78
84
if (m_print_to_console) fwrite (s.data (), 1 , s.size (), stdout);
79
85
for (const auto & cb : m_print_callbacks) {
80
86
cb (s);
81
87
}
82
-
83
- m_msgs_before_open.pop_front ();
84
88
}
89
+ m_cur_buffer_memusage = 0 ;
85
90
if (m_print_to_console) fflush (stdout);
86
91
87
92
return true ;
@@ -94,14 +99,31 @@ void BCLog::Logger::DisconnectTestLogger()
94
99
if (m_fileout != nullptr ) fclose (m_fileout);
95
100
m_fileout = nullptr ;
96
101
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 ();
97
119
}
98
120
99
121
void BCLog::Logger::EnableCategory (BCLog::LogFlags flag)
100
122
{
101
123
m_categories |= flag;
102
124
}
103
125
104
- bool BCLog::Logger::EnableCategory (const std::string& str)
126
+ bool BCLog::Logger::EnableCategory (std::string_view str)
105
127
{
106
128
BCLog::LogFlags flag;
107
129
if (!GetLogCategory (flag, str)) return false ;
@@ -114,7 +136,7 @@ void BCLog::Logger::DisableCategory(BCLog::LogFlags flag)
114
136
m_categories &= ~flag;
115
137
}
116
138
117
- bool BCLog::Logger::DisableCategory (const std::string& str)
139
+ bool BCLog::Logger::DisableCategory (std::string_view str)
118
140
{
119
141
BCLog::LogFlags flag;
120
142
if (!GetLogCategory (flag, str)) return false ;
@@ -145,7 +167,7 @@ bool BCLog::Logger::DefaultShrinkDebugFile() const
145
167
return m_categories == BCLog::NONE;
146
168
}
147
169
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{
149
171
{" 0" , BCLog::NONE},
150
172
{" " , BCLog::NONE},
151
173
{" net" , BCLog::NET},
@@ -185,7 +207,7 @@ static const std::map<std::string, BCLog::LogFlags> LOG_CATEGORIES_BY_STR{
185
207
186
208
static const std::unordered_map<BCLog::LogFlags, std::string> LOG_CATEGORIES_BY_FLAG{
187
209
// Swap keys and values from LOG_CATEGORIES_BY_STR.
188
- [](const std::map<std::string, BCLog::LogFlags> & in) {
210
+ [](const auto & in) {
189
211
std::unordered_map<BCLog::LogFlags, std::string> out;
190
212
for (const auto & [k, v] : in) {
191
213
switch (v) {
@@ -198,7 +220,7 @@ static const std::unordered_map<BCLog::LogFlags, std::string> LOG_CATEGORIES_BY_
198
220
}(LOG_CATEGORIES_BY_STR)
199
221
};
200
222
201
- bool GetLogCategory (BCLog::LogFlags& flag, const std::string& str)
223
+ bool GetLogCategory (BCLog::LogFlags& flag, std::string_view str)
202
224
{
203
225
if (str.empty ()) {
204
226
flag = BCLog::ALL;
@@ -236,7 +258,7 @@ std::string LogCategoryToStr(BCLog::LogFlags category)
236
258
return it->second ;
237
259
}
238
260
239
- static std::optional<BCLog::Level> GetLogLevel (const std::string& level_str)
261
+ static std::optional<BCLog::Level> GetLogLevel (std::string_view level_str)
240
262
{
241
263
if (level_str == " trace" ) {
242
264
return BCLog::Level::Trace;
@@ -276,28 +298,23 @@ std::string BCLog::Logger::LogLevelsString() const
276
298
return Join (std::vector<BCLog::Level>{levels.begin (), levels.end ()}, " , " , [](BCLog::Level level) { return LogLevelToStr (level); });
277
299
}
278
300
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
280
302
{
281
303
std::string strStamped;
282
304
283
305
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 += ' ' ;
301
318
302
319
return strStamped;
303
320
}
@@ -310,7 +327,7 @@ namespace BCLog {
310
327
* It escapes instead of removes them to still allow for troubleshooting
311
328
* issues where they accidentally end up in strings.
312
329
*/
313
- std::string LogEscapeMessage (const std::string& str) {
330
+ std::string LogEscapeMessage (std::string_view str) {
314
331
std::string ret;
315
332
for (char ch_in : str) {
316
333
uint8_t ch = (uint8_t )ch_in;
@@ -350,34 +367,84 @@ std::string BCLog::Logger::GetLogPrefix(BCLog::LogFlags category, BCLog::Level l
350
367
return s;
351
368
}
352
369
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 )
354
371
{
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
+ }
357
374
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));
361
378
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) );
364
381
}
365
382
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)));
369
385
}
370
386
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
+ }
372
395
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;
373
401
m_started_new_line = !str.empty () && str[str.size ()-1 ] == ' \n ' ;
374
402
375
403
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
+
378
441
return ;
379
442
}
380
443
444
+ if (starts_new_line) {
445
+ FormatLogStrInPlace (str_prefixed, category, level, source_file, source_line, logging_function, util::ThreadGetInternalName (), SystemClock::now (), GetMockTime ());
446
+ }
447
+
381
448
if (m_print_to_console) {
382
449
// print to console
383
450
fwrite (str_prefixed.data (), 1 , str_prefixed.size (), stdout);
@@ -444,15 +511,15 @@ void BCLog::Logger::ShrinkDebugFile()
444
511
fclose (file);
445
512
}
446
513
447
- bool BCLog::Logger::SetLogLevel (const std::string& level_str)
514
+ bool BCLog::Logger::SetLogLevel (std::string_view level_str)
448
515
{
449
516
const auto level = GetLogLevel (level_str);
450
517
if (!level.has_value () || level.value () > MAX_USER_SETABLE_SEVERITY_LEVEL) return false ;
451
518
m_log_level = level.value ();
452
519
return true ;
453
520
}
454
521
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)
456
523
{
457
524
BCLog::LogFlags flag;
458
525
if (!GetLogCategory (flag, category_str)) return false ;
0 commit comments