feat(GODT-2482): more attachment to relevant exceptions.

This commit is contained in:
Xavier Michelon
2023-03-13 14:55:59 +01:00
parent 4273405393
commit 48274ee178
9 changed files with 135 additions and 67 deletions

View File

@ -23,6 +23,7 @@
#include <stdexcept>
namespace bridgepp {
@ -44,6 +45,9 @@ public: // member functions
QByteArray attachment() const noexcept; ///< Return the attachment for the exception.
QString detailedWhat() const; ///< Return the detailed description of the message (i.e. including the function name and the details).
public: // static data members
static qsizetype const attachmentMaxLength {25 * 1024}; ///< The maximum length text attachment sent in Sentry reports, in bytes.
private: // data members
QString const qwhat_; ///< The description of the exception.
QByteArray const what_; ///< The c-string version of the qwhat message. Stored as a QByteArray for automatic lifetime management.

View File

@ -22,6 +22,7 @@
#include "../BridgeUtils.h"
#include "../Exception/Exception.h"
#include "../ProcessMonitor.h"
#include "../Log/LogUtils.h"
using namespace google::protobuf;
@ -70,7 +71,8 @@ GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(QString const &configDir, qi
bool found = false;
while (true) {
if (serverProcess && serverProcess->getStatus().ended) {
throw Exception("Bridge application exited before providing a gRPC service configuration file.");
throw Exception("Bridge application exited before providing a gRPC service configuration file.", QString(), __FUNCTION__,
tailOfLatestBridgeLog());
}
if (file.exists()) {
@ -84,13 +86,20 @@ GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(QString const &configDir, qi
}
if (!found) {
throw Exception("Server did not provide gRPC service configuration in time.");
throw Exception("Server did not provide gRPC service configuration in time.", QString(), __FUNCTION__, tailOfLatestBridgeLog());
}
GRPCConfig sc;
QString err;
if (!sc.load(path, &err)) {
throw Exception("The gRPC service configuration file is invalid.", err);
// include the file content in the exception, if any
QByteArray array;
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
file.readAll();
array = array.right(Exception::attachmentMaxLength);
}
throw Exception("The gRPC service configuration file is invalid.", err, __FUNCTION__, array);
}
return sc;
@ -126,19 +135,20 @@ void GRPCClient::connectToServer(QString const &configDir, GRPCConfig const &con
channel_ = CreateCustomChannel(address.toStdString(), grpc::SslCredentials(opts), chanArgs);
if (!channel_) {
throw Exception("Channel creation failed.");
throw Exception("gRPC channel creation failed.");
}
stub_ = Bridge::NewStub(channel_);
if (!stub_) {
throw Exception("Stub creation failed.");
throw Exception("gRPC stub creation failed.");
}
QDateTime const giveUpTime = QDateTime::currentDateTime().addMSecs(grpcConnectionWaitTimeoutMs); // if we reach giveUpTime without connecting, we give up
int i = 0;
while (true) {
if (serverProcess && serverProcess->getStatus().ended) {
throw Exception("Bridge application ended before gRPC connexion could be established.");
throw Exception("Bridge application ended before gRPC connexion could be established.", QString(), __FUNCTION__,
tailOfLatestBridgeLog());
}
this->logInfo(QString("Connection to gRPC server at %1. attempt #%2").arg(address).arg(++i));
@ -148,7 +158,8 @@ void GRPCClient::connectToServer(QString const &configDir, GRPCConfig const &con
} // connection established.
if (QDateTime::currentDateTime() > giveUpTime) {
throw Exception("Connection to the RPC server failed.");
throw Exception("Connection to the gRPC server failed because of a timeout.", QString(), __FUNCTION__,
tailOfLatestBridgeLog());
}
}
@ -180,7 +191,7 @@ void GRPCClient::connectToServer(QString const &configDir, GRPCConfig const &con
log_->info("gRPC token was validated");
}
catch (Exception const &e) {
throw Exception("Cannot connect to Go backend via gRPC: " + e.qwhat(), e.details());
throw Exception("Cannot connect to Go backend via gRPC: " + e.qwhat(), e.details(), __FUNCTION__, e.attachment());
}
}

View File

@ -0,0 +1,68 @@
// Copyright (c) 2023 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "LogUtils.h"
#include <bridgepp/BridgeUtils.h>
#include <bridgepp/Exception/Exception.h>
namespace bridgepp {
//****************************************************************************************************************************************************
/// \return user logs directory used by bridge.
//****************************************************************************************************************************************************
QString userLogsDir() {
QString const path = QDir(bridgepp::userDataDir()).absoluteFilePath("logs");
QDir().mkpath(path);
return path;
}
//****************************************************************************************************************************************************
/// \brief Return the path of the latest bridge log.
/// \return The path of the latest bridge log file.
/// \return An empty string if no bridge log file was found.
//****************************************************************************************************************************************************
QString latestBridgeLogPath() {
QDir const logsDir(userLogsDir());
if (logsDir.isEmpty()) {
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.
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.
//****************************************************************************************************************************************************
QByteArray tailOfLatestBridgeLog() {
QString path = latestBridgeLogPath();
if (path.isEmpty()) {
return QByteArray();
}
QFile file(path);
return file.open(QIODevice::Text | QIODevice::ReadOnly) ? file.readAll().right(Exception::attachmentMaxLength) : QByteArray();
}
} // namespace bridgepp

View File

@ -0,0 +1,33 @@
// Copyright (c) 2023 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_PP_LOG_UTILS_H
#define BRIDGE_PP_LOG_UTILS_H
namespace bridgepp {
QString userLogsDir(); ///< Return the path of the user logs dir.
QByteArray tailOfLatestBridgeLog(); ///< Return the last bytes of the last bridge log.
} // namespace bridgepp
#endif //BRIDGE_PP_LOG_UTILS_H