MOVEONLY: Move logging code from util.{h,cpp} to new files.
This commit is contained in:
parent
3a8a4dc4a1
commit
b77b6e2345
5 changed files with 412 additions and 377 deletions
|
@ -113,6 +113,7 @@ BITCOIN_CORE_H = \
|
||||||
keystore.h \
|
keystore.h \
|
||||||
dbwrapper.h \
|
dbwrapper.h \
|
||||||
limitedmap.h \
|
limitedmap.h \
|
||||||
|
logging.h \
|
||||||
memusage.h \
|
memusage.h \
|
||||||
merkleblock.h \
|
merkleblock.h \
|
||||||
miner.h \
|
miner.h \
|
||||||
|
@ -364,6 +365,7 @@ libbitcoin_util_a_SOURCES = \
|
||||||
fs.cpp \
|
fs.cpp \
|
||||||
interfaces/handler.cpp \
|
interfaces/handler.cpp \
|
||||||
interfaces/node.cpp \
|
interfaces/node.cpp \
|
||||||
|
logging.cpp \
|
||||||
random.cpp \
|
random.cpp \
|
||||||
rpc/protocol.cpp \
|
rpc/protocol.cpp \
|
||||||
rpc/util.cpp \
|
rpc/util.cpp \
|
||||||
|
|
283
src/logging.cpp
Normal file
283
src/logging.cpp
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <logging.h>
|
||||||
|
#include <util.h>
|
||||||
|
#include <utilstrencodings.h>
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
|
||||||
|
|
||||||
|
bool fPrintToConsole = false;
|
||||||
|
bool fPrintToDebugLog = true;
|
||||||
|
|
||||||
|
bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS;
|
||||||
|
bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS;
|
||||||
|
bool fLogIPs = DEFAULT_LOGIPS;
|
||||||
|
std::atomic<bool> fReopenDebugLog(false);
|
||||||
|
|
||||||
|
/** Log categories bitfield. */
|
||||||
|
std::atomic<uint32_t> logCategories(0);
|
||||||
|
/**
|
||||||
|
* LogPrintf() has been broken a couple of times now
|
||||||
|
* by well-meaning people adding mutexes in the most straightforward way.
|
||||||
|
* It breaks because it may be called by global destructors during shutdown.
|
||||||
|
* Since the order of destruction of static/global objects is undefined,
|
||||||
|
* defining a mutex as a global object doesn't work (the mutex gets
|
||||||
|
* destroyed, and then some later destructor calls OutputDebugStringF,
|
||||||
|
* maybe indirectly, and you get a core dump at shutdown trying to lock
|
||||||
|
* the mutex).
|
||||||
|
*/
|
||||||
|
|
||||||
|
static std::once_flag debugPrintInitFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We use std::call_once() to make sure mutexDebugLog and
|
||||||
|
* vMsgsBeforeOpenLog are initialized in a thread-safe manner.
|
||||||
|
*
|
||||||
|
* NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog
|
||||||
|
* are leaked on exit. This is ugly, but will be cleaned up by
|
||||||
|
* the OS/libc. When the shutdown sequence is fully audited and
|
||||||
|
* tested, explicit destruction of these objects can be implemented.
|
||||||
|
*/
|
||||||
|
static FILE* fileout = nullptr;
|
||||||
|
static std::mutex* mutexDebugLog = nullptr;
|
||||||
|
static std::list<std::string>* vMsgsBeforeOpenLog;
|
||||||
|
|
||||||
|
static int FileWriteStr(const std::string &str, FILE *fp)
|
||||||
|
{
|
||||||
|
return fwrite(str.data(), 1, str.size(), fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DebugPrintInit()
|
||||||
|
{
|
||||||
|
assert(mutexDebugLog == nullptr);
|
||||||
|
mutexDebugLog = new std::mutex();
|
||||||
|
vMsgsBeforeOpenLog = new std::list<std::string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path GetDebugLogPath()
|
||||||
|
{
|
||||||
|
fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
|
||||||
|
return AbsPathForConfigVal(logfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenDebugLog()
|
||||||
|
{
|
||||||
|
std::call_once(debugPrintInitFlag, &DebugPrintInit);
|
||||||
|
std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog);
|
||||||
|
|
||||||
|
assert(fileout == nullptr);
|
||||||
|
assert(vMsgsBeforeOpenLog);
|
||||||
|
fs::path pathDebug = GetDebugLogPath();
|
||||||
|
|
||||||
|
fileout = fsbridge::fopen(pathDebug, "a");
|
||||||
|
if (!fileout) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setbuf(fileout, nullptr); // unbuffered
|
||||||
|
// dump buffered messages from before we opened the log
|
||||||
|
while (!vMsgsBeforeOpenLog->empty()) {
|
||||||
|
FileWriteStr(vMsgsBeforeOpenLog->front(), fileout);
|
||||||
|
vMsgsBeforeOpenLog->pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete vMsgsBeforeOpenLog;
|
||||||
|
vMsgsBeforeOpenLog = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CLogCategoryDesc
|
||||||
|
{
|
||||||
|
uint32_t flag;
|
||||||
|
std::string category;
|
||||||
|
};
|
||||||
|
|
||||||
|
const CLogCategoryDesc LogCategories[] =
|
||||||
|
{
|
||||||
|
{BCLog::NONE, "0"},
|
||||||
|
{BCLog::NONE, "none"},
|
||||||
|
{BCLog::NET, "net"},
|
||||||
|
{BCLog::TOR, "tor"},
|
||||||
|
{BCLog::MEMPOOL, "mempool"},
|
||||||
|
{BCLog::HTTP, "http"},
|
||||||
|
{BCLog::BENCH, "bench"},
|
||||||
|
{BCLog::ZMQ, "zmq"},
|
||||||
|
{BCLog::DB, "db"},
|
||||||
|
{BCLog::RPC, "rpc"},
|
||||||
|
{BCLog::ESTIMATEFEE, "estimatefee"},
|
||||||
|
{BCLog::ADDRMAN, "addrman"},
|
||||||
|
{BCLog::SELECTCOINS, "selectcoins"},
|
||||||
|
{BCLog::REINDEX, "reindex"},
|
||||||
|
{BCLog::CMPCTBLOCK, "cmpctblock"},
|
||||||
|
{BCLog::RAND, "rand"},
|
||||||
|
{BCLog::PRUNE, "prune"},
|
||||||
|
{BCLog::PROXY, "proxy"},
|
||||||
|
{BCLog::MEMPOOLREJ, "mempoolrej"},
|
||||||
|
{BCLog::LIBEVENT, "libevent"},
|
||||||
|
{BCLog::COINDB, "coindb"},
|
||||||
|
{BCLog::QT, "qt"},
|
||||||
|
{BCLog::LEVELDB, "leveldb"},
|
||||||
|
{BCLog::ALL, "1"},
|
||||||
|
{BCLog::ALL, "all"},
|
||||||
|
};
|
||||||
|
|
||||||
|
bool GetLogCategory(uint32_t *f, const std::string *str)
|
||||||
|
{
|
||||||
|
if (f && str) {
|
||||||
|
if (*str == "") {
|
||||||
|
*f = BCLog::ALL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
|
||||||
|
if (LogCategories[i].category == *str) {
|
||||||
|
*f = LogCategories[i].flag;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ListLogCategories()
|
||||||
|
{
|
||||||
|
std::string ret;
|
||||||
|
int outcount = 0;
|
||||||
|
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
|
||||||
|
// Omit the special cases.
|
||||||
|
if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
|
||||||
|
if (outcount != 0) ret += ", ";
|
||||||
|
ret += LogCategories[i].category;
|
||||||
|
outcount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CLogCategoryActive> ListActiveLogCategories()
|
||||||
|
{
|
||||||
|
std::vector<CLogCategoryActive> ret;
|
||||||
|
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
|
||||||
|
// Omit the special cases.
|
||||||
|
if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
|
||||||
|
CLogCategoryActive catActive;
|
||||||
|
catActive.category = LogCategories[i].category;
|
||||||
|
catActive.active = LogAcceptCategory(LogCategories[i].flag);
|
||||||
|
ret.push_back(catActive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fStartedNewLine is a state variable held by the calling context that will
|
||||||
|
* suppress printing of the timestamp when multiple calls are made that don't
|
||||||
|
* end in a newline. Initialize it to true, and hold it, in the calling context.
|
||||||
|
*/
|
||||||
|
static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fStartedNewLine)
|
||||||
|
{
|
||||||
|
std::string strStamped;
|
||||||
|
|
||||||
|
if (!fLogTimestamps)
|
||||||
|
return str;
|
||||||
|
|
||||||
|
if (*fStartedNewLine) {
|
||||||
|
int64_t nTimeMicros = GetTimeMicros();
|
||||||
|
strStamped = FormatISO8601DateTime(nTimeMicros/1000000);
|
||||||
|
if (fLogTimeMicros) {
|
||||||
|
strStamped.pop_back();
|
||||||
|
strStamped += strprintf(".%06dZ", nTimeMicros%1000000);
|
||||||
|
}
|
||||||
|
int64_t mocktime = GetMockTime();
|
||||||
|
if (mocktime) {
|
||||||
|
strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")";
|
||||||
|
}
|
||||||
|
strStamped += ' ' + str;
|
||||||
|
} else
|
||||||
|
strStamped = str;
|
||||||
|
|
||||||
|
if (!str.empty() && str[str.size()-1] == '\n')
|
||||||
|
*fStartedNewLine = true;
|
||||||
|
else
|
||||||
|
*fStartedNewLine = false;
|
||||||
|
|
||||||
|
return strStamped;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LogPrintStr(const std::string &str)
|
||||||
|
{
|
||||||
|
int ret = 0; // Returns total number of characters written
|
||||||
|
static std::atomic_bool fStartedNewLine(true);
|
||||||
|
|
||||||
|
std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine);
|
||||||
|
|
||||||
|
if (fPrintToConsole) {
|
||||||
|
// print to console
|
||||||
|
ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
if (fPrintToDebugLog) {
|
||||||
|
std::call_once(debugPrintInitFlag, &DebugPrintInit);
|
||||||
|
std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog);
|
||||||
|
|
||||||
|
// buffer if we haven't opened the log yet
|
||||||
|
if (fileout == nullptr) {
|
||||||
|
assert(vMsgsBeforeOpenLog);
|
||||||
|
ret = strTimestamped.length();
|
||||||
|
vMsgsBeforeOpenLog->push_back(strTimestamped);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// reopen the log file, if requested
|
||||||
|
if (fReopenDebugLog) {
|
||||||
|
fReopenDebugLog = false;
|
||||||
|
fs::path pathDebug = GetDebugLogPath();
|
||||||
|
if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr)
|
||||||
|
setbuf(fileout, nullptr); // unbuffered
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = FileWriteStr(strTimestamped, fileout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShrinkDebugFile()
|
||||||
|
{
|
||||||
|
// Amount of debug.log to save at end when shrinking (must fit in memory)
|
||||||
|
constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
|
||||||
|
// Scroll debug.log if it's getting too big
|
||||||
|
fs::path pathLog = GetDebugLogPath();
|
||||||
|
FILE* file = fsbridge::fopen(pathLog, "r");
|
||||||
|
|
||||||
|
// Special files (e.g. device nodes) may not have a size.
|
||||||
|
size_t log_size = 0;
|
||||||
|
try {
|
||||||
|
log_size = fs::file_size(pathLog);
|
||||||
|
} catch (boost::filesystem::filesystem_error &) {}
|
||||||
|
|
||||||
|
// If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
|
||||||
|
// trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
|
||||||
|
if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
|
||||||
|
{
|
||||||
|
// Restart the file with some of the end
|
||||||
|
std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
|
||||||
|
fseek(file, -((long)vch.size()), SEEK_END);
|
||||||
|
int nBytes = fread(vch.data(), 1, vch.size(), file);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
file = fsbridge::fopen(pathLog, "w");
|
||||||
|
if (file)
|
||||||
|
{
|
||||||
|
fwrite(vch.data(), 1, nBytes, file);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (file != nullptr)
|
||||||
|
fclose(file);
|
||||||
|
}
|
125
src/logging.h
Normal file
125
src/logging.h
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_LOGGING_H
|
||||||
|
#define BITCOIN_LOGGING_H
|
||||||
|
|
||||||
|
#include <fs.h>
|
||||||
|
#include <tinyformat.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
static const bool DEFAULT_LOGTIMEMICROS = false;
|
||||||
|
static const bool DEFAULT_LOGIPS = false;
|
||||||
|
static const bool DEFAULT_LOGTIMESTAMPS = true;
|
||||||
|
extern const char * const DEFAULT_DEBUGLOGFILE;
|
||||||
|
|
||||||
|
extern bool fPrintToConsole;
|
||||||
|
extern bool fPrintToDebugLog;
|
||||||
|
|
||||||
|
extern bool fLogTimestamps;
|
||||||
|
extern bool fLogTimeMicros;
|
||||||
|
extern bool fLogIPs;
|
||||||
|
extern std::atomic<bool> fReopenDebugLog;
|
||||||
|
|
||||||
|
extern std::atomic<uint32_t> logCategories;
|
||||||
|
|
||||||
|
struct CLogCategoryActive
|
||||||
|
{
|
||||||
|
std::string category;
|
||||||
|
bool active;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace BCLog {
|
||||||
|
enum LogFlags : uint32_t {
|
||||||
|
NONE = 0,
|
||||||
|
NET = (1 << 0),
|
||||||
|
TOR = (1 << 1),
|
||||||
|
MEMPOOL = (1 << 2),
|
||||||
|
HTTP = (1 << 3),
|
||||||
|
BENCH = (1 << 4),
|
||||||
|
ZMQ = (1 << 5),
|
||||||
|
DB = (1 << 6),
|
||||||
|
RPC = (1 << 7),
|
||||||
|
ESTIMATEFEE = (1 << 8),
|
||||||
|
ADDRMAN = (1 << 9),
|
||||||
|
SELECTCOINS = (1 << 10),
|
||||||
|
REINDEX = (1 << 11),
|
||||||
|
CMPCTBLOCK = (1 << 12),
|
||||||
|
RAND = (1 << 13),
|
||||||
|
PRUNE = (1 << 14),
|
||||||
|
PROXY = (1 << 15),
|
||||||
|
MEMPOOLREJ = (1 << 16),
|
||||||
|
LIBEVENT = (1 << 17),
|
||||||
|
COINDB = (1 << 18),
|
||||||
|
QT = (1 << 19),
|
||||||
|
LEVELDB = (1 << 20),
|
||||||
|
ALL = ~(uint32_t)0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/** Return true if log accepts specified category */
|
||||||
|
static inline bool LogAcceptCategory(uint32_t category)
|
||||||
|
{
|
||||||
|
return (logCategories.load(std::memory_order_relaxed) & category) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a string with the log categories. */
|
||||||
|
std::string ListLogCategories();
|
||||||
|
|
||||||
|
/** Returns a vector of the active log categories. */
|
||||||
|
std::vector<CLogCategoryActive> ListActiveLogCategories();
|
||||||
|
|
||||||
|
/** Return true if str parses as a log category and set the flags in f */
|
||||||
|
bool GetLogCategory(uint32_t *f, const std::string *str);
|
||||||
|
|
||||||
|
/** Send a string to the log output */
|
||||||
|
int LogPrintStr(const std::string &str);
|
||||||
|
|
||||||
|
/** Get format string from VA_ARGS for error reporting */
|
||||||
|
template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; }
|
||||||
|
|
||||||
|
static inline void MarkUsed() {}
|
||||||
|
template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args)
|
||||||
|
{
|
||||||
|
(void)t;
|
||||||
|
MarkUsed(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Be conservative when using LogPrintf/error or other things which
|
||||||
|
// unconditionally log to debug.log! It should not be the case that an inbound
|
||||||
|
// peer can fill up a user's disk with debug.log entries.
|
||||||
|
|
||||||
|
#ifdef USE_COVERAGE
|
||||||
|
#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0)
|
||||||
|
#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0)
|
||||||
|
#else
|
||||||
|
#define LogPrintf(...) do { \
|
||||||
|
if (fPrintToConsole || fPrintToDebugLog) { \
|
||||||
|
std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \
|
||||||
|
try { \
|
||||||
|
_log_msg_ = tfm::format(__VA_ARGS__); \
|
||||||
|
} catch (tinyformat::format_error &fmterr) { \
|
||||||
|
/* Original format string will have newline so don't add one here */ \
|
||||||
|
_log_msg_ = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
LogPrintStr(_log_msg_); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define LogPrint(category, ...) do { \
|
||||||
|
if (LogAcceptCategory((category))) { \
|
||||||
|
LogPrintf(__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fs::path GetDebugLogPath();
|
||||||
|
bool OpenDebugLog();
|
||||||
|
void ShrinkDebugFile();
|
||||||
|
|
||||||
|
#endif // BITCOIN_LOGGING_H
|
270
src/util.cpp
270
src/util.cpp
|
@ -84,21 +84,11 @@ const int64_t nStartupTime = GetTime();
|
||||||
|
|
||||||
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
|
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
|
||||||
const char * const BITCOIN_PID_FILENAME = "bitcoind.pid";
|
const char * const BITCOIN_PID_FILENAME = "bitcoind.pid";
|
||||||
const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
|
|
||||||
|
|
||||||
ArgsManager gArgs;
|
ArgsManager gArgs;
|
||||||
bool fPrintToConsole = false;
|
|
||||||
bool fPrintToDebugLog = true;
|
|
||||||
|
|
||||||
bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS;
|
|
||||||
bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS;
|
|
||||||
bool fLogIPs = DEFAULT_LOGIPS;
|
|
||||||
std::atomic<bool> fReopenDebugLog(false);
|
|
||||||
CTranslationInterface translationInterface;
|
CTranslationInterface translationInterface;
|
||||||
|
|
||||||
/** Log categories bitfield. */
|
|
||||||
std::atomic<uint32_t> logCategories(0);
|
|
||||||
|
|
||||||
/** Init OpenSSL library multithreading support */
|
/** Init OpenSSL library multithreading support */
|
||||||
static std::unique_ptr<CCriticalSection[]> ppmutexOpenSSL;
|
static std::unique_ptr<CCriticalSection[]> ppmutexOpenSSL;
|
||||||
void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS
|
void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS
|
||||||
|
@ -147,231 +137,6 @@ public:
|
||||||
}
|
}
|
||||||
instance_of_cinit;
|
instance_of_cinit;
|
||||||
|
|
||||||
/**
|
|
||||||
* LogPrintf() has been broken a couple of times now
|
|
||||||
* by well-meaning people adding mutexes in the most straightforward way.
|
|
||||||
* It breaks because it may be called by global destructors during shutdown.
|
|
||||||
* Since the order of destruction of static/global objects is undefined,
|
|
||||||
* defining a mutex as a global object doesn't work (the mutex gets
|
|
||||||
* destroyed, and then some later destructor calls OutputDebugStringF,
|
|
||||||
* maybe indirectly, and you get a core dump at shutdown trying to lock
|
|
||||||
* the mutex).
|
|
||||||
*/
|
|
||||||
|
|
||||||
static std::once_flag debugPrintInitFlag;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We use std::call_once() to make sure mutexDebugLog and
|
|
||||||
* vMsgsBeforeOpenLog are initialized in a thread-safe manner.
|
|
||||||
*
|
|
||||||
* NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog
|
|
||||||
* are leaked on exit. This is ugly, but will be cleaned up by
|
|
||||||
* the OS/libc. When the shutdown sequence is fully audited and
|
|
||||||
* tested, explicit destruction of these objects can be implemented.
|
|
||||||
*/
|
|
||||||
static FILE* fileout = nullptr;
|
|
||||||
static std::mutex* mutexDebugLog = nullptr;
|
|
||||||
static std::list<std::string>* vMsgsBeforeOpenLog;
|
|
||||||
|
|
||||||
static int FileWriteStr(const std::string &str, FILE *fp)
|
|
||||||
{
|
|
||||||
return fwrite(str.data(), 1, str.size(), fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DebugPrintInit()
|
|
||||||
{
|
|
||||||
assert(mutexDebugLog == nullptr);
|
|
||||||
mutexDebugLog = new std::mutex();
|
|
||||||
vMsgsBeforeOpenLog = new std::list<std::string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::path GetDebugLogPath()
|
|
||||||
{
|
|
||||||
fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
|
|
||||||
return AbsPathForConfigVal(logfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OpenDebugLog()
|
|
||||||
{
|
|
||||||
std::call_once(debugPrintInitFlag, &DebugPrintInit);
|
|
||||||
std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog);
|
|
||||||
|
|
||||||
assert(fileout == nullptr);
|
|
||||||
assert(vMsgsBeforeOpenLog);
|
|
||||||
fs::path pathDebug = GetDebugLogPath();
|
|
||||||
|
|
||||||
fileout = fsbridge::fopen(pathDebug, "a");
|
|
||||||
if (!fileout) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
setbuf(fileout, nullptr); // unbuffered
|
|
||||||
// dump buffered messages from before we opened the log
|
|
||||||
while (!vMsgsBeforeOpenLog->empty()) {
|
|
||||||
FileWriteStr(vMsgsBeforeOpenLog->front(), fileout);
|
|
||||||
vMsgsBeforeOpenLog->pop_front();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete vMsgsBeforeOpenLog;
|
|
||||||
vMsgsBeforeOpenLog = nullptr;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CLogCategoryDesc
|
|
||||||
{
|
|
||||||
uint32_t flag;
|
|
||||||
std::string category;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CLogCategoryDesc LogCategories[] =
|
|
||||||
{
|
|
||||||
{BCLog::NONE, "0"},
|
|
||||||
{BCLog::NONE, "none"},
|
|
||||||
{BCLog::NET, "net"},
|
|
||||||
{BCLog::TOR, "tor"},
|
|
||||||
{BCLog::MEMPOOL, "mempool"},
|
|
||||||
{BCLog::HTTP, "http"},
|
|
||||||
{BCLog::BENCH, "bench"},
|
|
||||||
{BCLog::ZMQ, "zmq"},
|
|
||||||
{BCLog::DB, "db"},
|
|
||||||
{BCLog::RPC, "rpc"},
|
|
||||||
{BCLog::ESTIMATEFEE, "estimatefee"},
|
|
||||||
{BCLog::ADDRMAN, "addrman"},
|
|
||||||
{BCLog::SELECTCOINS, "selectcoins"},
|
|
||||||
{BCLog::REINDEX, "reindex"},
|
|
||||||
{BCLog::CMPCTBLOCK, "cmpctblock"},
|
|
||||||
{BCLog::RAND, "rand"},
|
|
||||||
{BCLog::PRUNE, "prune"},
|
|
||||||
{BCLog::PROXY, "proxy"},
|
|
||||||
{BCLog::MEMPOOLREJ, "mempoolrej"},
|
|
||||||
{BCLog::LIBEVENT, "libevent"},
|
|
||||||
{BCLog::COINDB, "coindb"},
|
|
||||||
{BCLog::QT, "qt"},
|
|
||||||
{BCLog::LEVELDB, "leveldb"},
|
|
||||||
{BCLog::ALL, "1"},
|
|
||||||
{BCLog::ALL, "all"},
|
|
||||||
};
|
|
||||||
|
|
||||||
bool GetLogCategory(uint32_t *f, const std::string *str)
|
|
||||||
{
|
|
||||||
if (f && str) {
|
|
||||||
if (*str == "") {
|
|
||||||
*f = BCLog::ALL;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
|
|
||||||
if (LogCategories[i].category == *str) {
|
|
||||||
*f = LogCategories[i].flag;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ListLogCategories()
|
|
||||||
{
|
|
||||||
std::string ret;
|
|
||||||
int outcount = 0;
|
|
||||||
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
|
|
||||||
// Omit the special cases.
|
|
||||||
if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
|
|
||||||
if (outcount != 0) ret += ", ";
|
|
||||||
ret += LogCategories[i].category;
|
|
||||||
outcount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<CLogCategoryActive> ListActiveLogCategories()
|
|
||||||
{
|
|
||||||
std::vector<CLogCategoryActive> ret;
|
|
||||||
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
|
|
||||||
// Omit the special cases.
|
|
||||||
if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
|
|
||||||
CLogCategoryActive catActive;
|
|
||||||
catActive.category = LogCategories[i].category;
|
|
||||||
catActive.active = LogAcceptCategory(LogCategories[i].flag);
|
|
||||||
ret.push_back(catActive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* fStartedNewLine is a state variable held by the calling context that will
|
|
||||||
* suppress printing of the timestamp when multiple calls are made that don't
|
|
||||||
* end in a newline. Initialize it to true, and hold it, in the calling context.
|
|
||||||
*/
|
|
||||||
static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fStartedNewLine)
|
|
||||||
{
|
|
||||||
std::string strStamped;
|
|
||||||
|
|
||||||
if (!fLogTimestamps)
|
|
||||||
return str;
|
|
||||||
|
|
||||||
if (*fStartedNewLine) {
|
|
||||||
int64_t nTimeMicros = GetTimeMicros();
|
|
||||||
strStamped = FormatISO8601DateTime(nTimeMicros/1000000);
|
|
||||||
if (fLogTimeMicros) {
|
|
||||||
strStamped.pop_back();
|
|
||||||
strStamped += strprintf(".%06dZ", nTimeMicros%1000000);
|
|
||||||
}
|
|
||||||
int64_t mocktime = GetMockTime();
|
|
||||||
if (mocktime) {
|
|
||||||
strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")";
|
|
||||||
}
|
|
||||||
strStamped += ' ' + str;
|
|
||||||
} else
|
|
||||||
strStamped = str;
|
|
||||||
|
|
||||||
if (!str.empty() && str[str.size()-1] == '\n')
|
|
||||||
*fStartedNewLine = true;
|
|
||||||
else
|
|
||||||
*fStartedNewLine = false;
|
|
||||||
|
|
||||||
return strStamped;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LogPrintStr(const std::string &str)
|
|
||||||
{
|
|
||||||
int ret = 0; // Returns total number of characters written
|
|
||||||
static std::atomic_bool fStartedNewLine(true);
|
|
||||||
|
|
||||||
std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine);
|
|
||||||
|
|
||||||
if (fPrintToConsole) {
|
|
||||||
// print to console
|
|
||||||
ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
if (fPrintToDebugLog) {
|
|
||||||
std::call_once(debugPrintInitFlag, &DebugPrintInit);
|
|
||||||
std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog);
|
|
||||||
|
|
||||||
// buffer if we haven't opened the log yet
|
|
||||||
if (fileout == nullptr) {
|
|
||||||
assert(vMsgsBeforeOpenLog);
|
|
||||||
ret = strTimestamped.length();
|
|
||||||
vMsgsBeforeOpenLog->push_back(strTimestamped);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// reopen the log file, if requested
|
|
||||||
if (fReopenDebugLog) {
|
|
||||||
fReopenDebugLog = false;
|
|
||||||
fs::path pathDebug = GetDebugLogPath();
|
|
||||||
if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr)
|
|
||||||
setbuf(fileout, nullptr); // unbuffered
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = FileWriteStr(strTimestamped, fileout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A map that contains all the currently held directory locks. After
|
/** A map that contains all the currently held directory locks. After
|
||||||
* successful locking, these will be held here until the global destructor
|
* successful locking, these will be held here until the global destructor
|
||||||
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
|
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
|
||||||
|
@ -1117,41 +882,6 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShrinkDebugFile()
|
|
||||||
{
|
|
||||||
// Amount of debug.log to save at end when shrinking (must fit in memory)
|
|
||||||
constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
|
|
||||||
// Scroll debug.log if it's getting too big
|
|
||||||
fs::path pathLog = GetDebugLogPath();
|
|
||||||
FILE* file = fsbridge::fopen(pathLog, "r");
|
|
||||||
|
|
||||||
// Special files (e.g. device nodes) may not have a size.
|
|
||||||
size_t log_size = 0;
|
|
||||||
try {
|
|
||||||
log_size = fs::file_size(pathLog);
|
|
||||||
} catch (boost::filesystem::filesystem_error &) {}
|
|
||||||
|
|
||||||
// If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
|
|
||||||
// trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
|
|
||||||
if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
|
|
||||||
{
|
|
||||||
// Restart the file with some of the end
|
|
||||||
std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
|
|
||||||
fseek(file, -((long)vch.size()), SEEK_END);
|
|
||||||
int nBytes = fread(vch.data(), 1, vch.size(), file);
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
file = fsbridge::fopen(pathLog, "w");
|
|
||||||
if (file)
|
|
||||||
{
|
|
||||||
fwrite(vch.data(), 1, nBytes, file);
|
|
||||||
fclose(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (file != nullptr)
|
|
||||||
fclose(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
|
fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
|
||||||
{
|
{
|
||||||
|
|
109
src/util.h
109
src/util.h
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server/client environment: argument handling, config file parsing,
|
* Server/client environment: argument handling, config file parsing,
|
||||||
* logging, thread wrappers, startup time
|
* thread wrappers, startup time
|
||||||
*/
|
*/
|
||||||
#ifndef BITCOIN_UTIL_H
|
#ifndef BITCOIN_UTIL_H
|
||||||
#define BITCOIN_UTIL_H
|
#define BITCOIN_UTIL_H
|
||||||
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include <compat.h>
|
#include <compat.h>
|
||||||
#include <fs.h>
|
#include <fs.h>
|
||||||
|
#include <logging.h>
|
||||||
#include <sync.h>
|
#include <sync.h>
|
||||||
#include <tinyformat.h>
|
#include <tinyformat.h>
|
||||||
#include <utiltime.h>
|
#include <utiltime.h>
|
||||||
|
@ -36,11 +37,6 @@
|
||||||
// Application startup time (used for uptime calculation)
|
// Application startup time (used for uptime calculation)
|
||||||
int64_t GetStartupTime();
|
int64_t GetStartupTime();
|
||||||
|
|
||||||
static const bool DEFAULT_LOGTIMEMICROS = false;
|
|
||||||
static const bool DEFAULT_LOGIPS = false;
|
|
||||||
static const bool DEFAULT_LOGTIMESTAMPS = true;
|
|
||||||
extern const char * const DEFAULT_DEBUGLOGFILE;
|
|
||||||
|
|
||||||
/** Signals for translation. */
|
/** Signals for translation. */
|
||||||
class CTranslationInterface
|
class CTranslationInterface
|
||||||
{
|
{
|
||||||
|
@ -49,20 +45,11 @@ public:
|
||||||
boost::signals2::signal<std::string (const char* psz)> Translate;
|
boost::signals2::signal<std::string (const char* psz)> Translate;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern bool fPrintToConsole;
|
|
||||||
extern bool fPrintToDebugLog;
|
|
||||||
|
|
||||||
extern bool fLogTimestamps;
|
|
||||||
extern bool fLogTimeMicros;
|
|
||||||
extern bool fLogIPs;
|
|
||||||
extern std::atomic<bool> fReopenDebugLog;
|
|
||||||
extern CTranslationInterface translationInterface;
|
extern CTranslationInterface translationInterface;
|
||||||
|
|
||||||
extern const char * const BITCOIN_CONF_FILENAME;
|
extern const char * const BITCOIN_CONF_FILENAME;
|
||||||
extern const char * const BITCOIN_PID_FILENAME;
|
extern const char * const BITCOIN_PID_FILENAME;
|
||||||
|
|
||||||
extern std::atomic<uint32_t> logCategories;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translation function: Call Translate signal on UI interface, which returns a boost::optional result.
|
* Translation function: Call Translate signal on UI interface, which returns a boost::optional result.
|
||||||
* If no translation slot is registered, nothing is returned, and simply return the input.
|
* If no translation slot is registered, nothing is returned, and simply return the input.
|
||||||
|
@ -76,95 +63,6 @@ inline std::string _(const char* psz)
|
||||||
void SetupEnvironment();
|
void SetupEnvironment();
|
||||||
bool SetupNetworking();
|
bool SetupNetworking();
|
||||||
|
|
||||||
struct CLogCategoryActive
|
|
||||||
{
|
|
||||||
std::string category;
|
|
||||||
bool active;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace BCLog {
|
|
||||||
enum LogFlags : uint32_t {
|
|
||||||
NONE = 0,
|
|
||||||
NET = (1 << 0),
|
|
||||||
TOR = (1 << 1),
|
|
||||||
MEMPOOL = (1 << 2),
|
|
||||||
HTTP = (1 << 3),
|
|
||||||
BENCH = (1 << 4),
|
|
||||||
ZMQ = (1 << 5),
|
|
||||||
DB = (1 << 6),
|
|
||||||
RPC = (1 << 7),
|
|
||||||
ESTIMATEFEE = (1 << 8),
|
|
||||||
ADDRMAN = (1 << 9),
|
|
||||||
SELECTCOINS = (1 << 10),
|
|
||||||
REINDEX = (1 << 11),
|
|
||||||
CMPCTBLOCK = (1 << 12),
|
|
||||||
RAND = (1 << 13),
|
|
||||||
PRUNE = (1 << 14),
|
|
||||||
PROXY = (1 << 15),
|
|
||||||
MEMPOOLREJ = (1 << 16),
|
|
||||||
LIBEVENT = (1 << 17),
|
|
||||||
COINDB = (1 << 18),
|
|
||||||
QT = (1 << 19),
|
|
||||||
LEVELDB = (1 << 20),
|
|
||||||
ALL = ~(uint32_t)0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/** Return true if log accepts specified category */
|
|
||||||
static inline bool LogAcceptCategory(uint32_t category)
|
|
||||||
{
|
|
||||||
return (logCategories.load(std::memory_order_relaxed) & category) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns a string with the log categories. */
|
|
||||||
std::string ListLogCategories();
|
|
||||||
|
|
||||||
/** Returns a vector of the active log categories. */
|
|
||||||
std::vector<CLogCategoryActive> ListActiveLogCategories();
|
|
||||||
|
|
||||||
/** Return true if str parses as a log category and set the flags in f */
|
|
||||||
bool GetLogCategory(uint32_t *f, const std::string *str);
|
|
||||||
|
|
||||||
/** Send a string to the log output */
|
|
||||||
int LogPrintStr(const std::string &str);
|
|
||||||
|
|
||||||
/** Get format string from VA_ARGS for error reporting */
|
|
||||||
template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; }
|
|
||||||
|
|
||||||
static inline void MarkUsed() {}
|
|
||||||
template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args)
|
|
||||||
{
|
|
||||||
(void)t;
|
|
||||||
MarkUsed(args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Be conservative when using LogPrintf/error or other things which
|
|
||||||
// unconditionally log to debug.log! It should not be the case that an inbound
|
|
||||||
// peer can fill up a user's disk with debug.log entries.
|
|
||||||
|
|
||||||
#ifdef USE_COVERAGE
|
|
||||||
#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0)
|
|
||||||
#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0)
|
|
||||||
#else
|
|
||||||
#define LogPrintf(...) do { \
|
|
||||||
if (fPrintToConsole || fPrintToDebugLog) { \
|
|
||||||
std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \
|
|
||||||
try { \
|
|
||||||
_log_msg_ = tfm::format(__VA_ARGS__); \
|
|
||||||
} catch (tinyformat::format_error &fmterr) { \
|
|
||||||
/* Original format string will have newline so don't add one here */ \
|
|
||||||
_log_msg_ = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \
|
|
||||||
} \
|
|
||||||
LogPrintStr(_log_msg_); \
|
|
||||||
} \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define LogPrint(category, ...) do { \
|
|
||||||
if (LogAcceptCategory((category))) { \
|
|
||||||
LogPrintf(__VA_ARGS__); \
|
|
||||||
} \
|
|
||||||
} while(0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
bool error(const char* fmt, const Args&... args)
|
bool error(const char* fmt, const Args&... args)
|
||||||
{
|
{
|
||||||
|
@ -199,9 +97,6 @@ void CreatePidFile(const fs::path &path, pid_t pid);
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
|
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
|
||||||
#endif
|
#endif
|
||||||
fs::path GetDebugLogPath();
|
|
||||||
bool OpenDebugLog();
|
|
||||||
void ShrinkDebugFile();
|
|
||||||
void runCommand(const std::string& strCommand);
|
void runCommand(const std::string& strCommand);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue