Merge #12954: util: Refactor logging code into a global object
8c2d695c4a
util: Store debug log file path in BCLog::Logger member. (Jim Posen)8e7b961388
scripted-diff: Rename BCLog::Logger member variables. (Jim Posen)1eac317f25
util: Refactor GetLogCategory. (Jim Posen)3316a9ebb6
util: Encapsulate logCategories within BCLog::Logger. (Jim Posen)6a6d764ca5
util: Move debug file management functions into Logger. (Jim Posen)f55f4fcf05
util: Establish global logger object. (Jim Posen) Pull request description: This is purely a refactor with no behavior changes. This creates a new class `BCLog::Logger` to encapsulate all global logging configuration and state. Tree-SHA512: b34811f54a53b7375d7b6f84925453c6f2419d21179379ee28b3843d0f4ff8e22020de84a5e783453ea927e9074e32de8ecd05a6fa50d7bb05502001aaed8e53
This commit is contained in:
commit
5a666428b0
10 changed files with 205 additions and 180 deletions
|
@ -46,7 +46,6 @@ main(int argc, char** argv)
|
|||
RandomInit();
|
||||
ECC_Start();
|
||||
SetupEnvironment();
|
||||
fPrintToDebugLog = false; // don't want to write to debug.log file
|
||||
|
||||
int64_t evaluations = gArgs.GetArg("-evals", DEFAULT_BENCH_EVALUATIONS);
|
||||
std::string regex_filter = gArgs.GetArg("-filter", DEFAULT_BENCH_FILTER);
|
||||
|
|
|
@ -364,8 +364,8 @@ bool InitHTTPServer()
|
|||
// Update libevent's log handling. Returns false if our version of
|
||||
// libevent doesn't support debug logging, in which case we should
|
||||
// clear the BCLog::LIBEVENT flag.
|
||||
if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
|
||||
logCategories &= ~BCLog::LIBEVENT;
|
||||
if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) {
|
||||
g_logger->DisableCategory(BCLog::LIBEVENT);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
|
|
@ -32,7 +32,7 @@ void InterruptHTTPServer();
|
|||
/** Stop HTTP server */
|
||||
void StopHTTPServer();
|
||||
|
||||
/** Change logging level for libevent. Removes BCLog::LIBEVENT from logCategories if
|
||||
/** Change logging level for libevent. Removes BCLog::LIBEVENT from log categories if
|
||||
* libevent doesn't support debug logging.*/
|
||||
bool UpdateHTTPServerLogging(bool enable);
|
||||
|
||||
|
|
36
src/init.cpp
36
src/init.cpp
|
@ -305,7 +305,7 @@ static void HandleSIGTERM(int)
|
|||
|
||||
static void HandleSIGHUP(int)
|
||||
{
|
||||
fReopenDebugLog = true;
|
||||
g_logger->m_reopen_file = true;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
|
@ -826,15 +826,18 @@ static std::string ResolveErrMsg(const char * const optname, const std::string&
|
|||
*/
|
||||
void InitLogging()
|
||||
{
|
||||
g_logger->m_print_to_file = !gArgs.IsArgNegated("-debuglogfile");
|
||||
g_logger->m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
|
||||
|
||||
// Add newlines to the logfile to distinguish this execution from the last
|
||||
// one; called before console logging is set up, so this is only sent to
|
||||
// debug.log.
|
||||
LogPrintf("\n\n\n\n\n");
|
||||
|
||||
fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
|
||||
fPrintToDebugLog = !gArgs.IsArgNegated("-debuglogfile");
|
||||
fLogTimestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
|
||||
fLogTimeMicros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
|
||||
g_logger->m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
|
||||
g_logger->m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
|
||||
g_logger->m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
|
||||
|
||||
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
|
||||
|
||||
std::string version_string = FormatFullVersion();
|
||||
|
@ -962,24 +965,18 @@ bool AppInitParameterInteraction()
|
|||
if (std::none_of(categories.begin(), categories.end(),
|
||||
[](std::string cat){return cat == "0" || cat == "none";})) {
|
||||
for (const auto& cat : categories) {
|
||||
uint32_t flag = 0;
|
||||
if (!GetLogCategory(&flag, &cat)) {
|
||||
if (!g_logger->EnableCategory(cat)) {
|
||||
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat));
|
||||
continue;
|
||||
}
|
||||
logCategories |= flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now remove the logging categories which were explicitly excluded
|
||||
for (const std::string& cat : gArgs.GetArgs("-debugexclude")) {
|
||||
uint32_t flag = 0;
|
||||
if (!GetLogCategory(&flag, &cat)) {
|
||||
if (!g_logger->DisableCategory(cat)) {
|
||||
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
|
||||
continue;
|
||||
}
|
||||
logCategories &= ~flag;
|
||||
}
|
||||
|
||||
// Check for -debugnet
|
||||
|
@ -1230,18 +1227,19 @@ bool AppInitMain()
|
|||
#ifndef WIN32
|
||||
CreatePidFile(GetPidFile(), getpid());
|
||||
#endif
|
||||
if (fPrintToDebugLog) {
|
||||
if (gArgs.GetBoolArg("-shrinkdebugfile", logCategories == BCLog::NONE)) {
|
||||
if (g_logger->m_print_to_file) {
|
||||
if (gArgs.GetBoolArg("-shrinkdebugfile", g_logger->DefaultShrinkDebugFile())) {
|
||||
// Do this first since it both loads a bunch of debug.log into memory,
|
||||
// and because this needs to happen before any other debug.log printing
|
||||
ShrinkDebugFile();
|
||||
g_logger->ShrinkDebugFile();
|
||||
}
|
||||
if (!OpenDebugLog()) {
|
||||
return InitError(strprintf("Could not open debug log file %s", GetDebugLogPath().string()));
|
||||
if (!g_logger->OpenDebugLog()) {
|
||||
return InitError(strprintf("Could not open debug log file %s",
|
||||
g_logger->m_file_path.string()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!fLogTimestamps)
|
||||
if (!g_logger->m_log_timestamps)
|
||||
LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime()));
|
||||
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
|
||||
LogPrintf("Using data directory %s\n", GetDataDir().string());
|
||||
|
|
|
@ -60,7 +60,7 @@ class NodeImpl : public Node
|
|||
void initLogging() override { InitLogging(); }
|
||||
void initParameterInteraction() override { InitParameterInteraction(); }
|
||||
std::string getWarnings(const std::string& type) override { return GetWarnings(type); }
|
||||
uint32_t getLogCategories() override { return ::logCategories; }
|
||||
uint32_t getLogCategories() override { return g_logger->GetCategoryMask(); }
|
||||
bool baseInitialize() override
|
||||
{
|
||||
return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() &&
|
||||
|
|
215
src/logging.cpp
215
src/logging.cpp
|
@ -4,97 +4,93 @@
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <logging.h>
|
||||
#include <util.h>
|
||||
#include <utilstrencodings.h>
|
||||
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <utiltime.h>
|
||||
|
||||
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: the logger instances is leaked on exit. This is ugly, but will be
|
||||
* cleaned up by the OS/libc. Defining a logger as a global object doesn't work
|
||||
* since the order of destruction of static/global objects is undefined.
|
||||
* Consider if the logger gets destroyed, and then some later destructor calls
|
||||
* LogPrintf, maybe indirectly, and you get a core dump at shutdown trying to
|
||||
* access the logger. When the shutdown sequence is fully audited and tested,
|
||||
* explicit destruction of these objects can be implemented by changing this
|
||||
* from a raw pointer to a std::unique_ptr.
|
||||
*
|
||||
* 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.
|
||||
* This method of initialization was originally introduced in
|
||||
* ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c.
|
||||
*/
|
||||
static FILE* fileout = nullptr;
|
||||
static std::mutex* mutexDebugLog = nullptr;
|
||||
static std::list<std::string>* vMsgsBeforeOpenLog;
|
||||
BCLog::Logger* const g_logger = new BCLog::Logger();
|
||||
|
||||
bool fLogIPs = DEFAULT_LOGIPS;
|
||||
|
||||
static int FileWriteStr(const std::string &str, FILE *fp)
|
||||
{
|
||||
return fwrite(str.data(), 1, str.size(), fp);
|
||||
}
|
||||
|
||||
static void DebugPrintInit()
|
||||
bool BCLog::Logger::OpenDebugLog()
|
||||
{
|
||||
assert(mutexDebugLog == nullptr);
|
||||
mutexDebugLog = new std::mutex();
|
||||
vMsgsBeforeOpenLog = new std::list<std::string>;
|
||||
}
|
||||
std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
|
||||
|
||||
fs::path GetDebugLogPath()
|
||||
{
|
||||
fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
|
||||
return AbsPathForConfigVal(logfile);
|
||||
}
|
||||
assert(m_fileout == nullptr);
|
||||
assert(!m_file_path.empty());
|
||||
|
||||
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) {
|
||||
m_fileout = fsbridge::fopen(m_file_path, "a");
|
||||
if (!m_fileout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setbuf(fileout, nullptr); // unbuffered
|
||||
setbuf(m_fileout, nullptr); // unbuffered
|
||||
// dump buffered messages from before we opened the log
|
||||
while (!vMsgsBeforeOpenLog->empty()) {
|
||||
FileWriteStr(vMsgsBeforeOpenLog->front(), fileout);
|
||||
vMsgsBeforeOpenLog->pop_front();
|
||||
while (!m_msgs_before_open.empty()) {
|
||||
FileWriteStr(m_msgs_before_open.front(), m_fileout);
|
||||
m_msgs_before_open.pop_front();
|
||||
}
|
||||
|
||||
delete vMsgsBeforeOpenLog;
|
||||
vMsgsBeforeOpenLog = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
|
||||
{
|
||||
m_categories |= flag;
|
||||
}
|
||||
|
||||
bool BCLog::Logger::EnableCategory(const std::string& str)
|
||||
{
|
||||
BCLog::LogFlags flag;
|
||||
if (!GetLogCategory(flag, str)) return false;
|
||||
EnableCategory(flag);
|
||||
return true;
|
||||
}
|
||||
|
||||
void BCLog::Logger::DisableCategory(BCLog::LogFlags flag)
|
||||
{
|
||||
m_categories &= ~flag;
|
||||
}
|
||||
|
||||
bool BCLog::Logger::DisableCategory(const std::string& str)
|
||||
{
|
||||
BCLog::LogFlags flag;
|
||||
if (!GetLogCategory(flag, str)) return false;
|
||||
DisableCategory(flag);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BCLog::Logger::WillLogCategory(BCLog::LogFlags category) const
|
||||
{
|
||||
return (m_categories.load(std::memory_order_relaxed) & category) != 0;
|
||||
}
|
||||
|
||||
bool BCLog::Logger::DefaultShrinkDebugFile() const
|
||||
{
|
||||
return m_categories == BCLog::NONE;
|
||||
}
|
||||
|
||||
struct CLogCategoryDesc
|
||||
{
|
||||
uint32_t flag;
|
||||
BCLog::LogFlags flag;
|
||||
std::string category;
|
||||
};
|
||||
|
||||
|
@ -127,19 +123,17 @@ const CLogCategoryDesc LogCategories[] =
|
|||
{BCLog::ALL, "all"},
|
||||
};
|
||||
|
||||
bool GetLogCategory(uint32_t *f, const std::string *str)
|
||||
bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
|
||||
{
|
||||
if (f && str) {
|
||||
if (*str == "") {
|
||||
*f = BCLog::ALL;
|
||||
if (str == "") {
|
||||
flag = BCLog::ALL;
|
||||
return true;
|
||||
}
|
||||
for (const CLogCategoryDesc& category_desc : LogCategories) {
|
||||
if (category_desc.category == str) {
|
||||
flag = category_desc.flag;
|
||||
return true;
|
||||
}
|
||||
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
|
||||
if (LogCategories[i].category == *str) {
|
||||
*f = LogCategories[i].flag;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -148,11 +142,11 @@ std::string ListLogCategories()
|
|||
{
|
||||
std::string ret;
|
||||
int outcount = 0;
|
||||
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
|
||||
for (const CLogCategoryDesc& category_desc : LogCategories) {
|
||||
// Omit the special cases.
|
||||
if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
|
||||
if (category_desc.flag != BCLog::NONE && category_desc.flag != BCLog::ALL) {
|
||||
if (outcount != 0) ret += ", ";
|
||||
ret += LogCategories[i].category;
|
||||
ret += category_desc.category;
|
||||
outcount++;
|
||||
}
|
||||
}
|
||||
|
@ -162,34 +156,29 @@ std::string ListLogCategories()
|
|||
std::vector<CLogCategoryActive> ListActiveLogCategories()
|
||||
{
|
||||
std::vector<CLogCategoryActive> ret;
|
||||
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
|
||||
for (const CLogCategoryDesc& category_desc : LogCategories) {
|
||||
// Omit the special cases.
|
||||
if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
|
||||
if (category_desc.flag != BCLog::NONE && category_desc.flag != BCLog::ALL) {
|
||||
CLogCategoryActive catActive;
|
||||
catActive.category = LogCategories[i].category;
|
||||
catActive.active = LogAcceptCategory(LogCategories[i].flag);
|
||||
catActive.category = category_desc.category;
|
||||
catActive.active = LogAcceptCategory(category_desc.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 BCLog::Logger::LogTimestampStr(const std::string &str)
|
||||
{
|
||||
std::string strStamped;
|
||||
|
||||
if (!fLogTimestamps)
|
||||
if (!m_log_timestamps)
|
||||
return str;
|
||||
|
||||
if (*fStartedNewLine) {
|
||||
if (m_started_new_line) {
|
||||
int64_t nTimeMicros = GetTimeMicros();
|
||||
strStamped = FormatISO8601DateTime(nTimeMicros/1000000);
|
||||
if (fLogTimeMicros) {
|
||||
if (m_log_time_micros) {
|
||||
strStamped.pop_back();
|
||||
strStamped += strprintf(".%06dZ", nTimeMicros%1000000);
|
||||
}
|
||||
|
@ -202,63 +191,61 @@ static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fSt
|
|||
strStamped = str;
|
||||
|
||||
if (!str.empty() && str[str.size()-1] == '\n')
|
||||
*fStartedNewLine = true;
|
||||
m_started_new_line = true;
|
||||
else
|
||||
*fStartedNewLine = false;
|
||||
m_started_new_line = false;
|
||||
|
||||
return strStamped;
|
||||
}
|
||||
|
||||
int LogPrintStr(const std::string &str)
|
||||
int BCLog::Logger::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);
|
||||
std::string strTimestamped = LogTimestampStr(str);
|
||||
|
||||
if (fPrintToConsole) {
|
||||
if (m_print_to_console) {
|
||||
// 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);
|
||||
if (m_print_to_file) {
|
||||
std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
|
||||
|
||||
// buffer if we haven't opened the log yet
|
||||
if (fileout == nullptr) {
|
||||
assert(vMsgsBeforeOpenLog);
|
||||
if (m_fileout == nullptr) {
|
||||
ret = strTimestamped.length();
|
||||
vMsgsBeforeOpenLog->push_back(strTimestamped);
|
||||
m_msgs_before_open.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
|
||||
if (m_reopen_file) {
|
||||
m_reopen_file = false;
|
||||
if (fsbridge::freopen(m_file_path,"a",m_fileout) != nullptr)
|
||||
setbuf(m_fileout, nullptr); // unbuffered
|
||||
}
|
||||
|
||||
ret = FileWriteStr(strTimestamped, fileout);
|
||||
ret = FileWriteStr(strTimestamped, m_fileout);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ShrinkDebugFile()
|
||||
void BCLog::Logger::ShrinkDebugFile()
|
||||
{
|
||||
// Amount of debug.log to save at end when shrinking (must fit in memory)
|
||||
constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
|
||||
|
||||
assert(!m_file_path.empty());
|
||||
|
||||
// Scroll debug.log if it's getting too big
|
||||
fs::path pathLog = GetDebugLogPath();
|
||||
FILE* file = fsbridge::fopen(pathLog, "r");
|
||||
FILE* file = fsbridge::fopen(m_file_path, "r");
|
||||
|
||||
// Special files (e.g. device nodes) may not have a size.
|
||||
size_t log_size = 0;
|
||||
try {
|
||||
log_size = fs::file_size(pathLog);
|
||||
log_size = fs::file_size(m_file_path);
|
||||
} catch (boost::filesystem::filesystem_error &) {}
|
||||
|
||||
// If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
|
||||
|
@ -271,7 +258,7 @@ void ShrinkDebugFile()
|
|||
int nBytes = fread(vch.data(), 1, vch.size(), file);
|
||||
fclose(file);
|
||||
|
||||
file = fsbridge::fopen(pathLog, "w");
|
||||
file = fsbridge::fopen(m_file_path, "w");
|
||||
if (file)
|
||||
{
|
||||
fwrite(vch.data(), 1, nBytes, file);
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -19,15 +21,7 @@ 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
|
||||
{
|
||||
|
@ -61,11 +55,65 @@ namespace BCLog {
|
|||
LEVELDB = (1 << 20),
|
||||
ALL = ~(uint32_t)0,
|
||||
};
|
||||
}
|
||||
|
||||
class Logger
|
||||
{
|
||||
private:
|
||||
FILE* m_fileout = nullptr;
|
||||
std::mutex m_file_mutex;
|
||||
std::list<std::string> m_msgs_before_open;
|
||||
|
||||
/**
|
||||
* m_started_new_line is a state variable that will suppress printing of
|
||||
* the timestamp when multiple calls are made that don't end in a
|
||||
* newline.
|
||||
*/
|
||||
std::atomic_bool m_started_new_line{true};
|
||||
|
||||
/** Log categories bitfield. */
|
||||
std::atomic<uint32_t> m_categories{0};
|
||||
|
||||
std::string LogTimestampStr(const std::string& str);
|
||||
|
||||
public:
|
||||
bool m_print_to_console = false;
|
||||
bool m_print_to_file = false;
|
||||
|
||||
bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS;
|
||||
bool m_log_time_micros = DEFAULT_LOGTIMEMICROS;
|
||||
|
||||
fs::path m_file_path;
|
||||
std::atomic<bool> m_reopen_file{false};
|
||||
|
||||
/** Send a string to the log output */
|
||||
int LogPrintStr(const std::string &str);
|
||||
|
||||
/** Returns whether logs will be written to any output */
|
||||
bool Enabled() const { return m_print_to_console || m_print_to_file; }
|
||||
|
||||
bool OpenDebugLog();
|
||||
void ShrinkDebugFile();
|
||||
|
||||
uint32_t GetCategoryMask() const { return m_categories.load(); }
|
||||
|
||||
void EnableCategory(LogFlags flag);
|
||||
bool EnableCategory(const std::string& str);
|
||||
void DisableCategory(LogFlags flag);
|
||||
bool DisableCategory(const std::string& str);
|
||||
|
||||
bool WillLogCategory(LogFlags category) const;
|
||||
|
||||
bool DefaultShrinkDebugFile() const;
|
||||
};
|
||||
|
||||
} // namespace BCLog
|
||||
|
||||
extern BCLog::Logger* const g_logger;
|
||||
|
||||
/** Return true if log accepts specified category */
|
||||
static inline bool LogAcceptCategory(uint32_t category)
|
||||
static inline bool LogAcceptCategory(BCLog::LogFlags category)
|
||||
{
|
||||
return (logCategories.load(std::memory_order_relaxed) & category) != 0;
|
||||
return g_logger->WillLogCategory(category);
|
||||
}
|
||||
|
||||
/** Returns a string with the log categories. */
|
||||
|
@ -74,11 +122,8 @@ 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);
|
||||
/** Return true if str parses as a log category and set the flag */
|
||||
bool GetLogCategory(BCLog::LogFlags& flag, 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; }
|
||||
|
@ -99,7 +144,7 @@ template<typename T, typename... Args> static inline void MarkUsed(const T& t, c
|
|||
#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0)
|
||||
#else
|
||||
#define LogPrintf(...) do { \
|
||||
if (fPrintToConsole || fPrintToDebugLog) { \
|
||||
if (g_logger->Enabled()) { \
|
||||
std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \
|
||||
try { \
|
||||
_log_msg_ = tfm::format(__VA_ARGS__); \
|
||||
|
@ -107,7 +152,7 @@ template<typename T, typename... Args> static inline void MarkUsed(const T& t, c
|
|||
/* 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_); \
|
||||
g_logger->LogPrintStr(_log_msg_); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -118,8 +163,4 @@ template<typename T, typename... Args> static inline void MarkUsed(const T& t, c
|
|||
} while(0)
|
||||
#endif
|
||||
|
||||
fs::path GetDebugLogPath();
|
||||
bool OpenDebugLog();
|
||||
void ShrinkDebugFile();
|
||||
|
||||
#endif // BITCOIN_LOGGING_H
|
||||
|
|
|
@ -346,21 +346,22 @@ UniValue getmemoryinfo(const JSONRPCRequest& request)
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t getCategoryMask(UniValue cats) {
|
||||
void EnableOrDisableLogCategories(UniValue cats, bool enable) {
|
||||
cats = cats.get_array();
|
||||
uint32_t mask = 0;
|
||||
for (unsigned int i = 0; i < cats.size(); ++i) {
|
||||
uint32_t flag = 0;
|
||||
std::string cat = cats[i].get_str();
|
||||
if (!GetLogCategory(&flag, &cat)) {
|
||||
|
||||
bool success;
|
||||
if (enable) {
|
||||
success = g_logger->EnableCategory(cat);
|
||||
} else {
|
||||
success = g_logger->DisableCategory(cat);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
|
||||
}
|
||||
if (flag == BCLog::NONE) {
|
||||
return 0;
|
||||
}
|
||||
mask |= flag;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
UniValue logging(const JSONRPCRequest& request)
|
||||
|
@ -399,25 +400,25 @@ UniValue logging(const JSONRPCRequest& request)
|
|||
);
|
||||
}
|
||||
|
||||
uint32_t originalLogCategories = logCategories;
|
||||
uint32_t original_log_categories = g_logger->GetCategoryMask();
|
||||
if (request.params[0].isArray()) {
|
||||
logCategories |= getCategoryMask(request.params[0]);
|
||||
EnableOrDisableLogCategories(request.params[0], true);
|
||||
}
|
||||
|
||||
if (request.params[1].isArray()) {
|
||||
logCategories &= ~getCategoryMask(request.params[1]);
|
||||
EnableOrDisableLogCategories(request.params[1], false);
|
||||
}
|
||||
uint32_t updated_log_categories = g_logger->GetCategoryMask();
|
||||
uint32_t changed_log_categories = original_log_categories ^ updated_log_categories;
|
||||
|
||||
// Update libevent logging if BCLog::LIBEVENT has changed.
|
||||
// If the library version doesn't allow it, UpdateHTTPServerLogging() returns false,
|
||||
// in which case we should clear the BCLog::LIBEVENT flag.
|
||||
// Throw an error if the user has explicitly asked to change only the libevent
|
||||
// flag and it failed.
|
||||
uint32_t changedLogCategories = originalLogCategories ^ logCategories;
|
||||
if (changedLogCategories & BCLog::LIBEVENT) {
|
||||
if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
|
||||
logCategories &= ~BCLog::LIBEVENT;
|
||||
if (changedLogCategories == BCLog::LIBEVENT) {
|
||||
if (changed_log_categories & BCLog::LIBEVENT) {
|
||||
if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) {
|
||||
g_logger->DisableCategory(BCLog::LIBEVENT);
|
||||
if (changed_log_categories == BCLog::LIBEVENT) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
|
|||
SetupNetworking();
|
||||
InitSignatureCache();
|
||||
InitScriptExecutionCache();
|
||||
fPrintToDebugLog = false; // don't want to write to debug.log file
|
||||
fCheckBlockIndex = true;
|
||||
SelectParams(chainName);
|
||||
noui_connect();
|
||||
|
|
|
@ -66,7 +66,7 @@ bool SetupNetworking();
|
|||
template<typename... Args>
|
||||
bool error(const char* fmt, const Args&... args)
|
||||
{
|
||||
LogPrintStr("ERROR: " + tfm::format(fmt, args...) + "\n");
|
||||
LogPrintf("ERROR: %s\n", tfm::format(fmt, args...));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue