diff --git a/internal/frontend/bridge-gui/bridge-gui/main.cpp b/internal/frontend/bridge-gui/bridge-gui/main.cpp index e6667016..33cd85d9 100644 --- a/internal/frontend/bridge-gui/bridge-gui/main.cpp +++ b/internal/frontend/bridge-gui/bridge-gui/main.cpp @@ -81,6 +81,17 @@ Log &initLog() Log &log = app().log(); log.registerAsQtMessageHandler(); log.setEchoInConsole(true); + + // remove old gui log files + QDir const logsDir(userLogsDir()); + for (QFileInfo const fileInfo: logsDir.entryInfoList({ "gui_v*.log" }, QDir::Filter::Files)) // entryInfolist apparently only support wildcards, not regex. + QFile(fileInfo.absoluteFilePath()).remove(); + + // create new GUI log file + QString error; + if (!log.startWritingToFile(logsDir.absoluteFilePath(QString("gui_v%1_%2.log").arg(PROJECT_VER).arg(QDateTime::currentSecsSinceEpoch())), &error)) + log.error(error); + return log; } @@ -278,6 +289,7 @@ int main(int argc, char *argv[]) // display it in our own output and error, so we only continue to log directly to console if we are running in attached mode. log.setEchoInConsole(attach); log.info("Backend was successfully initialized."); + log.stopWritingToFile(); QQmlApplicationEngine engine; std::unique_ptr rootComponent(createRootQmlComponent(engine)); diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeUtils.cpp b/internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeUtils.cpp index 0d59fb4e..bb31ae1a 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeUtils.cpp +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeUtils.cpp @@ -133,6 +133,17 @@ QString userCacheDir() } +//**************************************************************************************************************************************************** +/// \return user logs directory used by bridge. +//**************************************************************************************************************************************************** +QString userLogsDir() +{ + QString const path = QDir(userCacheDir()).absoluteFilePath("logs"); + QDir().mkpath(path); + return path; +} + + //**************************************************************************************************************************************************** /// \return The value GOOS would return for the current platform. //**************************************************************************************************************************************************** diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeUtils.h b/internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeUtils.h index 87f009ad..fddecf9a 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeUtils.h +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeUtils.h @@ -29,6 +29,7 @@ namespace bridgepp QString userConfigDir(); ///< Get the path of the user configuration folder. QString userCacheDir(); ///< Get the path of the user cache folder. +QString userLogsDir(); ///< Get the path of the user logs folder. QString goos(); ///< return the value of Go's GOOS for the current platform ("darwin", "linux" and "windows" are supported). qint64 randN(qint64 n); ///< return a random integer in the half open range [0,n) QString randomFirstName(); ///< Get a random first name from a pre-determined list. diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.cpp b/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.cpp index 300f2778..ca14b2a0 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.cpp +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.cpp @@ -123,7 +123,7 @@ bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError) int i = 0; while (true) { - this->logDebug(QString("Connection to gRPC server at %1. attempt #%2").arg(address).arg(++i)); + this->logInfo(QString("Connection to gRPC server at %1. attempt #%2").arg(address).arg(++i)); if (channel_->WaitForConnected(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(grpcConnectionRetryDelayMs, GPR_TIMESPAN)))) break; // connection established. @@ -135,13 +135,13 @@ bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError) if (channel_->GetState(true) != GRPC_CHANNEL_READY) throw Exception("connection check failed."); - this->logDebug("Successfully connected to gRPC server."); + this->logInfo("Successfully connected to gRPC server."); QString const clientToken = QUuid::createUuid().toString(); QString clientConfigPath = createClientConfigFile(clientToken); if (clientConfigPath.isEmpty()) throw Exception("gRPC client config could not be saved."); - this->logDebug(QString("Client config file was saved to '%1'").arg(QDir::toNativeSeparators(clientConfigPath))); + this->logInfo(QString("Client config file was saved to '%1'").arg(QDir::toNativeSeparators(clientConfigPath))); QString returnedClientToken; grpc::Status status = this->checkTokens(QDir::toNativeSeparators(clientConfigPath), returnedClientToken); @@ -153,7 +153,7 @@ bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError) if (!status.ok()) throw Exception(QString::fromStdString(status.error_message())); - log_->debug("gRPC token was validated"); + log_->info("gRPC token was validated"); return true; } @@ -933,6 +933,15 @@ void GRPCClient::logError(QString const &message) } +//**************************************************************************************************************************************************** +/// \param[in] message The event message. +//**************************************************************************************************************************************************** +void GRPCClient::logInfo(QString const &message) +{ + this->log(Log::Level::Info, message); +} + + //**************************************************************************************************************************************************** /// \param[in] status The status /// \param[in] callName The call name. diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.h b/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.h index 892297f4..fa1612f2 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.h +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/GRPC/GRPCClient.h @@ -211,6 +211,8 @@ private: void logTrace(QString const &message); ///< Log a trace event. void logDebug(QString const &message); ///< Log a debug event. void logError(QString const &message); ///< Log an error event. + void logInfo(QString const &message); ///< Log an info event. + grpc::Status logGRPCCallStatus(grpc::Status const &status, QString const &callName, QList allowedErrors = {}); ///< Log the status of a gRPC code. grpc::Status simpleMethod(SimpleMethod method); ///< perform a gRPC call to a bool setter. grpc::Status setBool(BoolSetter setter, bool value); ///< perform a gRPC call to a bool setter. diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/Log/Log.cpp b/internal/frontend/bridge-gui/bridgepp/bridgepp/Log/Log.cpp index 480ff509..e7b8d617 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/Log/Log.cpp +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/Log/Log.cpp @@ -17,6 +17,7 @@ #include "Log.h" +#include "../Exception/Exception.h" namespace bridgepp @@ -204,6 +205,35 @@ bool Log::echoInConsole() const } +//**************************************************************************************************************************************************** +/// \param[in] path The path of the file to write to. +/// \param[out] outError if an error occurs and this pointer in not null, on exit it contains a description of the error. +/// \return true if and only if the operation was successful. +//**************************************************************************************************************************************************** +bool Log::startWritingToFile(QString const &path, QString *outError) +{ + QMutexLocker locker(&mutex_); + file_ = std::make_unique(path); + if (file_->open(QIODevice::WriteOnly | QIODevice::Text)) + return true; + + if (outError) + *outError = QString("Could not open log file '%1' for writing."); + file_.reset(); + return false; +} + + +//**************************************************************************************************************************************************** +/// +//**************************************************************************************************************************************************** +void Log::stopWritingToFile() +{ + QMutexLocker locker(&mutex_); + file_.reset(); +} + + //**************************************************************************************************************************************************** /// \param[in] message The message. //**************************************************************************************************************************************************** @@ -278,12 +308,22 @@ void Log::addEntry(Log::Level level, QString const &message) return; emit entryAdded(level, message); + if (!(echoInConsole_ || file_)) + return; + + QString const entryStr = logEntryToString(level, message) + "\n"; if (echoInConsole_) { QTextStream &stream = (qint32(level) <= (qint32(Level::Warn))) ? stderr_ : stdout_; - stream << logEntryToString(level, message) << "\n"; + stream << entryStr; stream.flush(); } + + if (file_) + { + file_->write(entryStr.toLocal8Bit()); + file_->flush(); + } } diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/Log/Log.h b/internal/frontend/bridge-gui/bridgepp/bridgepp/Log/Log.h index 039e8bc6..c21d8420 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/Log/Log.h +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/Log/Log.h @@ -63,6 +63,8 @@ public: // member functions. Level level() const; ///< Get the log level. void setEchoInConsole(bool value); ///< Set if the log entries should be echoed in STDOUT/STDERR. bool echoInConsole() const; ///< Check if the log entries should be echoed in STDOUT/STDERR. + bool startWritingToFile(QString const& path, QString *outError = nullptr); ///< Start writing the log to file. Concerns only future entries. + void stopWritingToFile(); void registerAsQtMessageHandler(); ///< Install the Qt message handler. public slots: @@ -83,7 +85,7 @@ private: // data members mutable QMutex mutex_; ///< The mutex. Level level_ { defaultLevel }; ///< The log level bool echoInConsole_ { false }; ///< Set if the log messages should be sent to STDOUT/STDERR. - + std::unique_ptr file_; ///< The file to write the log to. QTextStream stdout_; ///< The stdout stream. QTextStream stderr_; ///< The stderr stream. };