diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index d077644d..ee56a1c7 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -60,8 +60,12 @@ namespace i2p return service; } - bool Daemon_Singleton::init(int argc, char* argv[]) - { + bool Daemon_Singleton::init(int argc, char* argv[]) { + return init(argc, argv, nullptr); + } + + bool Daemon_Singleton::init(int argc, char* argv[], std::shared_ptr logstream) + { i2p::config::Init(); i2p::config::ParseCmdline(argc, argv); @@ -104,7 +108,10 @@ namespace i2p logs = "file"; i2p::log::Logger().SetLogLevel(loglevel); - if (logs == "file") { + if (logstream) { + LogPrint(eLogInfo, "Log: will send messages to std::ostream"); + i2p::log::Logger().SendTo (logstream); + } else if (logs == "file") { if (logfile == "") logfile = i2p::fs::DataDirPath("i2pd.log"); LogPrint(eLogInfo, "Log: will send messages to ", logfile); diff --git a/daemon/Daemon.h b/daemon/Daemon.h index 00baf7b9..1745b980 100644 --- a/daemon/Daemon.h +++ b/daemon/Daemon.h @@ -3,6 +3,7 @@ #include #include +#include namespace i2p { @@ -12,8 +13,9 @@ namespace util class Daemon_Singleton { public: - virtual bool init(int argc, char* argv[]); - virtual bool start(); + virtual bool init(int argc, char* argv[], std::shared_ptr logstream); + virtual bool init(int argc, char* argv[]); + virtual bool start(); virtual bool stop(); virtual void run () {}; diff --git a/libi2pd/Log.cpp b/libi2pd/Log.cpp index b664a5d9..79b4a511 100644 --- a/libi2pd/Log.cpp +++ b/libi2pd/Log.cpp @@ -8,6 +8,9 @@ #include "Log.h" +//for std::transform +#include + namespace i2p { namespace log { static Log logger; @@ -107,7 +110,18 @@ namespace log { } } - void Log::SetLogLevel (const std::string& level) { + std::string str_tolower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), + // static_cast(std::tolower) // wrong + // [](int c){ return std::tolower(c); } // wrong + // [](char c){ return std::tolower(c); } // wrong + [](unsigned char c){ return std::tolower(c); } // correct + ); + return s; + } + + void Log::SetLogLevel (const std::string& level_) { + std::string level=str_tolower(level_); if (level == "none") { m_MinLevel = eLogNone; } else if (level == "error") { m_MinLevel = eLogError; } else if (level == "warn") { m_MinLevel = eLogWarning; } diff --git a/qt/i2pd_qt/DaemonQT.cpp b/qt/i2pd_qt/DaemonQT.cpp index dd7c892d..f5e6d62b 100644 --- a/qt/i2pd_qt/DaemonQT.cpp +++ b/qt/i2pd_qt/DaemonQT.cpp @@ -1,6 +1,11 @@ +#include + #include "DaemonQT.h" #include "Daemon.h" #include "mainwindow.h" + +#include "Log.h" + #include #include #include @@ -90,12 +95,12 @@ namespace qt delete mutex; } - bool DaemonQTImpl::init(int argc, char* argv[]) + bool DaemonQTImpl::init(int argc, char* argv[], std::shared_ptr logstream) { mutex=new QMutex(QMutex::Recursive); setRunningCallback(0); m_IsRunning=false; - return Daemon.init(argc,argv); + return Daemon.init(argc,argv,logstream); } void DaemonQTImpl::start() @@ -146,33 +151,35 @@ namespace qt int result; { + std::shared_ptr logstreamptr=std::make_shared(); + //TODO move daemon init deinit to a bg thread DaemonQTImpl daemon; - qDebug("Initialising the daemon..."); - bool daemonInitSuccess = daemon.init(argc, argv); + (*logstreamptr) << "Initialising the daemon..." << std::endl; + bool daemonInitSuccess = daemon.init(argc, argv, logstreamptr); if(!daemonInitSuccess) { QMessageBox::critical(0, "Error", "Daemon init failed"); return 1; } - qDebug("Initialised, creating the main window..."); - MainWindow w; - qDebug("Before main window.show()..."); + LogPrint(eLogDebug, "Initialised, creating the main window..."); + MainWindow w(logstreamptr); + LogPrint(eLogDebug, "Before main window.show()..."); w.show (); { i2p::qt::Controller daemonQtController(daemon); w.setI2PController(&daemonQtController); - qDebug("Starting the daemon..."); + LogPrint(eLogDebug, "Starting the daemon..."); emit daemonQtController.startDaemon(); //daemon.start (); - qDebug("Starting GUI event loop..."); + LogPrint(eLogDebug, "Starting GUI event loop..."); result = app.exec(); //daemon.stop (); } } //QMessageBox::information(&w, "Debug", "demon stopped"); - qDebug("Exiting the application"); + LogPrint(eLogDebug, "Exiting the application"); return result; } } diff --git a/qt/i2pd_qt/DaemonQT.h b/qt/i2pd_qt/DaemonQT.h index d0add0e3..780d2f73 100644 --- a/qt/i2pd_qt/DaemonQT.h +++ b/qt/i2pd_qt/DaemonQT.h @@ -25,7 +25,7 @@ namespace qt * @param argv * @return success */ - bool init(int argc, char* argv[]); + bool init(int argc, char* argv[], std::shared_ptr logstream); void start(); void stop(); void restart(); diff --git a/qt/i2pd_qt/SignatureTypeComboboxFactory.h b/qt/i2pd_qt/SignatureTypeComboboxFactory.h index 41245dac..f7cac658 100644 --- a/qt/i2pd_qt/SignatureTypeComboboxFactory.h +++ b/qt/i2pd_qt/SignatureTypeComboboxFactory.h @@ -18,7 +18,7 @@ class SignatureTypeComboBoxFactory } public: - static const uint16_t getSigType(const QVariant& var) { + static uint16_t getSigType(const QVariant& var) { return (uint16_t)var.toInt(); } diff --git a/qt/i2pd_qt/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro index 12d13a17..6c0464ab 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -4,7 +4,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = i2pd_qt TEMPLATE = app -QMAKE_CXXFLAGS *= -std=c++11 +QMAKE_CXXFLAGS *= -std=c++11 -ggdb DEFINES += USE_UPNP # change to your own path, where you will store all needed libraries with 'git clone' commands below. @@ -93,7 +93,8 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \ textbrowsertweaked1.cpp \ pagewithbackbutton.cpp \ widgetlock.cpp \ - widgetlockregistry.cpp + widgetlockregistry.cpp \ + logviewermanager.cpp #qt creator does not handle this well #SOURCES += $$files(../../libi2pd/*.cpp) @@ -179,7 +180,8 @@ HEADERS += DaemonQT.h mainwindow.h \ widgetlock.h \ widgetlockregistry.h \ i2pd.rc \ - i2pd.rc + i2pd.rc \ + logviewermanager.h INCLUDEPATH += ../../libi2pd INCLUDEPATH += ../../libi2pd_client @@ -280,8 +282,11 @@ windows { DEFINES += BOOST_USE_WINDOWS_H WINDOWS _WINDOWS WIN32_LEAN_AND_MEAN MINIUPNP_STATICLIB DEFINES -= UNICODE _UNICODE BOOST_SUFFIX = -mt - QMAKE_CXXFLAGS = -Os - QMAKE_LFLAGS = -s -Wl,-Bstatic -static-libgcc -static-libstdc++ -mwindows + QMAKE_CXXFLAGS_RELEASE = -Os + QMAKE_LFLAGS = -Wl,-Bstatic -static-libgcc -static-libstdc++ -mwindows + + #linker's -s means "strip" + QMAKE_LFLAGS_RELEASE += -s LIBS = -lminiupnpc \ -lboost_system$$BOOST_SUFFIX \ diff --git a/qt/i2pd_qt/logviewermanager.cpp b/qt/i2pd_qt/logviewermanager.cpp new file mode 100644 index 00000000..30fc904a --- /dev/null +++ b/qt/i2pd_qt/logviewermanager.cpp @@ -0,0 +1,45 @@ +#include "logviewermanager.h" + +LogViewerManager::LogViewerManager(std::shared_ptr logStream_, + QPlainTextEdit* logTextEdit_, + QObject *parent) : + QObject(parent), + logStream(logStream_), + logTextEdit(logTextEdit_), + controllerForBgThread(nullptr) +{ + assert(logTextEdit!=nullptr); + controllerForBgThread=new i2pd::qt::logviewer::Controller(*this); +} + +namespace i2pd { +namespace qt { +namespace logviewer { + +QString Worker::pollAndShootATimerForInfiniteRetries() { + std::shared_ptr logStream=logViewerManager.getLogStream(); + assert(logStream!=nullptr); + std::streamsize MAX_SZ=64*1024; + char*buf=(char*)malloc(MAX_SZ*sizeof(char)); + if(buf==nullptr)return ""; + std::streamsize read=logStream->readsome(buf, MAX_SZ); + if(read<0)read=0; + QString ret=QString::fromUtf8(buf, read); + free(buf); + return ret; +} + +Controller::Controller(LogViewerManager ¶meter1):logViewerManager(parameter1) { + Worker *worker = new Worker(parameter1); + worker->moveToThread(&workerThread); + connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); + connect(this, &Controller::operate1, worker, &Worker::doWork1); + connect(worker, &Worker::resultReady, + ¶meter1, &LogViewerManager::appendPlainText_atGuiThread); + workerThread.start(); + timerId=startTimer(100/*millis*/); +} + +} +} +} diff --git a/qt/i2pd_qt/logviewermanager.h b/qt/i2pd_qt/logviewermanager.h new file mode 100644 index 00000000..e9ede79f --- /dev/null +++ b/qt/i2pd_qt/logviewermanager.h @@ -0,0 +1,130 @@ +#ifndef LOGVIEWERMANAGER_H +#define LOGVIEWERMANAGER_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "FS.h" +#include "Log.h" + +class LogViewerManager; + +namespace i2pd { +namespace qt { +namespace logviewer { + +class Worker : public QObject +{ + Q_OBJECT +private: + LogViewerManager &logViewerManager; +public: + Worker(LogViewerManager ¶meter1):logViewerManager(parameter1){} +private: + QString pollAndShootATimerForInfiniteRetries(); + +public slots: + void doWork1() { + /* ... here is the expensive or blocking operation ... */ + QString read=pollAndShootATimerForInfiniteRetries(); + emit resultReady(read); + } + +signals: + void resultReady(QString read); +}; + +class Controller : public QObject +{ + Q_OBJECT + QThread workerThread; + LogViewerManager& logViewerManager; + int timerId; +public: + Controller(LogViewerManager ¶meter1); + ~Controller() { + if(timerId!=0)killTimer(timerId); + workerThread.quit(); + workerThread.wait(); + } +signals: + void operate1(); +protected: + void timerEvent(QTimerEvent */*event*/) { + emit operate1(); + } +}; + +} +} +} + +class LogViewerManager : public QObject +{ + Q_OBJECT +private: + std::shared_ptr logStream; + QPlainTextEdit* logTextEdit; + i2pd::qt::logviewer::Controller* controllerForBgThread; +public: + //also starts a bg thread (QTimer) polling logStream->readsome(buf, n) + explicit LogViewerManager(std::shared_ptr logStream_, + QPlainTextEdit* logTextEdit_, + QObject *parent); + //also deallocs the bg thread (QTimer) + virtual ~LogViewerManager(){} + const i2pd::qt::logviewer::Controller& getControllerForBgThread() { + assert(controllerForBgThread!=nullptr); + return *controllerForBgThread; + } + const QPlainTextEdit* getLogTextEdit(){ return logTextEdit; } + const std::shared_ptr getLogStream(){ return logStream; } +signals: + +public slots: + //void appendFromNonGuiThread(std::string read) { + //} +public slots: + void appendPlainText_atGuiThread(QString plainText) { + if(plainText.length()==0)return; + assert(logTextEdit!=nullptr); + int scrollPosVert =logTextEdit->verticalScrollBar()->value(); + int scrollPosHoriz=logTextEdit->horizontalScrollBar()->value(); + int scrollPosVertMax =logTextEdit->verticalScrollBar()->maximum(); + const int MAX_LINES=10*1024; + logTextEdit->setMaximumBlockCount(MAX_LINES); + //logTextEdit->appendPlainText(plainText); + //navigate the window to the end + //QTextCursor cursor = logTextEdit->textCursor(); + //cursor.movePosition(QTextCursor::MoveOperation::End); + //logTextEdit->setTextCursor(cursor); + //QTextCursor prev_cursor = logTextEdit->textCursor(); + logTextEdit->moveCursor(QTextCursor::End); + logTextEdit->insertPlainText(plainText); + if(/*prev_cursor.atEnd()*/scrollPosVert==scrollPosVertMax){ + //logTextEdit->moveCursor(QTextCursor::End); + scrollPosVert =logTextEdit->verticalScrollBar()->maximum(); + scrollPosHoriz=logTextEdit->horizontalScrollBar()->minimum(); + } + //else + // logTextEdit->setTextCursor(prev_cursor); + logTextEdit->verticalScrollBar()->setValue(scrollPosVert); + logTextEdit->horizontalScrollBar()->setValue(scrollPosHoriz); + } + /* + void replaceText_atGuiThread() { + assert(logTextEdit!=nullptr); + logTextEdit->setText(QString::fromStdString(nav.getContent())); + } + */ +}; + +#endif // LOGVIEWERMANAGER_H diff --git a/qt/i2pd_qt/mainwindow.cpp b/qt/i2pd_qt/mainwindow.cpp index a095f78c..e4c9f2a7 100644 --- a/qt/i2pd_qt/mainwindow.cpp +++ b/qt/i2pd_qt/mainwindow.cpp @@ -27,15 +27,19 @@ #include "DaemonQT.h" #include "SignatureTypeComboboxFactory.h" +#include "logviewermanager.h" + std::string programOptionsWriterCurrentSection; -MainWindow::MainWindow(QWidget *parent) : +MainWindow::MainWindow(std::shared_ptr logStream_, QWidget *parent) : QMainWindow(parent) + ,logStream(logStream_) #ifndef ANDROID ,quitting(false) #endif ,wasSelectingAtStatusMainPage(false) ,showHiddenInfoStatusMainPage(false) + ,logViewerManagerPtr(nullptr) ,ui(new Ui::MainWindow) ,statusButtonsUI(new Ui::StatusButtonsForm) ,routerCommandsUI(new Ui::routerCommandsWidget) @@ -132,6 +136,8 @@ MainWindow::MainWindow(QWidget *parent) : QObject::connect(routerCommandsUI->acceptTransitTunnelsPushButton, SIGNAL(released()), this, SLOT(enableTransit())); QObject::connect(routerCommandsUI->declineTransitTunnelsPushButton, SIGNAL(released()), this, SLOT(disableTransit())); + QObject::connect(ui->logViewerPushButton, SIGNAL(released()), this, SLOT(showLogViewerPage())); + QObject::connect(ui->settingsPagePushButton, SIGNAL(released()), this, SLOT(showSettingsPage())); QObject::connect(ui->tunnelsPagePushButton, SIGNAL(released()), this, SLOT(showTunnelsPage())); @@ -299,6 +305,9 @@ MainWindow::MainWindow(QWidget *parent) : trayIcon->show(); #endif + logViewerManagerPtr=new LogViewerManager(logStream_,ui->logViewerTextEdit,this); + assert(logViewerManagerPtr!=nullptr); + onLoggingOptionsChange(); //QMetaObject::connectSlotsByName(this); } @@ -333,10 +342,11 @@ void MainWindow::showStatusPage(StatusPage newStatusPage){ } wasSelectingAtStatusMainPage=false; } -void MainWindow::showSettingsPage(){ui->stackedWidget->setCurrentIndex(1);setStatusButtonsVisible(false);} -void MainWindow::showTunnelsPage(){ui->stackedWidget->setCurrentIndex(2);setStatusButtonsVisible(false);} -void MainWindow::showRestartPage(){ui->stackedWidget->setCurrentIndex(3);setStatusButtonsVisible(false);} -void MainWindow::showQuitPage(){ui->stackedWidget->setCurrentIndex(4);setStatusButtonsVisible(false);} +void MainWindow::showLogViewerPage(){ui->stackedWidget->setCurrentIndex(1);setStatusButtonsVisible(false);} +void MainWindow::showSettingsPage(){ui->stackedWidget->setCurrentIndex(2);setStatusButtonsVisible(false);} +void MainWindow::showTunnelsPage(){ui->stackedWidget->setCurrentIndex(3);setStatusButtonsVisible(false);} +void MainWindow::showRestartPage(){ui->stackedWidget->setCurrentIndex(4);setStatusButtonsVisible(false);} +void MainWindow::showQuitPage(){ui->stackedWidget->setCurrentIndex(5);setStatusButtonsVisible(false);} void MainWindow::setStatusButtonsVisible(bool visible) { ui->statusButtonsPane->setVisible(visible); @@ -631,6 +641,8 @@ void MainWindow::loadAllConfigs(){ } ReadTunnelsConfig(); + + onLoggingOptionsChange(); } /** returns false iff not valid items present and save was aborted */ bool MainWindow::saveAllConfigs(){ @@ -668,6 +680,8 @@ bool MainWindow::saveAllConfigs(){ SaveTunnelsConfig(); + onLoggingOptionsChange(); + return true; } diff --git a/qt/i2pd_qt/mainwindow.h b/qt/i2pd_qt/mainwindow.h index c5f0c902..51384f51 100644 --- a/qt/i2pd_qt/mainwindow.h +++ b/qt/i2pd_qt/mainwindow.h @@ -62,6 +62,8 @@ #include "widgetlockregistry.h" #include "widgetlock.h" +class LogViewerManager; + template bool isType(boost::any& a) { return @@ -215,7 +217,8 @@ public: }; class LogDestinationComboBoxItem : public ComboBoxItem { public: - LogDestinationComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : ComboBoxItem(option_, comboBox_) {}; + LogDestinationComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : + ComboBoxItem(option_, comboBox_) {} virtual ~LogDestinationComboBoxItem(){} virtual void loadFromConfigOption(){ MainWindowItem::loadFromConfigOption(); @@ -228,6 +231,8 @@ public: MainWindowItem::saveToStringStream(out); } virtual bool isValid() { return true; } + + Q_OBJECT }; class LogLevelComboBoxItem : public ComboBoxItem { public: @@ -370,9 +375,10 @@ class Controller; class MainWindow : public QMainWindow { Q_OBJECT - +private: + std::shared_ptr logStream; public: - explicit MainWindow(QWidget *parent=0); + explicit MainWindow(std::shared_ptr logStream_, QWidget *parent=nullptr); ~MainWindow(); void setI2PController(i2p::qt::Controller* controller_); @@ -419,6 +425,7 @@ public slots: void showStatus_i2p_tunnels_Page(); void showStatus_sam_sessions_Page(); + void showLogViewerPage(); void showSettingsPage(); void showTunnelsPage(); void showRestartPage(); @@ -430,6 +437,8 @@ private: bool wasSelectingAtStatusMainPage; bool showHiddenInfoStatusMainPage; + LogViewerManager *logViewerManagerPtr; + void showStatusPage(StatusPage newStatusPage); #ifndef ANDROID void createActions(); @@ -522,13 +531,6 @@ private: void appendTunnelForms(std::string tunnelNameToFocus); void deleteTunnelForms(); - - /* - - TODO signaturetype - - */ - template std::string GetI2CPOption (const Section& section, const std::string& name, const Type& value) const { @@ -790,6 +792,8 @@ private: }; TunnelsPageUpdateListenerMainWindowImpl tunnelsPageUpdateListener; + + void onLoggingOptionsChange() {} }; #endif // MAINWINDOW_H diff --git a/qt/i2pd_qt/mainwindow.ui b/qt/i2pd_qt/mainwindow.ui index 9b463f44..dcdf88bd 100644 --- a/qt/i2pd_qt/mainwindow.ui +++ b/qt/i2pd_qt/mainwindow.ui @@ -58,7 +58,7 @@ QLayout::SetMaximumSize - + QLayout::SetMinimumSize @@ -96,6 +96,13 @@ + + + + Log + + + @@ -596,7 +603,7 @@ - TextLabel + wrongInputMessageLabel true @@ -627,7 +634,7 @@ - 2 + 1 @@ -671,6 +678,69 @@ + + + + 0 + 0 + + + + + + 0 + 0 + 711 + 531 + + + + + QLayout::SetMinAndMaxSize + + + + + + 15 + + + + Log + + + + + + + + 0 + 0 + + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustIgnored + + + 10000 + + + false + + + true + + + + + + @@ -728,8 +798,8 @@ 0 0 - 689 - 496 + 81 + 28