feat(GODT-2690): update sentry reporting in GUI for new log file naming.

This commit is contained in:
Xavier Michelon
2023-06-14 08:09:11 +02:00
parent c587dfc0dc
commit 38a0cdb4ab
12 changed files with 71 additions and 51 deletions

View File

@ -117,7 +117,27 @@ void AppController::restart(bool isCrashing) {
} }
//****************************************************************************************************************************************************
/// \param[in] launcher The launcher.
/// \param[in] args The launcher arguments.
//****************************************************************************************************************************************************
void AppController::setLauncherArgs(const QString &launcher, const QStringList &args) { void AppController::setLauncherArgs(const QString &launcher, const QStringList &args) {
launcher_ = launcher; launcher_ = launcher;
launcherArgs_ = args; launcherArgs_ = args;
} }
//****************************************************************************************************************************************************
/// \param[in] sessionID The sessionID.
//****************************************************************************************************************************************************
void AppController::setSessionID(const QString &sessionID) {
sessionID_ = sessionID;
}
//****************************************************************************************************************************************************
/// \return The sessionID.
//****************************************************************************************************************************************************
QString AppController::sessionID() {
return sessionID_;
}

View File

@ -37,7 +37,7 @@ class Exception;
/// \brief App controller class. /// \brief App controller class.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
class AppController : public QObject { class AppController : public QObject {
Q_OBJECT Q_OBJECT
friend AppController &app(); friend AppController &app();
public: // member functions. public: // member functions.
@ -52,10 +52,12 @@ public: // member functions.
std::unique_ptr<bridgepp::Overseer> &bridgeOverseer() { return bridgeOverseer_; }; ///< Returns a reference the bridge overseer std::unique_ptr<bridgepp::Overseer> &bridgeOverseer() { return bridgeOverseer_; }; ///< Returns a reference the bridge overseer
bridgepp::ProcessMonitor *bridgeMonitor() const; ///< Return the bridge worker. bridgepp::ProcessMonitor *bridgeMonitor() const; ///< Return the bridge worker.
Settings &settings();; ///< Return the application settings. Settings &settings();; ///< Return the application settings.
void setLauncherArgs(const QString &launcher, const QStringList &args); void setLauncherArgs(const QString &launcher, const QStringList &args); ///< Set the launcher arguments.
void setSessionID(QString const &sessionID); ///< Set the sessionID.
QString sessionID(); ///< Get the sessionID.
public slots: public slots:
void onFatalError(bridgepp::Exception const& e); ///< Handle fatal errors. void onFatalError(bridgepp::Exception const &e); ///< Handle fatal errors.
private: // member functions private: // member functions
AppController(); ///< Default constructor. AppController(); ///< Default constructor.
@ -67,8 +69,9 @@ private: // data members
std::unique_ptr<bridgepp::Log> log_; ///< The log. std::unique_ptr<bridgepp::Log> log_; ///< The log.
std::unique_ptr<bridgepp::Overseer> bridgeOverseer_; ///< The overseer for the bridge monitor worker. std::unique_ptr<bridgepp::Overseer> bridgeOverseer_; ///< The overseer for the bridge monitor worker.
std::unique_ptr<Settings> settings_; ///< The application settings. std::unique_ptr<Settings> settings_; ///< The application settings.
QString launcher_; QString launcher_; ///< The launcher.
QStringList launcherArgs_; QStringList launcherArgs_; ///< The launcher arguments.
QString sessionID_; ///< The sessionID.
}; };

View File

@ -143,12 +143,14 @@ CommandLineOptions parseCommandLine(int argc, char *argv[]) {
options.logLevel = parseLogLevel(argc, argv); options.logLevel = parseLogLevel(argc, argv);
options.sessionID = parseGoCLIStringArgument(argc, argv, { "session-id" }); QString sessionID = parseGoCLIStringArgument(argc, argv, { "session-id" });
if (options.sessionID.isEmpty()) { if (sessionID.isEmpty()) {
options.sessionID = newSessionID(); // The session ID was not passed to us on the command-line -> create one and add to the command-line for bridge
sessionID = newSessionID();
options.bridgeArgs.append("--session-id"); options.bridgeArgs.append("--session-id");
options.bridgeArgs.append(options.sessionID); options.bridgeArgs.append(sessionID);
} }
app().setSessionID(sessionID);
return options; return options;
} }

View File

@ -34,7 +34,6 @@ struct CommandLineOptions {
bridgepp::Log::Level logLevel { bridgepp::Log::defaultLevel }; ///< The log level bridgepp::Log::Level logLevel { bridgepp::Log::defaultLevel }; ///< The log level
bool noWindow { false }; ///< Should the application start without displaying the main window? bool noWindow { false }; ///< Should the application start without displaying the main window?
bool useSoftwareRenderer { false }; ///< Should QML be renderer in software (i.e. without rendering hardware interface). bool useSoftwareRenderer { false }; ///< Should QML be renderer in software (i.e. without rendering hardware interface).
QString sessionID; ///< The sessionID.
}; };

View File

@ -27,20 +27,15 @@ using namespace bridgepp;
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \return A reference to the log. /// \return A reference to the log.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
Log &initLog(QString const &sessionID) { Log &initLog() {
Log &log = app().log(); Log &log = app().log();
log.registerAsQtMessageHandler(); log.registerAsQtMessageHandler();
log.setEchoInConsole(true); 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 // create new GUI log file
QString error; QString error;
if (!log.startWritingToFile(logsDir.absoluteFilePath(QString("%1_gui_000_v%2_%3.log").arg(sessionID, PROJECT_VER, PROJECT_TAG)), &error)) { if (!log.startWritingToFile(QDir(userLogsDir()).absoluteFilePath(QString("%1_gui_000_v%2_%3.log").arg(app().sessionID(),
PROJECT_VER, PROJECT_TAG)), &error)) {
log.error(error); log.error(error);
} }

View File

@ -23,7 +23,7 @@
#include <bridgepp/Log/Log.h> #include <bridgepp/Log/Log.h>
bridgepp::Log &initLog(QString const &sessionID); ///< Initialize the application log. bridgepp::Log &initLog(); ///< Initialize the application log.
#endif //BRIDGE_GUI_LOG_UTILS_H #endif //BRIDGE_GUI_LOG_UTILS_H

View File

@ -61,7 +61,7 @@ void QMLBackend::init(GRPCConfig const &serviceConfig) {
app().grpc().setLog(&log); app().grpc().setLog(&log);
this->connectGrpcEvents(); this->connectGrpcEvents();
app().grpc().connectToServer(bridgepp::userConfigDir(), serviceConfig, app().bridgeMonitor()); app().grpc().connectToServer(app().sessionID(), bridgepp::userConfigDir(), serviceConfig, app().bridgeMonitor());
app().log().info("Connected to backend via gRPC service."); app().log().info("Connected to backend via gRPC service.");
QString bridgeVer; QString bridgeVer;
@ -640,7 +640,7 @@ void QMLBackend::login(QString const &username, QString const &password) const {
HANDLE_EXCEPTION( HANDLE_EXCEPTION(
if (username.compare("coco@bandicoot", Qt::CaseInsensitive) == 0) { if (username.compare("coco@bandicoot", Qt::CaseInsensitive) == 0) {
throw Exception("User requested bridge-gui to crash by trying to log as coco@bandicoot", throw Exception("User requested bridge-gui to crash by trying to log as coco@bandicoot",
"This error exists for test purposes and should be ignored.", __func__, tailOfLatestBridgeLog()); "This error exists for test purposes and should be ignored.", __func__, tailOfLatestBridgeLog(app().sessionID()));
} }
app().grpc().login(username, password); app().grpc().login(username, password);
) )

View File

@ -287,7 +287,7 @@ int main(int argc, char *argv[]) {
initQtApplication(); initQtApplication();
CommandLineOptions const cliOptions = parseCommandLine(argc, argv); CommandLineOptions const cliOptions = parseCommandLine(argc, argv);
Log &log = initLog(cliOptions.sessionID); Log &log = initLog();
QLockFile lock(bridgepp::userCacheDir() + "/" + bridgeGUILock); QLockFile lock(bridgepp::userCacheDir() + "/" + bridgeGUILock);
if (!checkSingleInstance(lock)) { if (!checkSingleInstance(lock)) {
@ -306,22 +306,23 @@ int main(int argc, char *argv[]) {
log.setLevel(cliOptions.logLevel); log.setLevel(cliOptions.logLevel);
log.info(QString("New Sentry reporter - id: %1.").arg(getProtectedHostname())); log.info(QString("New Sentry reporter - id: %1.").arg(getProtectedHostname()));
QString bridgeexec; QString const &sessionID = app().sessionID();
QString bridgeExe;
if (!cliOptions.attach) { if (!cliOptions.attach) {
if (isBridgeRunning()) { if (isBridgeRunning()) {
throw Exception("An orphan instance of bridge is already running. Please terminate it and relaunch the application.", throw Exception("An orphan instance of bridge is already running. Please terminate it and relaunch the application.",
QString(), __FUNCTION__, tailOfLatestBridgeLog()); QString(), __FUNCTION__, tailOfLatestBridgeLog(sessionID));
} }
// before launching bridge, we remove any trailing service config file, because we need to make sure we get a newly generated one. // before launching bridge, we remove any trailing service config file, because we need to make sure we get a newly generated one.
FocusGRPCClient::removeServiceConfigFile(configDir); FocusGRPCClient::removeServiceConfigFile(configDir);
GRPCClient::removeServiceConfigFile(configDir); GRPCClient::removeServiceConfigFile(configDir);
bridgeexec = launchBridge(cliOptions.bridgeArgs); bridgeExe = launchBridge(cliOptions.bridgeArgs);
} }
log.info(QString("Retrieving gRPC service configuration from '%1'").arg(QDir::toNativeSeparators(grpcServerConfigPath(configDir)))); log.info(QString("Retrieving gRPC service configuration from '%1'").arg(QDir::toNativeSeparators(grpcServerConfigPath(configDir))));
app().backend().init(GRPCClient::waitAndRetrieveServiceConfig(configDir, cliOptions.attach ? 0 : grpcServiceConfigWaitDelayMs, app().backend().init(GRPCClient::waitAndRetrieveServiceConfig(sessionID, configDir,
app().bridgeMonitor())); cliOptions.attach ? 0 : grpcServiceConfigWaitDelayMs, app().bridgeMonitor()));
if (!cliOptions.attach) { if (!cliOptions.attach) {
GRPCClient::removeServiceConfigFile(configDir); GRPCClient::removeServiceConfigFile(configDir);
} }
@ -379,9 +380,9 @@ int main(int argc, char *argv[]) {
QStringList args = cliOptions.bridgeGuiArgs; QStringList args = cliOptions.bridgeGuiArgs;
args.append(waitFlag); args.append(waitFlag);
args.append(mainexec); args.append(mainexec);
if (!bridgeexec.isEmpty()) { if (!bridgeExe.isEmpty()) {
args.append(waitFlag); args.append(waitFlag);
args.append(bridgeexec); args.append(bridgeExe);
} }
app().setLauncherArgs(cliOptions.launcher, args); app().setLauncherArgs(cliOptions.launcher, args);
result = QGuiApplication::exec(); result = QGuiApplication::exec();

View File

@ -57,11 +57,13 @@ void GRPCClient::removeServiceConfigFile(QString const &configDir) {
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \param[in] sessionID The sessionID.
/// \param[in] timeoutMs The timeout in milliseconds /// \param[in] timeoutMs The timeout in milliseconds
/// \param[in] serverProcess An optional server process to monitor. If the process it, no need and retry, as connexion cannot be established. Ignored if null. /// \param[in] serverProcess An optional server process to monitor. If the process it, no need and retry, as connexion cannot be established. Ignored if null.
/// \return The service config. /// \return The service config.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(QString const &configDir, qint64 timeoutMs, ProcessMonitor *serverProcess) { GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(QString const & sessionID, QString const &configDir, qint64 timeoutMs,
ProcessMonitor *serverProcess) {
QString const path = grpcServerConfigPath(configDir); QString const path = grpcServerConfigPath(configDir);
QFile file(path); QFile file(path);
@ -71,7 +73,7 @@ GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(QString const &configDir, qi
while (true) { while (true) {
if (serverProcess && serverProcess->getStatus().ended) { if (serverProcess && serverProcess->getStatus().ended) {
throw Exception("Bridge application exited before providing a gRPC service configuration file.", QString(), __FUNCTION__, throw Exception("Bridge application exited before providing a gRPC service configuration file.", QString(), __FUNCTION__,
tailOfLatestBridgeLog()); tailOfLatestBridgeLog(sessionID));
} }
if (file.exists()) { if (file.exists()) {
@ -85,7 +87,7 @@ GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(QString const &configDir, qi
} }
if (!found) { if (!found) {
throw Exception("Server did not provide gRPC service configuration in time.", QString(), __FUNCTION__, tailOfLatestBridgeLog()); throw Exception("Server did not provide gRPC service configuration in time.", QString(), __FUNCTION__, tailOfLatestBridgeLog(sessionID));
} }
GRPCConfig sc; GRPCConfig sc;
@ -114,10 +116,12 @@ void GRPCClient::setLog(Log *log) {
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \param[in] sessionID The sessionID.
/// \param[in] configDir The configuration directory
/// \param[in] serverProcess An optional server process to monitor. If the process it, no need and retry, as connexion cannot be established. Ignored if null. /// \param[in] serverProcess An optional server process to monitor. If the process it, no need and retry, as connexion cannot be established. Ignored if null.
/// \return true iff the connection was successful. /// \return true iff the connection was successful.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
void GRPCClient::connectToServer(QString const &configDir, GRPCConfig const &config, ProcessMonitor *serverProcess) { void GRPCClient::connectToServer(QString const &sessionID, QString const &configDir, GRPCConfig const &config, ProcessMonitor *serverProcess) {
try { try {
serverToken_ = config.token.toStdString(); serverToken_ = config.token.toStdString();
QString address; QString address;
@ -147,7 +151,7 @@ void GRPCClient::connectToServer(QString const &configDir, GRPCConfig const &con
while (true) { while (true) {
if (serverProcess && serverProcess->getStatus().ended) { if (serverProcess && serverProcess->getStatus().ended) {
throw Exception("Bridge application ended before gRPC connexion could be established.", QString(), __FUNCTION__, throw Exception("Bridge application ended before gRPC connexion could be established.", QString(), __FUNCTION__,
tailOfLatestBridgeLog()); tailOfLatestBridgeLog(sessionID));
} }
this->logInfo(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));
@ -158,7 +162,7 @@ void GRPCClient::connectToServer(QString const &configDir, GRPCConfig const &con
if (QDateTime::currentDateTime() > giveUpTime) { if (QDateTime::currentDateTime() > giveUpTime) {
throw Exception("Connection to the gRPC server failed because of a timeout.", QString(), __FUNCTION__, throw Exception("Connection to the gRPC server failed because of a timeout.", QString(), __FUNCTION__,
tailOfLatestBridgeLog()); tailOfLatestBridgeLog(sessionID));
} }
} }

View File

@ -49,7 +49,8 @@ class GRPCClient : public QObject {
Q_OBJECT Q_OBJECT
public: // static member functions public: // static member functions
static void removeServiceConfigFile(QString const &configDir); ///< Delete the service config file. static void removeServiceConfigFile(QString const &configDir); ///< Delete the service config file.
static GRPCConfig waitAndRetrieveServiceConfig(QString const &configDir, qint64 timeoutMs, class ProcessMonitor *serverProcess); ///< Wait and retrieve the service configuration. static GRPCConfig waitAndRetrieveServiceConfig(QString const &sessionID, QString const &configDir, qint64 timeoutMs,
class ProcessMonitor *serverProcess); ///< Wait and retrieve the service configuration.
public: // member functions. public: // member functions.
GRPCClient() = default; ///< Default constructor. GRPCClient() = default; ///< Default constructor.
@ -59,7 +60,7 @@ public: // member functions.
GRPCClient &operator=(GRPCClient const &) = delete; ///< Disabled assignment operator. GRPCClient &operator=(GRPCClient const &) = delete; ///< Disabled assignment operator.
GRPCClient &operator=(GRPCClient &&) = delete; ///< Disabled move assignment operator. GRPCClient &operator=(GRPCClient &&) = delete; ///< Disabled move assignment operator.
void setLog(Log *log); ///< Set the log for the client. void setLog(Log *log); ///< Set the log for the client.
void connectToServer(QString const &configDir, GRPCConfig const &config, class ProcessMonitor *serverProcess); ///< Establish connection to the gRPC server. void connectToServer(QString const &sessionID, QString const &configDir, GRPCConfig const &config, class ProcessMonitor *serverProcess); ///< Establish connection to the gRPC server.
bool isConnected() const; ///< Check whether the gRPC client is connected to the server. bool isConnected() const; ///< Check whether the gRPC client is connected to the server.
grpc::Status checkTokens(QString const &clientConfigPath, QString &outReturnedClientToken); ///< Performs a token check. grpc::Status checkTokens(QString const &clientConfigPath, QString &outReturnedClientToken); ///< Performs a token check.

View File

@ -35,34 +35,29 @@ QString userLogsDir() {
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \brief Return the path of the latest bridge log. /// \brief Return the path of the latest bridge log.
///
/// \param[in] sessionID The sessionID.
/// \return The path of the latest bridge log file. /// \return The path of the latest bridge log file.
/// \return An empty string if no bridge log file was found. /// \return An empty string if no bridge log file was found.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
QString latestBridgeLogPath() { QString latestBridgeLogPath(QString const &sessionID) {
QDir const logsDir(userLogsDir()); QDir const logsDir(userLogsDir());
if (logsDir.isEmpty()) { if (logsDir.isEmpty()) {
return QString(); return QString();
} }
QFileInfoList files = logsDir.entryInfoList({ "v*.log" }, QDir::Files); // could do sorting, but only by last modification time. we want to sort by creation time. QFileInfoList const files = logsDir.entryInfoList({ sessionID + "_bri_*.log" }, QDir::Files, QDir::Name);
if (files.isEmpty()) { return files.isEmpty() ? QString() : files.back().absoluteFilePath();
return QString();
}
std::sort(files.begin(), files.end(), [](QFileInfo const &lhs, QFileInfo const &rhs) -> bool {
return lhs.birthTime() < rhs.birthTime();
});
return files.back().absoluteFilePath();
} }
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// Return the maxSize last bytes of the latest bridge log. /// Return the maxSize last bytes of the latest bridge log.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
QByteArray tailOfLatestBridgeLog() { QByteArray tailOfLatestBridgeLog(QString const &sessionID) {
QString path = latestBridgeLogPath(); QString path = latestBridgeLogPath(sessionID);
if (path.isEmpty()) { if (path.isEmpty()) {
return QByteArray(); return QString("We could not find a bridge log file for the current session.").toLocal8Bit();
} }
QFile file(path); QFile file(path);

View File

@ -24,7 +24,7 @@ namespace bridgepp {
QString userLogsDir(); ///< Return the path of the user logs dir. QString userLogsDir(); ///< Return the path of the user logs dir.
QByteArray tailOfLatestBridgeLog(); ///< Return the last bytes of the last bridge log. QByteArray tailOfLatestBridgeLog(QString const &sessionID); ///< Return the last bytes of the last bridge log.
} // namespace bridgepp } // namespace bridgepp