Skip to content

Commit b77b6e2

Browse files
author
Jim Posen
committed
MOVEONLY: Move logging code from util.{h,cpp} to new files.
1 parent 3a8a4dc commit b77b6e2

File tree

5 files changed

+412
-377
lines changed

5 files changed

+412
-377
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ BITCOIN_CORE_H = \
113113
keystore.h \
114114
dbwrapper.h \
115115
limitedmap.h \
116+
logging.h \
116117
memusage.h \
117118
merkleblock.h \
118119
miner.h \
@@ -364,6 +365,7 @@ libbitcoin_util_a_SOURCES = \
364365
fs.cpp \
365366
interfaces/handler.cpp \
366367
interfaces/node.cpp \
368+
logging.cpp \
367369
random.cpp \
368370
rpc/protocol.cpp \
369371
rpc/util.cpp \

src/logging.cpp

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
// Copyright (c) 2009-2010 Satoshi Nakamoto
2+
// Copyright (c) 2009-2018 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.h>
8+
#include <utilstrencodings.h>
9+
10+
#include <list>
11+
#include <mutex>
12+
13+
const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
14+
15+
bool fPrintToConsole = false;
16+
bool fPrintToDebugLog = true;
17+
18+
bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS;
19+
bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS;
20+
bool fLogIPs = DEFAULT_LOGIPS;
21+
std::atomic<bool> fReopenDebugLog(false);
22+
23+
/** Log categories bitfield. */
24+
std::atomic<uint32_t> logCategories(0);
25+
/**
26+
* LogPrintf() has been broken a couple of times now
27+
* by well-meaning people adding mutexes in the most straightforward way.
28+
* It breaks because it may be called by global destructors during shutdown.
29+
* Since the order of destruction of static/global objects is undefined,
30+
* defining a mutex as a global object doesn't work (the mutex gets
31+
* destroyed, and then some later destructor calls OutputDebugStringF,
32+
* maybe indirectly, and you get a core dump at shutdown trying to lock
33+
* the mutex).
34+
*/
35+
36+
static std::once_flag debugPrintInitFlag;
37+
38+
/**
39+
* We use std::call_once() to make sure mutexDebugLog and
40+
* vMsgsBeforeOpenLog are initialized in a thread-safe manner.
41+
*
42+
* NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog
43+
* are leaked on exit. This is ugly, but will be cleaned up by
44+
* the OS/libc. When the shutdown sequence is fully audited and
45+
* tested, explicit destruction of these objects can be implemented.
46+
*/
47+
static FILE* fileout = nullptr;
48+
static std::mutex* mutexDebugLog = nullptr;
49+
static std::list<std::string>* vMsgsBeforeOpenLog;
50+
51+
static int FileWriteStr(const std::string &str, FILE *fp)
52+
{
53+
return fwrite(str.data(), 1, str.size(), fp);
54+
}
55+
56+
static void DebugPrintInit()
57+
{
58+
assert(mutexDebugLog == nullptr);
59+
mutexDebugLog = new std::mutex();
60+
vMsgsBeforeOpenLog = new std::list<std::string>;
61+
}
62+
63+
fs::path GetDebugLogPath()
64+
{
65+
fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
66+
return AbsPathForConfigVal(logfile);
67+
}
68+
69+
bool OpenDebugLog()
70+
{
71+
std::call_once(debugPrintInitFlag, &DebugPrintInit);
72+
std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog);
73+
74+
assert(fileout == nullptr);
75+
assert(vMsgsBeforeOpenLog);
76+
fs::path pathDebug = GetDebugLogPath();
77+
78+
fileout = fsbridge::fopen(pathDebug, "a");
79+
if (!fileout) {
80+
return false;
81+
}
82+
83+
setbuf(fileout, nullptr); // unbuffered
84+
// dump buffered messages from before we opened the log
85+
while (!vMsgsBeforeOpenLog->empty()) {
86+
FileWriteStr(vMsgsBeforeOpenLog->front(), fileout);
87+
vMsgsBeforeOpenLog->pop_front();
88+
}
89+
90+
delete vMsgsBeforeOpenLog;
91+
vMsgsBeforeOpenLog = nullptr;
92+
return true;
93+
}
94+
95+
struct CLogCategoryDesc
96+
{
97+
uint32_t flag;
98+
std::string category;
99+
};
100+
101+
const CLogCategoryDesc LogCategories[] =
102+
{
103+
{BCLog::NONE, "0"},
104+
{BCLog::NONE, "none"},
105+
{BCLog::NET, "net"},
106+
{BCLog::TOR, "tor"},
107+
{BCLog::MEMPOOL, "mempool"},
108+
{BCLog::HTTP, "http"},
109+
{BCLog::BENCH, "bench"},
110+
{BCLog::ZMQ, "zmq"},
111+
{BCLog::DB, "db"},
112+
{BCLog::RPC, "rpc"},
113+
{BCLog::ESTIMATEFEE, "estimatefee"},
114+
{BCLog::ADDRMAN, "addrman"},
115+
{BCLog::SELECTCOINS, "selectcoins"},
116+
{BCLog::REINDEX, "reindex"},
117+
{BCLog::CMPCTBLOCK, "cmpctblock"},
118+
{BCLog::RAND, "rand"},
119+
{BCLog::PRUNE, "prune"},
120+
{BCLog::PROXY, "proxy"},
121+
{BCLog::MEMPOOLREJ, "mempoolrej"},
122+
{BCLog::LIBEVENT, "libevent"},
123+
{BCLog::COINDB, "coindb"},
124+
{BCLog::QT, "qt"},
125+
{BCLog::LEVELDB, "leveldb"},
126+
{BCLog::ALL, "1"},
127+
{BCLog::ALL, "all"},
128+
};
129+
130+
bool GetLogCategory(uint32_t *f, const std::string *str)
131+
{
132+
if (f && str) {
133+
if (*str == "") {
134+
*f = BCLog::ALL;
135+
return true;
136+
}
137+
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
138+
if (LogCategories[i].category == *str) {
139+
*f = LogCategories[i].flag;
140+
return true;
141+
}
142+
}
143+
}
144+
return false;
145+
}
146+
147+
std::string ListLogCategories()
148+
{
149+
std::string ret;
150+
int outcount = 0;
151+
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
152+
// Omit the special cases.
153+
if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
154+
if (outcount != 0) ret += ", ";
155+
ret += LogCategories[i].category;
156+
outcount++;
157+
}
158+
}
159+
return ret;
160+
}
161+
162+
std::vector<CLogCategoryActive> ListActiveLogCategories()
163+
{
164+
std::vector<CLogCategoryActive> ret;
165+
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
166+
// Omit the special cases.
167+
if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
168+
CLogCategoryActive catActive;
169+
catActive.category = LogCategories[i].category;
170+
catActive.active = LogAcceptCategory(LogCategories[i].flag);
171+
ret.push_back(catActive);
172+
}
173+
}
174+
return ret;
175+
}
176+
177+
/**
178+
* fStartedNewLine is a state variable held by the calling context that will
179+
* suppress printing of the timestamp when multiple calls are made that don't
180+
* end in a newline. Initialize it to true, and hold it, in the calling context.
181+
*/
182+
static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fStartedNewLine)
183+
{
184+
std::string strStamped;
185+
186+
if (!fLogTimestamps)
187+
return str;
188+
189+
if (*fStartedNewLine) {
190+
int64_t nTimeMicros = GetTimeMicros();
191+
strStamped = FormatISO8601DateTime(nTimeMicros/1000000);
192+
if (fLogTimeMicros) {
193+
strStamped.pop_back();
194+
strStamped += strprintf(".%06dZ", nTimeMicros%1000000);
195+
}
196+
int64_t mocktime = GetMockTime();
197+
if (mocktime) {
198+
strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")";
199+
}
200+
strStamped += ' ' + str;
201+
} else
202+
strStamped = str;
203+
204+
if (!str.empty() && str[str.size()-1] == '\n')
205+
*fStartedNewLine = true;
206+
else
207+
*fStartedNewLine = false;
208+
209+
return strStamped;
210+
}
211+
212+
int LogPrintStr(const std::string &str)
213+
{
214+
int ret = 0; // Returns total number of characters written
215+
static std::atomic_bool fStartedNewLine(true);
216+
217+
std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine);
218+
219+
if (fPrintToConsole) {
220+
// print to console
221+
ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
222+
fflush(stdout);
223+
}
224+
if (fPrintToDebugLog) {
225+
std::call_once(debugPrintInitFlag, &DebugPrintInit);
226+
std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog);
227+
228+
// buffer if we haven't opened the log yet
229+
if (fileout == nullptr) {
230+
assert(vMsgsBeforeOpenLog);
231+
ret = strTimestamped.length();
232+
vMsgsBeforeOpenLog->push_back(strTimestamped);
233+
}
234+
else
235+
{
236+
// reopen the log file, if requested
237+
if (fReopenDebugLog) {
238+
fReopenDebugLog = false;
239+
fs::path pathDebug = GetDebugLogPath();
240+
if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr)
241+
setbuf(fileout, nullptr); // unbuffered
242+
}
243+
244+
ret = FileWriteStr(strTimestamped, fileout);
245+
}
246+
}
247+
return ret;
248+
}
249+
250+
void ShrinkDebugFile()
251+
{
252+
// Amount of debug.log to save at end when shrinking (must fit in memory)
253+
constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
254+
// Scroll debug.log if it's getting too big
255+
fs::path pathLog = GetDebugLogPath();
256+
FILE* file = fsbridge::fopen(pathLog, "r");
257+
258+
// Special files (e.g. device nodes) may not have a size.
259+
size_t log_size = 0;
260+
try {
261+
log_size = fs::file_size(pathLog);
262+
} catch (boost::filesystem::filesystem_error &) {}
263+
264+
// If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
265+
// trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
266+
if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
267+
{
268+
// Restart the file with some of the end
269+
std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
270+
fseek(file, -((long)vch.size()), SEEK_END);
271+
int nBytes = fread(vch.data(), 1, vch.size(), file);
272+
fclose(file);
273+
274+
file = fsbridge::fopen(pathLog, "w");
275+
if (file)
276+
{
277+
fwrite(vch.data(), 1, nBytes, file);
278+
fclose(file);
279+
}
280+
}
281+
else if (file != nullptr)
282+
fclose(file);
283+
}

0 commit comments

Comments
 (0)