diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index dcdfef8a1..10f3b2266 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -80,7 +80,6 @@ $(package)_config_opts += -no-feature-concurrent
$(package)_config_opts += -no-feature-sql
$(package)_config_opts += -no-feature-statemachine
$(package)_config_opts += -no-feature-syntaxhighlighter
-$(package)_config_opts += -no-feature-textbrowser
$(package)_config_opts += -no-feature-textodfwriter
$(package)_config_opts += -no-feature-udpsocket
$(package)_config_opts += -no-feature-xml
diff --git a/src/init.cpp b/src/init.cpp
index 72bfa6fe7..d341088df 100755
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -324,7 +324,7 @@ void HandleSIGTERM(int)
void HandleSIGHUP(int)
{
- fReopenDebugLog = true;
+ fReopenLogFiles = true;
}
bool static Bind(const CService &addr, unsigned int flags) {
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index d80c6a639..663687e33 100755
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -1582,6 +1582,42 @@
+
+
+ &Error Log
+
+
+
+ 3
+
+
+ 5
+
+ -
+
+
+
+ 100
+ 100
+
+
+
+ true
+
+
+
+ -
+
+
+ Copy Error Log contents to Clipboard
+
+
+ Copy to Clipboard
+
+
+
+
+
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h
index 52561bc35..d437701b1 100755
--- a/src/qt/guiconstants.h
+++ b/src/qt/guiconstants.h
@@ -8,6 +8,12 @@
/* Milliseconds between model updates */
static const int MODEL_UPDATE_DELAY = 250;
+/* Milliseconds between error log refreshes */
+static const int ERROR_LOG_UPDATE_DELAY = 2500;
+
+/* Initial number of debug logs to parse error log entries from */
+static const int ERROR_LOG_INITIAL_COUNT = 500;
+
/* AskPassphraseDialog -- Maximum passphrase length */
static const int MAX_PASSPHRASE_SIZE = 1024;
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 08baf8f84..e7e12e1a4 100755
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -12,6 +12,7 @@
#include "bantablemodel.h"
#include "clientmodel.h"
#include "guiutil.h"
+#include "guiconstants.h"
#include "platformstyle.h"
#include "bantablemodel.h"
@@ -28,16 +29,25 @@
#include
#endif
+#include
#include
#include
#include
#include
#include
+
+#include
#include
+#include
+#include
#include
#include
#include
+#if QT_VERSION < 0x050000
+#include
+#endif
+
// TODO: add a scrollback limit, as there is currently none
// TODO: make it possible to filter out categories (esp debug messages when implemented)
// TODO: receive errors and debug messages through ClientModel
@@ -290,6 +300,11 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) :
ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
+ // set up timer for auto refresh for error log
+ errorLogTimer = new QTimer();
+ connect(errorLogTimer, SIGNAL(timeout()), SLOT(errorLogRefresh()));
+ errorLogTimer->setInterval(ERROR_LOG_UPDATE_DELAY);
+
QSettings settings;
consoleFontSize = settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()).toInt();
clear();
@@ -300,10 +315,115 @@ RPCConsole::~RPCConsole()
GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this);
Q_EMIT stopExecutor();
RPCUnsetTimerInterface(rpcTimerInterface);
+ errorLogFile->close();
delete rpcTimerInterface;
delete ui;
}
+void RPCConsole::errorLogInitPos()
+{
+ // Check if we already have the file
+ if (errorLogFile != NULL) {
+ // Get a QFile instance
+ errorLogFile = new QFile(QString::fromStdString(GetErrorLogPath().string()));
+ }
+
+ // Try to open file
+ if (!errorLogFile->open(QFile::ReadOnly | QFile::Text))
+ return;
+
+ // Seek to the end of file
+ errorLogFile->seek(errorLogFile->size() - 1);
+
+ // We need to move the file pos back by ERROR_LOG_INITIAL_COUNT lines
+ QString ch;
+ int lineCount = 0;
+ while ((lineCount < ERROR_LOG_INITIAL_COUNT) && (errorLogFile->pos() > 0))
+ {
+ // Load the current character
+ ch = errorLogFile->read(1);
+
+ // Move pos back by 2 spaces
+ errorLogFile->seek(errorLogFile->pos() - 2);
+
+ // Check if we have a newline
+ if (ch == "\n")
+ lineCount++; // Count it
+ }
+
+ // Move pos forward by 2 spaces
+ errorLogFile->seek(errorLogFile->pos() + 2);
+
+ // Clear the textarea
+ ui->errorLogTextBrowser->setText("");
+
+ // Mark init as done
+ errorLogInitPosDone = true;
+}
+
+void RPCConsole::errorLogRefresh()
+{
+ // Check if we are still refreshing
+ if (errorLogRefreshing)
+ return;
+
+ // Set to refreshing
+ errorLogRefreshing = true;
+
+ // Check if we have initialized debug log already
+ if (!errorLogInitPosDone)
+ errorLogInitPos();
+
+ // Load the stream
+ QTextStream in(errorLogFile);
+
+ // Load up the lines
+ QString logLines = "";
+ while (!in.atEnd()) {
+ // Load the next line
+ logLines += in.readLine() + "\n";
+ }
+
+ // Check if we have lines
+ if (logLines != "") {
+ // Add the new lines, purpose of duplicate moveCursor calls is
+ // to auto scroll to the end of the log instead of sticking to the
+ // top of the text area
+ ui->errorLogTextBrowser->moveCursor(QTextCursor::End);
+ ui->errorLogTextBrowser->textCursor().insertText(logLines);
+ ui->errorLogTextBrowser->moveCursor(QTextCursor::End);
+ }
+
+ // Count the lines in the UI textarea
+ int uiLineCount = ui->errorLogTextBrowser->document()->lineCount();
+
+ // Check if lines are more than ERROR_LOG_INITIAL_COUNT
+ if (uiLineCount > ERROR_LOG_INITIAL_COUNT) {
+ // Count how many to remove
+ int lineCountDiff = uiLineCount - ERROR_LOG_INITIAL_COUNT;
+
+ // Get our cursor
+ QTextCursor cursor = ui->errorLogTextBrowser->textCursor();
+
+ // REMOVE THEM
+ for (int i = 0; i < lineCountDiff; i++) {
+ cursor.movePosition(QTextCursor::Start);
+ cursor.select(QTextCursor::LineUnderCursor);
+ cursor.deleteChar(); // Remove the selected text
+ cursor.deleteChar(); // This is by design, this removes the \n
+ }
+
+ // Replace the cursor
+ ui->errorLogTextBrowser->setTextCursor(cursor);
+
+ // Move cursor back to the end
+ ui->errorLogTextBrowser->moveCursor(QTextCursor::End);
+ }
+
+ // Mark as done
+ errorLogRefreshing = false;
+}
+
bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
{
if(event->type() == QEvent::KeyPress) // Special key handling
@@ -672,6 +792,11 @@ void RPCConsole::on_tabWidget_currentChanged(int index)
clearSelectedNode();
}
+void RPCConsole::on_errorLogCopyClipboardButton_clicked()
+{
+ GUIUtil::setClipboard(ui->errorLogTextBrowser->toPlainText());
+}
+
void RPCConsole::on_openDebugLogfileButton_clicked()
{
GUIUtil::openDebugLogfile();
@@ -837,6 +962,15 @@ void RPCConsole::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
+ // Mark init as not done
+ errorLogInitPosDone = false;
+
+ // Load error log initial data
+ errorLogRefresh();
+
+ // Start the error log timer
+ errorLogTimer->start();
+
if (!clientModel || !clientModel->getPeerTableModel())
return;
@@ -848,6 +982,9 @@ void RPCConsole::hideEvent(QHideEvent *event)
{
QWidget::hideEvent(event);
+ // Stop the error log timer
+ errorLogTimer->stop();
+
if (!clientModel || !clientModel->getPeerTableModel())
return;
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index f0bca10eb..133bf1fae 100755
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -10,8 +10,11 @@
#include "net.h"
-#include
#include
+#include
+#include
+#include
+#include
class ClientModel;
class PlatformStyle;
@@ -61,6 +64,8 @@ private Q_SLOTS:
void on_tabWidget_currentChanged(int index);
/** open the debug.log from the current datadir */
void on_openDebugLogfileButton_clicked();
+ /** copy the error log text from errorLogTextBrowser */
+ void on_errorLogCopyClipboardButton_clicked();
/** change the time range of the network traffic graph */
void on_sldGraphRange_valueChanged(int value);
/** update traffic statistics */
@@ -106,6 +111,10 @@ public Q_SLOTS:
void unbanSelectedNode();
/** set which tab has the focus (is visible) */
void setTabFocus(enum TabTypes tabType);
+ /** load the error log initial file pos */
+ void errorLogInitPos();
+ /** update the error log */
+ void errorLogRefresh();
Q_SIGNALS:
// For RPC command executor
@@ -140,6 +149,10 @@ public Q_SLOTS:
QMenu *banTableContextMenu;
int consoleFontSize;
QCompleter *autoCompleter;
+ QTimer *errorLogTimer;
+ QFile *errorLogFile;
+ bool errorLogInitPosDone = false;
+ bool errorLogRefreshing = false;
};
#endif // NAVCOIN_QT_RPCCONSOLE_H
diff --git a/src/util.cpp b/src/util.cpp
index e435e4999..fc21a9775 100755
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -122,7 +122,7 @@ string strMiscWarning;
bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS;
bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS;
bool fLogIPs = DEFAULT_LOGIPS;
-std::atomic fReopenDebugLog(false);
+std::atomic fReopenLogFiles(false);
CTranslationInterface translationInterface;
/** Init OpenSSL library multithreading support */
@@ -188,51 +188,91 @@ instance_of_cinit;
*/
static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT;
+static boost::once_flag errorPrintInitFlag = BOOST_ONCE_INIT;
/**
* We use boost::call_once() to make sure mutexDebugLog and
- * vMsgsBeforeOpenLog are initialized in a thread-safe manner.
+ * vMsgsBeforeOpenDebugLog 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
+ * NOTE: fileoutDebugLog, fileoutErrorLog, mutexDebugLog and sometimes
+ * vMsgsBeforeOpenDebugLog 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 = NULL;
+static FILE* fileoutDebugLog = NULL;
+static FILE* fileoutErrorLog = NULL;
static boost::mutex* mutexDebugLog = NULL;
-static list *vMsgsBeforeOpenLog;
+static boost::mutex* mutexErrorLog = NULL;
+static list *vMsgsBeforeOpenDebugLog;
+static list *vMsgsBeforeOpenErrorLog;
static int FileWriteStr(const std::string &str, FILE *fp)
{
return fwrite(str.data(), 1, str.size(), fp);
}
+static int DebugLogWriteStr(const std::string &str)
+{
+ // Return the int from the write size
+ return FileWriteStr(str, fileoutDebugLog); // write to debug log
+}
+
+static int ErrorLogWriteStr(const std::string &str)
+{
+ // Return the int from the write size
+ return FileWriteStr(str, fileoutErrorLog); // write to error log
+}
+
static void DebugPrintInit()
{
assert(mutexDebugLog == NULL);
mutexDebugLog = new boost::mutex();
- vMsgsBeforeOpenLog = new list;
+ vMsgsBeforeOpenDebugLog = new list;
+}
+
+static void ErrorPrintInit()
+{
+ assert(mutexErrorLog == NULL);
+ mutexErrorLog = new boost::mutex();
+ vMsgsBeforeOpenErrorLog = new list;
}
void OpenDebugLog()
{
boost::call_once(&DebugPrintInit, debugPrintInitFlag);
- boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);
+ boost::call_once(&ErrorPrintInit, errorPrintInitFlag);
+ boost::mutex::scoped_lock scoped_lock_debug(*mutexDebugLog);
+ boost::mutex::scoped_lock scoped_lock_error(*mutexErrorLog);
+
+ assert(fileoutDebugLog == NULL);
+ assert(fileoutErrorLog == NULL);
+ assert(vMsgsBeforeOpenDebugLog);
+ assert(vMsgsBeforeOpenErrorLog);
- assert(fileout == NULL);
- assert(vMsgsBeforeOpenLog);
- boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
- fileout = fopen(pathDebug.string().c_str(), "a");
- if (fileout) setbuf(fileout, NULL); // unbuffered
+ // Open the debug log
+ fileoutDebugLog = fopen(GetDebugLogPath().string().c_str(), "a");
+ if (fileoutDebugLog) setbuf(fileoutDebugLog, NULL); // unbuffered
+
+ // Open the error log
+ fileoutErrorLog = fopen(GetErrorLogPath().string().c_str(), "a");
+ if (fileoutErrorLog) setbuf(fileoutErrorLog, NULL); // unbuffered
// dump buffered messages from before we opened the log
- while (!vMsgsBeforeOpenLog->empty()) {
- FileWriteStr(vMsgsBeforeOpenLog->front(), fileout);
- vMsgsBeforeOpenLog->pop_front();
+ while (!vMsgsBeforeOpenDebugLog->empty()) {
+ DebugLogWriteStr(vMsgsBeforeOpenDebugLog->front()); // write to log
+ vMsgsBeforeOpenDebugLog->pop_front();
}
- delete vMsgsBeforeOpenLog;
- vMsgsBeforeOpenLog = NULL;
+ // dump buffered messages from before we opened the log
+ while (!vMsgsBeforeOpenErrorLog->empty()) {
+ ErrorLogWriteStr(vMsgsBeforeOpenErrorLog->front()); // write to log
+ vMsgsBeforeOpenErrorLog->pop_front();
+ }
+
+ delete vMsgsBeforeOpenDebugLog;
+ delete vMsgsBeforeOpenErrorLog;
+ vMsgsBeforeOpenDebugLog = NULL;
+ vMsgsBeforeOpenErrorLog = NULL;
}
bool LogAcceptCategory(const char* category)
@@ -261,6 +301,7 @@ bool LogAcceptCategory(const char* category)
setCategories.count(string(category)) == 0)
return false;
}
+
return true;
}
@@ -293,7 +334,17 @@ static std::string LogTimestampStr(const std::string &str, bool *fStartedNewLine
return strStamped;
}
-int LogPrintStr(const std::string &str)
+boost::filesystem::path GetDebugLogPath()
+{
+ return GetDataDir() / "debug.log";
+}
+
+boost::filesystem::path GetErrorLogPath()
+{
+ return GetDataDir() / "error.log";
+}
+
+int DebugLogPrintStr(const std::string &str)
{
int ret = 0; // Returns total number of characters written
static bool fStartedNewLine = true;
@@ -309,27 +360,69 @@ int LogPrintStr(const std::string &str)
else if (fPrintToDebugLog)
{
boost::call_once(&DebugPrintInit, debugPrintInitFlag);
- boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);
+ boost::mutex::scoped_lock scoped_lock_debug(*mutexDebugLog);
// buffer if we haven't opened the log yet
- if (fileout == NULL) {
- assert(vMsgsBeforeOpenLog);
+ if (fileoutDebugLog == NULL) {
+ assert(vMsgsBeforeOpenDebugLog);
ret = strTimestamped.length();
- vMsgsBeforeOpenLog->push_back(strTimestamped);
+ vMsgsBeforeOpenDebugLog->push_back(strTimestamped);
}
else
{
// reopen the log file, if requested
- if (fReopenDebugLog) {
- fReopenDebugLog = false;
- boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
- if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL)
- setbuf(fileout, NULL); // unbuffered
+ if (fReopenLogFiles) {
+ fReopenLogFiles = false;
+
+ // Open the log files
+ OpenDebugLog();
}
- ret = FileWriteStr(strTimestamped, fileout);
+ ret = DebugLogWriteStr(strTimestamped);
}
}
+
+ return ret;
+}
+
+int ErrorLogPrintStr(const std::string &str)
+{
+ int ret = 0; // Returns total number of characters written
+ static bool fStartedNewLine = true;
+
+ string strTimestamped = LogTimestampStr(str, &fStartedNewLine);
+
+ if (fPrintToConsole)
+ {
+ // print to console
+ ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
+ fflush(stdout);
+ }
+ else if (fPrintToDebugLog)
+ {
+ boost::call_once(&ErrorPrintInit, errorPrintInitFlag);
+ boost::mutex::scoped_lock scoped_lock_error(*mutexErrorLog);
+
+ // buffer if we haven't opened the log yet
+ if (fileoutErrorLog == NULL) {
+ assert(vMsgsBeforeOpenErrorLog);
+ ret = strTimestamped.length();
+ vMsgsBeforeOpenErrorLog->push_back(strTimestamped);
+ }
+ else
+ {
+ // reopen the log file, if requested
+ if (fReopenLogFiles) {
+ fReopenLogFiles = false;
+
+ // Open the log files
+ OpenDebugLog();
+ }
+
+ ret = ErrorLogWriteStr(strTimestamped);
+ }
+ }
+
return ret;
}
@@ -742,19 +835,19 @@ bool TryCreateDirectory(const boost::filesystem::path& p)
return false;
}
-void FileCommit(FILE *fileout)
+void FileCommit(FILE *file)
{
- fflush(fileout); // harmless if redundantly called
+ fflush(file); // harmless if redundantly called
#ifdef WIN32
- HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(fileout));
+ HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
FlushFileBuffers(hFile);
#else
#if defined(__linux__) || defined(__NetBSD__)
- fdatasync(fileno(fileout));
+ fdatasync(fileno(file));
#elif defined(__APPLE__) && defined(F_FULLFSYNC)
- fcntl(fileno(fileout), F_FULLFSYNC, 0);
+ fcntl(fileno(file), F_FULLFSYNC, 0);
#else
- fsync(fileno(fileout));
+ fsync(fileno(file));
#endif
#endif
}
@@ -837,11 +930,16 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
}
void ShrinkDebugFile()
+{
+ ShrinkDebugFile(GetDebugLogPath(), 10); // Shrink the debug log
+ ShrinkDebugFile(GetErrorLogPath(), 2); // Shrink the error log
+}
+
+void ShrinkDebugFile(boost::filesystem::path pathLog, int maxSize)
{
// Scroll debug.log if it's getting too big
- boost::filesystem::path pathLog = GetDataDir() / "debug.log";
FILE* file = fopen(pathLog.string().c_str(), "r");
- if (file && boost::filesystem::file_size(pathLog) > 10 * 1000000)
+ if (file && boost::filesystem::file_size(pathLog) > maxSize * 1000000)
{
// Restart the file with some of the end
std::vector vch(200000,0);
diff --git a/src/util.h b/src/util.h
index 575c7ad5f..0a6473538 100755
--- a/src/util.h
+++ b/src/util.h
@@ -51,7 +51,7 @@ extern std::string strMiscWarning;
extern bool fLogTimestamps;
extern bool fLogTimeMicros;
extern bool fLogIPs;
-extern std::atomic fReopenDebugLog;
+extern std::atomic fReopenLogFiles;
extern CTranslationInterface translationInterface;
extern const char * const NAVCOIN_CONF_FILENAME;
@@ -72,8 +72,18 @@ bool SetupNetworking();
/** Return true if log accepts specified category */
bool LogAcceptCategory(const char* category);
-/** Send a string to the log output */
-int LogPrintStr(const std::string &str);
+
+/** Returns the path to the debug.log file */
+boost::filesystem::path GetDebugLogPath();
+
+/** Returns the path to the error.log file */
+boost::filesystem::path GetErrorLogPath();
+
+/** Send a string to the debug log output */
+int DebugLogPrintStr(const std::string &str);
+
+/** Send a string to the error log output */
+int ErrorLogPrintStr(const std::string &str);
#define LogPrintf(...) LogPrint(NULL, __VA_ARGS__)
@@ -87,14 +97,14 @@ static inline int LogPrint(const char* category, const char* fmt, const T1& v1,
} catch (std::runtime_error &e) {
_log_msg_ = "Error \"" + std::string(e.what()) + "\" while formatting log message: " + fmt;
}
- return LogPrintStr(_log_msg_);
+ return DebugLogPrintStr(_log_msg_);
}
template
bool error(const char* fmt, const T1& v1, const Args&... args)
{
- LogPrintStr("ERROR: " + tfm::format(fmt, v1, args...) + "\n");
+ ErrorLogPrintStr("ERROR: " + tfm::format(fmt, v1, args...) + "\n");
return false;
}
@@ -106,11 +116,11 @@ bool error(const char* fmt, const T1& v1, const Args&... args)
static inline int LogPrint(const char* category, const char* s)
{
if(!LogAcceptCategory(category)) return 0;
- return LogPrintStr(s);
+ return DebugLogPrintStr(s);
}
static inline bool error(const char* s)
{
- LogPrintStr(std::string("ERROR: ") + s + "\n");
+ ErrorLogPrintStr(std::string("ERROR: ") + s + "\n");
return false;
}
static inline bool error(std::string s)
@@ -119,7 +129,7 @@ static inline bool error(std::string s)
}
void PrintExceptionContinue(const std::exception *pex, const char* pszThread);
void ParseParameters(int argc, const char*const argv[]);
-void FileCommit(FILE *fileout);
+void FileCommit(FILE *file);
bool TruncateFile(FILE *file, unsigned int length);
int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
@@ -143,6 +153,7 @@ boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
void OpenDebugLog();
void ShrinkDebugFile();
+void ShrinkDebugFile(boost::filesystem::path pathLog, int maxSize);
void runCommand(const std::string& strCommand);
inline bool IsSwitchChar(char c)