Bitcoin Core 22.99.0
P2P Digital Currency
logging.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-2020 The Bitcoin Core developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6#include <logging.h>
7#include <util/threadnames.h>
8#include <util/string.h>
9#include <util/time.h>
10
11#include <algorithm>
12#include <array>
13#include <mutex>
14
15const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
16
18{
34 static BCLog::Logger* g_logger{new BCLog::Logger()};
35 return *g_logger;
36}
37
39
40static int FileWriteStr(const std::string &str, FILE *fp)
41{
42 return fwrite(str.data(), 1, str.size(), fp);
43}
44
46{
47 StdLockGuard scoped_lock(m_cs);
48
49 assert(m_buffering);
50 assert(m_fileout == nullptr);
51
52 if (m_print_to_file) {
53 assert(!m_file_path.empty());
54 m_fileout = fsbridge::fopen(m_file_path, "a");
55 if (!m_fileout) {
56 return false;
57 }
58
59 setbuf(m_fileout, nullptr); // unbuffered
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) FileWriteStr(s, m_fileout);
72 if (m_print_to_console) fwrite(s.data(), 1, s.size(), stdout);
73 for (const auto& cb : m_print_callbacks) {
74 cb(s);
75 }
76
77 m_msgs_before_open.pop_front();
78 }
79 if (m_print_to_console) fflush(stdout);
80
81 return true;
82}
83
85{
86 StdLockGuard scoped_lock(m_cs);
87 m_buffering = true;
88 if (m_fileout != nullptr) fclose(m_fileout);
89 m_fileout = nullptr;
90 m_print_callbacks.clear();
91}
92
94{
95 m_categories |= flag;
96}
97
98bool BCLog::Logger::EnableCategory(const std::string& str)
99{
100 BCLog::LogFlags flag;
101 if (!GetLogCategory(flag, str)) return false;
102 EnableCategory(flag);
103 return true;
104}
105
107{
108 m_categories &= ~flag;
109}
110
111bool BCLog::Logger::DisableCategory(const std::string& str)
112{
113 BCLog::LogFlags flag;
114 if (!GetLogCategory(flag, str)) return false;
115 DisableCategory(flag);
116 return true;
117}
118
120{
121 return (m_categories.load(std::memory_order_relaxed) & category) != 0;
122}
123
125{
126 return m_categories == BCLog::NONE;
127}
128
131 std::string category;
132};
133
135{
136 {BCLog::NONE, "0"},
137 {BCLog::NONE, "none"},
138 {BCLog::NET, "net"},
139 {BCLog::TOR, "tor"},
140 {BCLog::MEMPOOL, "mempool"},
141 {BCLog::HTTP, "http"},
142 {BCLog::BENCH, "bench"},
143 {BCLog::ZMQ, "zmq"},
144 {BCLog::WALLETDB, "walletdb"},
145 {BCLog::RPC, "rpc"},
146 {BCLog::ESTIMATEFEE, "estimatefee"},
147 {BCLog::ADDRMAN, "addrman"},
148 {BCLog::SELECTCOINS, "selectcoins"},
149 {BCLog::REINDEX, "reindex"},
150 {BCLog::CMPCTBLOCK, "cmpctblock"},
151 {BCLog::RAND, "rand"},
152 {BCLog::PRUNE, "prune"},
153 {BCLog::PROXY, "proxy"},
154 {BCLog::MEMPOOLREJ, "mempoolrej"},
155 {BCLog::LIBEVENT, "libevent"},
156 {BCLog::COINDB, "coindb"},
157 {BCLog::QT, "qt"},
158 {BCLog::LEVELDB, "leveldb"},
159 {BCLog::VALIDATION, "validation"},
160 {BCLog::I2P, "i2p"},
161 {BCLog::IPC, "ipc"},
162 {BCLog::LOCK, "lock"},
163 {BCLog::UTIL, "util"},
164 {BCLog::BLOCKSTORE, "blockstorage"},
165 {BCLog::ALL, "1"},
166 {BCLog::ALL, "all"},
167};
168
169bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
170{
171 if (str == "") {
172 flag = BCLog::ALL;
173 return true;
174 }
175 for (const CLogCategoryDesc& category_desc : LogCategories) {
176 if (category_desc.category == str) {
177 flag = category_desc.flag;
178 return true;
179 }
180 }
181 return false;
182}
183
184std::vector<LogCategory> BCLog::Logger::LogCategoriesList() const
185{
186 // Sort log categories by alphabetical order.
187 std::array<CLogCategoryDesc, std::size(LogCategories)> categories;
188 std::copy(std::begin(LogCategories), std::end(LogCategories), categories.begin());
189 std::sort(categories.begin(), categories.end(), [](auto a, auto b) { return a.category < b.category; });
190
191 std::vector<LogCategory> ret;
192 for (const CLogCategoryDesc& category_desc : categories) {
193 if (category_desc.flag == BCLog::NONE || category_desc.flag == BCLog::ALL) continue;
194 LogCategory catActive;
195 catActive.category = category_desc.category;
196 catActive.active = WillLogCategory(category_desc.flag);
197 ret.push_back(catActive);
198 }
199 return ret;
200}
201
202std::string BCLog::Logger::LogTimestampStr(const std::string& str)
203{
204 std::string strStamped;
205
206 if (!m_log_timestamps)
207 return str;
208
209 if (m_started_new_line) {
210 int64_t nTimeMicros = GetTimeMicros();
211 strStamped = FormatISO8601DateTime(nTimeMicros/1000000);
212 if (m_log_time_micros) {
213 strStamped.pop_back();
214 strStamped += strprintf(".%06dZ", nTimeMicros%1000000);
215 }
216 std::chrono::seconds mocktime = GetMockTime();
217 if (mocktime > 0s) {
218 strStamped += " (mocktime: " + FormatISO8601DateTime(count_seconds(mocktime)) + ")";
219 }
220 strStamped += ' ' + str;
221 } else
222 strStamped = str;
223
224 return strStamped;
225}
226
227namespace BCLog {
235 std::string LogEscapeMessage(const std::string& str) {
236 std::string ret;
237 for (char ch_in : str) {
238 uint8_t ch = (uint8_t)ch_in;
239 if ((ch >= 32 || ch == '\n') && ch != '\x7f') {
240 ret += ch_in;
241 } else {
242 ret += strprintf("\\x%02x", ch);
243 }
244 }
245 return ret;
246 }
247} // namespace BCLog
248
249void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line)
250{
251 StdLockGuard scoped_lock(m_cs);
252 std::string str_prefixed = LogEscapeMessage(str);
253
254 if (m_log_sourcelocations && m_started_new_line) {
255 str_prefixed.insert(0, "[" + RemovePrefix(source_file, "./") + ":" + ToString(source_line) + "] [" + logging_function + "] ");
256 }
257
258 if (m_log_threadnames && m_started_new_line) {
259 str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] ");
260 }
261
262 str_prefixed = LogTimestampStr(str_prefixed);
263
264 m_started_new_line = !str.empty() && str[str.size()-1] == '\n';
265
266 if (m_buffering) {
267 // buffer if we haven't started logging yet
268 m_msgs_before_open.push_back(str_prefixed);
269 return;
270 }
271
272 if (m_print_to_console) {
273 // print to console
274 fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
275 fflush(stdout);
276 }
277 for (const auto& cb : m_print_callbacks) {
278 cb(str_prefixed);
279 }
280 if (m_print_to_file) {
281 assert(m_fileout != nullptr);
282
283 // reopen the log file, if requested
284 if (m_reopen_file) {
285 m_reopen_file = false;
286 FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
287 if (new_fileout) {
288 setbuf(new_fileout, nullptr); // unbuffered
289 fclose(m_fileout);
290 m_fileout = new_fileout;
291 }
292 }
293 FileWriteStr(str_prefixed, m_fileout);
294 }
295}
296
298{
299 // Amount of debug.log to save at end when shrinking (must fit in memory)
300 constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
301
302 assert(!m_file_path.empty());
303
304 // Scroll debug.log if it's getting too big
305 FILE* file = fsbridge::fopen(m_file_path, "r");
306
307 // Special files (e.g. device nodes) may not have a size.
308 size_t log_size = 0;
309 try {
310 log_size = fs::file_size(m_file_path);
311 } catch (const fs::filesystem_error&) {}
312
313 // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
314 // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
315 if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
316 {
317 // Restart the file with some of the end
318 std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
319 if (fseek(file, -((long)vch.size()), SEEK_END)) {
320 LogPrintf("Failed to shrink debug log file: fseek(...) failed\n");
321 fclose(file);
322 return;
323 }
324 int nBytes = fread(vch.data(), 1, vch.size(), file);
325 fclose(file);
326
327 file = fsbridge::fopen(m_file_path, "w");
328 if (file)
329 {
330 fwrite(vch.data(), 1, nBytes, file);
331 fclose(file);
332 }
333 }
334 else if (file != nullptr)
335 fclose(file);
336}
bool WillLogCategory(LogFlags category) const
Definition: logging.cpp:119
std::string LogTimestampStr(const std::string &str)
Definition: logging.cpp:202
void DisconnectTestLogger()
Only for testing.
Definition: logging.cpp:84
bool DefaultShrinkDebugFile() const
Definition: logging.cpp:124
fs::path m_file_path
Definition: logging.h:101
std::vector< LogCategory > LogCategoriesList() const
Returns a vector of the log categories in alphabetical order.
Definition: logging.cpp:184
void EnableCategory(LogFlags flag)
Definition: logging.cpp:93
bool StartLogging()
Start logging (and flush all buffered messages)
Definition: logging.cpp:45
void ShrinkDebugFile()
Definition: logging.cpp:297
bool m_print_to_file
Definition: logging.h:94
bool m_print_to_console
Definition: logging.h:93
StdMutex m_cs
Definition: logging.h:71
void LogPrintStr(const std::string &str, const std::string &logging_function, const std::string &source_file, const int source_line)
Send a string to the log output.
Definition: logging.cpp:249
void DisableCategory(LogFlags flag)
Definition: logging.cpp:106
const CLogCategoryDesc LogCategories[]
Definition: logging.cpp:134
static int FileWriteStr(const std::string &str, FILE *fp)
Definition: logging.cpp:40
BCLog::Logger & LogInstance()
Definition: logging.cpp:17
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:169
bool fLogIPs
Definition: logging.cpp:38
const char *const DEFAULT_DEBUGLOGFILE
Definition: logging.cpp:15
static const bool DEFAULT_LOGIPS
Definition: logging.h:22
#define LogPrintf(...)
Definition: logging.h:187
std::string LogEscapeMessage(const std::string &str)
Belts and suspenders: make sure outgoing log messages don't contain potentially suspicious characters...
Definition: logging.cpp:235
LogFlags
Definition: logging.h:36
@ ESTIMATEFEE
Definition: logging.h:46
@ RAND
Definition: logging.h:51
@ COINDB
Definition: logging.h:56
@ REINDEX
Definition: logging.h:49
@ WALLETDB
Definition: logging.h:44
@ ADDRMAN
Definition: logging.h:47
@ ALL
Definition: logging.h:65
@ RPC
Definition: logging.h:45
@ HTTP
Definition: logging.h:41
@ LEVELDB
Definition: logging.h:58
@ NONE
Definition: logging.h:37
@ VALIDATION
Definition: logging.h:59
@ MEMPOOLREJ
Definition: logging.h:54
@ PRUNE
Definition: logging.h:52
@ TOR
Definition: logging.h:39
@ LIBEVENT
Definition: logging.h:55
@ LOCK
Definition: logging.h:62
@ CMPCTBLOCK
Definition: logging.h:50
@ PROXY
Definition: logging.h:53
@ ZMQ
Definition: logging.h:43
@ IPC
Definition: logging.h:61
@ MEMPOOL
Definition: logging.h:40
@ SELECTCOINS
Definition: logging.h:48
@ I2P
Definition: logging.h:60
@ BENCH
Definition: logging.h:42
@ NET
Definition: logging.h:38
@ UTIL
Definition: logging.h:63
@ QT
Definition: logging.h:57
@ BLOCKSTORE
Definition: logging.h:64
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:25
const std::string & ThreadGetInternalName()
Get the thread's internal (in-memory) name; used e.g.
Definition: threadnames.cpp:53
std::string RemovePrefix(const std::string &str, const std::string &prefix)
Definition: string.h:28
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:87
BCLog::LogFlags flag
Definition: logging.cpp:130
std::string category
Definition: logging.cpp:131
bool active
Definition: logging.h:32
std::string category
Definition: logging.h:31
int64_t GetTimeMicros()
Returns the system time (not mockable)
Definition: time.cpp:122
std::chrono::seconds GetMockTime()
For testing.
Definition: time.cpp:112
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.
Definition: time.cpp:132
constexpr int64_t count_seconds(std::chrono::seconds t)
Helper to count the seconds of a duration.
Definition: time.h:29
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164
assert(!tx.IsCoinBase())