Bitcoin ABC 0.30.7
P2P Digital Currency
logging.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-2016 The Bitcoin Core developers
3// Copyright (c) 2017-2019 The Bitcoin developers
4// Distributed under the MIT software license, see the accompanying
5// file COPYING or http://www.opensource.org/licenses/mit-license.php.
6
7#include <logging.h>
8#include <util/fs.h>
9
10#include <util/string.h>
11#include <util/threadnames.h>
12#include <util/time.h>
13
14#include <array>
15#include <map>
16#include <unordered_map>
17
19const char *const DEFAULT_DEBUGLOGFILE = "debug.log";
20
37 static BCLog::Logger *g_logger{new BCLog::Logger()};
38 return *g_logger;
39}
40
41static int FileWriteStr(const std::string &str, FILE *fp) {
42 return fwrite(str.data(), 1, str.size(), fp);
43}
44
46 StdLockGuard scoped_lock(m_cs);
47
48 assert(m_buffering);
49 assert(m_fileout == nullptr);
50
51 if (m_print_to_file) {
52 assert(!m_file_path.empty());
53 m_fileout = fsbridge::fopen(m_file_path, "a");
54 if (!m_fileout) {
55 return false;
56 }
57
58 // Unbuffered.
59 setbuf(m_fileout, nullptr);
60
61 // Add newlines to the logfile to distinguish this execution from the
62 // last one.
63 FileWriteStr("\n\n\n\n\n", m_fileout);
64 }
65
66 // Dump buffered messages from before we opened the log.
67 m_buffering = false;
68 while (!m_msgs_before_open.empty()) {
69 const std::string &s = m_msgs_before_open.front();
70
71 if (m_print_to_file) {
72 FileWriteStr(s, m_fileout);
73 }
75 fwrite(s.data(), 1, s.size(), stdout);
76 }
77 for (const auto &cb : m_print_callbacks) {
78 cb(s);
79 }
80
81 m_msgs_before_open.pop_front();
82 }
84 fflush(stdout);
85 }
86
87 return true;
88}
89
91 StdLockGuard scoped_lock(m_cs);
92 m_buffering = true;
93 if (m_fileout != nullptr) {
94 fclose(m_fileout);
95 }
96 m_fileout = nullptr;
97 m_print_callbacks.clear();
98}
99
100static const std::map<std::string, BCLog::LogFlags> LOG_CATEGORIES_BY_STR{
101 {"0", BCLog::NONE},
102 {"none", BCLog::NONE},
103 {"net", BCLog::NET},
104 {"tor", BCLog::TOR},
105 {"mempool", BCLog::MEMPOOL},
106 {"http", BCLog::HTTP},
107 {"bench", BCLog::BENCH},
108 {"zmq", BCLog::ZMQ},
109 {"walletdb", BCLog::WALLETDB},
110 {"rpc", BCLog::RPC},
111 {"estimatefee", BCLog::ESTIMATEFEE},
112 {"addrman", BCLog::ADDRMAN},
113 {"selectcoins", BCLog::SELECTCOINS},
114 {"reindex", BCLog::REINDEX},
115 {"cmpctblock", BCLog::CMPCTBLOCK},
116 {"rand", BCLog::RAND},
117 {"prune", BCLog::PRUNE},
118 {"proxy", BCLog::PROXY},
119 {"mempoolrej", BCLog::MEMPOOLREJ},
120 {"libevent", BCLog::LIBEVENT},
121 {"coindb", BCLog::COINDB},
122 {"qt", BCLog::QT},
123 {"leveldb", BCLog::LEVELDB},
124 {"validation", BCLog::VALIDATION},
125 {"avalanche", BCLog::AVALANCHE},
126 {"i2p", BCLog::I2P},
127 {"chronik", BCLog::CHRONIK},
128#ifdef DEBUG_LOCKCONTENTION
129 {"lock", BCLog::LOCK},
130#endif
131 {"blockstorage", BCLog::BLOCKSTORE},
132 {"netdebug", BCLog::NETDEBUG},
133 {"txpackages", BCLog::TXPACKAGES},
134 {"1", BCLog::ALL},
135 {"all", BCLog::ALL},
136};
137
138static const std::unordered_map<BCLog::LogFlags, std::string>
140 // Swap keys and values from LOG_CATEGORIES_BY_STR.
141 [](const std::map<std::string, BCLog::LogFlags> &in) {
142 std::unordered_map<BCLog::LogFlags, std::string> out;
143 for (const auto &[k, v] : in) {
144 switch (v) {
145 case BCLog::NONE:
146 out.emplace(BCLog::NONE, "");
147 break;
148 case BCLog::ALL:
149 out.emplace(BCLog::ALL, "all");
150 break;
151 default:
152 out.emplace(v, k);
153 }
154 }
155 return out;
157
158bool GetLogCategory(BCLog::LogFlags &flag, const std::string &str) {
159 if (str.empty()) {
160 flag = BCLog::ALL;
161 return true;
162 }
163 auto it = LOG_CATEGORIES_BY_STR.find(str);
164 if (it != LOG_CATEGORIES_BY_STR.end()) {
165 flag = it->second;
166 return true;
167 }
168 return false;
169}
170
171std::string LogLevelToStr(BCLog::Level level) {
172 switch (level) {
174 return "none";
176 return "debug";
178 return "info";
180 return "warning";
182 return "error";
183 }
184 assert(false);
185}
186
187std::string LogCategoryToStr(BCLog::LogFlags category) {
188 auto it = LOG_CATEGORIES_BY_FLAG.find(category);
189 assert(it != LOG_CATEGORIES_BY_FLAG.end());
190 return it->second;
191}
192
193std::vector<LogCategory> BCLog::Logger::LogCategoriesList() const {
194 std::vector<LogCategory> ret;
195 for (const auto &[category, flag] : LOG_CATEGORIES_BY_STR) {
196 if (flag != BCLog::NONE && flag != BCLog::ALL) {
197 ret.push_back(LogCategory{.category = category,
198 .active = WillLogCategory(flag)});
199 }
200 }
201 return ret;
202}
203
205 if (m_fileout) {
206 fclose(m_fileout);
207 }
208}
209
210std::string BCLog::Logger::LogTimestampStr(const std::string &str) {
211 std::string strStamped;
212
213 if (!m_log_timestamps) {
214 return str;
215 }
216
217 if (m_started_new_line) {
218 int64_t nTimeMicros = GetTimeMicros();
219 strStamped = FormatISO8601DateTime(nTimeMicros / 1000000);
220 if (m_log_time_micros) {
221 strStamped.pop_back();
222 strStamped += strprintf(".%06dZ", nTimeMicros % 1000000);
223 }
224 std::chrono::seconds mocktime = GetMockTime();
225 if (mocktime > 0s) {
226 strStamped += " (mocktime: " +
227 FormatISO8601DateTime(count_seconds(mocktime)) + ")";
228 }
229 strStamped += ' ' + str;
230 } else {
231 strStamped = str;
232 }
233
234 return strStamped;
235}
236
237namespace BCLog {
245std::string LogEscapeMessage(const std::string &str) {
246 std::string ret;
247 for (char ch_in : str) {
248 uint8_t ch = (uint8_t)ch_in;
249 if ((ch >= 32 || ch == '\n') && ch != '\x7f') {
250 ret += ch_in;
251 } else {
252 ret += strprintf("\\x%02x", ch);
253 }
254 }
255 return ret;
256}
257} // namespace BCLog
258
259void BCLog::Logger::LogPrintStr(const std::string &str,
260 const std::string &logging_function,
261 const std::string &source_file,
262 const int source_line,
263 const BCLog::LogFlags category,
264 const BCLog::Level level) {
265 StdLockGuard scoped_lock(m_cs);
266 std::string str_prefixed = LogEscapeMessage(str);
267
268 if ((category != LogFlags::NONE || level != Level::None) &&
269 m_started_new_line) {
270 std::string s{"["};
271
272 if (category != LogFlags::NONE) {
273 s += LogCategoryToStr(category);
274 }
275
276 if (category != LogFlags::NONE && level != Level::None) {
277 // Only add separator if both flag and level are not NONE
278 s += ":";
279 }
280
281 if (level != Level::None) {
282 s += LogLevelToStr(level);
283 }
284
285 s += "] ";
286 str_prefixed.insert(0, s);
287 }
288
289 if (m_log_sourcelocations && m_started_new_line) {
290 str_prefixed.insert(0, "[" + RemovePrefix(source_file, "./") + ":" +
291 ToString(source_line) + "] [" +
292 logging_function + "] ");
293 }
294
295 if (m_log_threadnames && m_started_new_line) {
296 str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] ");
297 }
298
299 str_prefixed = LogTimestampStr(str_prefixed);
300
301 m_started_new_line = !str.empty() && str[str.size() - 1] == '\n';
302
303 if (m_buffering) {
304 // buffer if we haven't started logging yet
305 m_msgs_before_open.push_back(str_prefixed);
306 return;
307 }
308
309 if (m_print_to_console) {
310 // Print to console.
311 fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
312 fflush(stdout);
313 }
314 for (const auto &cb : m_print_callbacks) {
315 cb(str_prefixed);
316 }
317 if (m_print_to_file) {
318 assert(m_fileout != nullptr);
319
320 // Reopen the log file, if requested.
321 if (m_reopen_file) {
322 m_reopen_file = false;
323 FILE *new_fileout = fsbridge::fopen(m_file_path, "a");
324 if (new_fileout) {
325 // unbuffered.
326 setbuf(m_fileout, nullptr);
327 fclose(m_fileout);
328 m_fileout = new_fileout;
329 }
330 }
331 FileWriteStr(str_prefixed, m_fileout);
332 }
333}
334
336 // Amount of debug.log to save at end when shrinking (must fit in memory)
337 constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
338
339 assert(!m_file_path.empty());
340
341 // Scroll debug.log if it's getting too big.
342 FILE *file = fsbridge::fopen(m_file_path, "r");
343
344 // Special files (e.g. device nodes) may not have a size.
345 size_t log_size = 0;
346 try {
347 log_size = fs::file_size(m_file_path);
348 } catch (const fs::filesystem_error &) {
349 }
350
351 // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
352 // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes.
353 if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) {
354 // Restart the file with some of the end.
355 std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
356 if (fseek(file, -((long)vch.size()), SEEK_END)) {
357 LogPrintf("Failed to shrink debug log file: fseek(...) failed\n");
358 fclose(file);
359 return;
360 }
361 int nBytes = fread(vch.data(), 1, vch.size(), file);
362 fclose(file);
363
364 file = fsbridge::fopen(m_file_path, "w");
365 if (file) {
366 fwrite(vch.data(), 1, nBytes, file);
367 fclose(file);
368 }
369 } else if (file != nullptr) {
370 fclose(file);
371 }
372}
373
375 m_categories |= category;
376}
377
378bool BCLog::Logger::EnableCategory(const std::string &str) {
379 BCLog::LogFlags flag;
380 if (!GetLogCategory(flag, str)) {
381 return false;
382 }
383 EnableCategory(flag);
384 return true;
385}
386
388 m_categories &= ~category;
389}
390
391bool BCLog::Logger::DisableCategory(const std::string &str) {
392 BCLog::LogFlags flag;
393 if (!GetLogCategory(flag, str)) {
394 return false;
395 }
396 DisableCategory(flag);
397 return true;
398}
399
401 // ALL is not meant to be used as a logging category, but only as a mask
402 // representing all categories.
403 if (category == BCLog::NONE || category == BCLog::ALL) {
404 LogPrintf("Error trying to log using a category mask instead of an "
405 "explicit category.\n");
406 return true;
407 }
408
409 return (m_categories.load(std::memory_order_relaxed) & category) != 0;
410}
411
413 return m_categories != BCLog::NONE;
414}
bool WillLogCategory(LogFlags category) const
Return true if log accepts specified category.
Definition: logging.cpp:400
std::string LogTimestampStr(const std::string &str)
Definition: logging.cpp:210
void DisconnectTestLogger()
Only for testing.
Definition: logging.cpp:90
bool DefaultShrinkDebugFile() const
Default for whether ShrinkDebugFile should be run.
Definition: logging.cpp:412
fs::path m_file_path
Definition: logging.h:119
void LogPrintStr(const std::string &str, const std::string &logging_function, const std::string &source_file, const int source_line, const BCLog::LogFlags category, const BCLog::Level level)
Send a string to the log output.
Definition: logging.cpp:259
std::vector< LogCategory > LogCategoriesList() const
Returns a vector of the log categories in alphabetical order.
Definition: logging.cpp:193
void DisableCategory(LogFlags category)
Definition: logging.cpp:387
void EnableCategory(LogFlags category)
Definition: logging.cpp:374
bool StartLogging()
Start logging (and flush all buffered messages)
Definition: logging.cpp:45
void ShrinkDebugFile()
Definition: logging.cpp:335
bool m_print_to_file
Definition: logging.h:112
bool m_print_to_console
Definition: logging.h:111
StdMutex m_cs
Definition: logging.h:86
std::string LogLevelToStr(BCLog::Level level)
Definition: logging.cpp:171
std::string LogCategoryToStr(BCLog::LogFlags category)
Definition: logging.cpp:187
static int FileWriteStr(const std::string &str, FILE *fp)
Definition: logging.cpp:41
BCLog::Logger & LogInstance()
Definition: logging.cpp:21
bool GetLogCategory(BCLog::LogFlags &flag, const std::string &str)
Return true if str parses as a log category and set the flag.
Definition: logging.cpp:158
bool fLogIPs
Definition: logging.cpp:18
static const std::unordered_map< BCLog::LogFlags, std::string > LOG_CATEGORIES_BY_FLAG
Definition: logging.cpp:139
const char *const DEFAULT_DEBUGLOGFILE
Definition: logging.cpp:19
static const std::map< std::string, BCLog::LogFlags > LOG_CATEGORIES_BY_STR
Definition: logging.cpp:100
static const bool DEFAULT_LOGIPS
Definition: logging.h:23
#define LogPrintf(...)
Definition: logging.h:227
Level
Definition: logging.h:74
std::string LogEscapeMessage(const std::string &str)
Belts and suspenders: make sure outgoing log messages don't contain potentially suspicious characters...
Definition: logging.cpp:245
LogFlags
Definition: logging.h:38
@ ESTIMATEFEE
Definition: logging.h:48
@ AVALANCHE
Definition: logging.h:62
@ RAND
Definition: logging.h:53
@ COINDB
Definition: logging.h:58
@ REINDEX
Definition: logging.h:51
@ TXPACKAGES
Definition: logging.h:70
@ WALLETDB
Definition: logging.h:46
@ ADDRMAN
Definition: logging.h:49
@ ALL
Definition: logging.h:71
@ NETDEBUG
Definition: logging.h:69
@ RPC
Definition: logging.h:47
@ HTTP
Definition: logging.h:43
@ LEVELDB
Definition: logging.h:60
@ NONE
Definition: logging.h:39
@ VALIDATION
Definition: logging.h:61
@ MEMPOOLREJ
Definition: logging.h:56
@ PRUNE
Definition: logging.h:54
@ TOR
Definition: logging.h:41
@ LIBEVENT
Definition: logging.h:57
@ CMPCTBLOCK
Definition: logging.h:52
@ PROXY
Definition: logging.h:55
@ CHRONIK
Definition: logging.h:64
@ ZMQ
Definition: logging.h:45
@ MEMPOOL
Definition: logging.h:42
@ SELECTCOINS
Definition: logging.h:50
@ I2P
Definition: logging.h:63
@ BENCH
Definition: logging.h:44
@ NET
Definition: logging.h:40
@ QT
Definition: logging.h:59
@ BLOCKSTORE
Definition: logging.h:68
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:30
const std::string & ThreadGetInternalName()
Get the thread's internal (in-memory) name; used e.g.
Definition: threadnames.cpp:39
std::string RemovePrefix(std::string_view str, std::string_view prefix)
Definition: string.h:50
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:100
std::string category
Definition: logging.h:32
#define LOCK(cs)
Definition: sync.h:306
int64_t GetTimeMicros()
Returns the system time (not mockable)
Definition: time.cpp:105
std::chrono::seconds GetMockTime()
For testing.
Definition: time.cpp:97
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.
Definition: time.cpp:113
constexpr int64_t count_seconds(std::chrono::seconds t)
Definition: time.h:55
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
assert(!tx.IsCoinBase())