refactor: 引入新的日志系统

This commit is contained in:
Chenx Dust
2026-07-27 18:35:05 +08:00
parent b2e29e768b
commit edd27f29ff
15 changed files with 480 additions and 123 deletions

View File

@@ -36,12 +36,14 @@ set(SOURCE_FILES
main.cpp
mainwindow.cpp
zjuconnectmode.cpp
application/applicationlogger.cpp
application/connectionsession.cpp
application/systemproxysession.cpp
core/corecommandbuilder.cpp
core/coreoutputbuffer.cpp
core/coreoutputparser.cpp
core/connectionsessionstate.cpp
infrastructure/corelogfile.cpp
infrastructure/settingsprofileloader.cpp
infrastructure/platformsystemproxybackend.cpp
zjuconnectcontroller/zjuconnectcontroller.cpp
@@ -120,6 +122,22 @@ target_link_libraries(${PROJECT_NAME}
include(CTest)
if(BUILD_TESTING)
add_executable(applicationlogger_test
tests/applicationlogger_test.cpp
application/applicationlogger.cpp
)
target_include_directories(applicationlogger_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(applicationlogger_test Qt::Core)
add_test(NAME applicationlogger_test COMMAND applicationlogger_test)
add_executable(corelogfile_test
tests/corelogfile_test.cpp
infrastructure/corelogfile.cpp
)
target_include_directories(corelogfile_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(corelogfile_test Qt::Core)
add_test(NAME corelogfile_test COMMAND corelogfile_test)
add_executable(corecommandbuilder_test
tests/corecommandbuilder_test.cpp
core/corecommandbuilder.cpp

View File

@@ -0,0 +1,143 @@
#include "applicationlogger.h"
#include <QCoreApplication>
#include <QDateTime>
#include <QIODevice>
#include <QMetaObject>
#include <QRecursiveMutex>
#include <QThread>
#include <cstdio>
namespace
{
ApplicationLogger *messageHandlerTarget = nullptr;
QRecursiveMutex messageHandlerMutex;
QString currentTimestamp()
{
return QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss");
}
QString messageTypeName(QtMsgType type)
{
switch (type)
{
case QtDebugMsg:
return "DEBUG";
case QtInfoMsg:
return "INFO";
case QtWarningMsg:
return "WARNING";
case QtCriticalMsg:
return "CRITICAL";
case QtFatalMsg:
return "FATAL";
}
return "UNKNOWN";
}
}
ApplicationLogger::ApplicationLogger(QObject *parent)
: QObject(parent),
standardOutput(stdout, QIODevice::WriteOnly)
{
writeStandardOutput(
"=== Log started at " + currentTimestamp()
+ " with " + QCoreApplication::applicationName()
+ " " + QCoreApplication::applicationVersion()
+ " ==="
);
{
QMutexLocker locker(&messageHandlerMutex);
messageHandlerTarget = this;
previousMessageHandler = qInstallMessageHandler(ApplicationLogger::qtMessageHandler);
}
}
ApplicationLogger::~ApplicationLogger()
{
{
QMutexLocker locker(&messageHandlerMutex);
if (messageHandlerTarget == this)
{
messageHandlerTarget = nullptr;
qInstallMessageHandler(previousMessageHandler);
}
}
writeStandardOutput("=== Log ended at " + currentTimestamp() + " ===");
}
void ApplicationLogger::appendMessage(const QString &prefix, const QString &message)
{
if (QThread::currentThread() != thread())
{
QMetaObject::invokeMethod(
this,
[this, prefix, message]() { appendMessage(prefix, message); },
Qt::QueuedConnection
);
return;
}
publishEntry(prefix + " " + currentTimestamp() + " " + message.trimmed());
}
void ApplicationLogger::appendCoreOutput(const QString &output)
{
if (QThread::currentThread() != thread())
{
QMetaObject::invokeMethod(
this,
[this, output]() { appendCoreOutput(output); },
Qt::QueuedConnection
);
return;
}
QStringList lines = output.split('\n', Qt::KeepEmptyParts);
for (QString &line : lines)
{
line.prepend("[CORE] ");
}
publishEntry(lines.join('\n'));
}
void ApplicationLogger::qtMessageHandler(
QtMsgType type,
const QMessageLogContext &context,
const QString &message
)
{
Q_UNUSED(context)
QMutexLocker locker(&messageHandlerMutex);
if (messageHandlerTarget != nullptr)
{
messageHandlerTarget->appendQtMessage(type, message);
}
}
void ApplicationLogger::appendQtMessage(QtMsgType type, const QString &message)
{
appendMessage("[" + messageTypeName(type) + "]", message);
}
void ApplicationLogger::publishEntry(const QString &entry)
{
emit entryAdded(entry);
writeStandardOutput(entry);
}
void ApplicationLogger::writeStandardOutput(const QString &output)
{
standardOutput << output;
if (!output.endsWith('\n'))
{
standardOutput << '\n';
}
standardOutput.flush();
}

View File

@@ -0,0 +1,37 @@
#ifndef APPLICATIONLOGGER_H
#define APPLICATIONLOGGER_H
#include <QLoggingCategory>
#include <QObject>
#include <QTextStream>
class ApplicationLogger : public QObject
{
Q_OBJECT
public:
explicit ApplicationLogger(QObject *parent = nullptr);
~ApplicationLogger() override;
void appendCoreOutput(const QString &output);
signals:
void entryAdded(const QString &entry);
private:
static void qtMessageHandler(
QtMsgType type,
const QMessageLogContext &context,
const QString &message
);
void appendQtMessage(QtMsgType type, const QString &message);
void appendMessage(const QString &prefix, const QString &message);
void publishEntry(const QString &entry);
void writeStandardOutput(const QString &output);
QTextStream standardOutput;
QtMessageHandler previousMessageHandler = nullptr;
};
#endif // APPLICATIONLOGGER_H

View File

@@ -1,8 +1,11 @@
#include "authinfowindow.h"
#include "utils/utils.h"
#include "mainwindow.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QMessageBox>
#include <QPushButton>
#include <QKeyEvent>
@@ -34,14 +37,13 @@ AuthInfoWindow::AuthInfoWindow(QWidget *parent)
connect(proc_, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
[this](int exitCode, QProcess::ExitStatus exitStatus) {
QString output = Utils::consoleOutputToQString(stdoutBuf_);
if (mainWindow)
mainWindow->addLog("可用认证方式:\n" + output);
qInfo().noquote() << "可用认证方式:\n" + output;
QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(output.toUtf8(), &jsonError);
if (jsonError.error != QJsonParseError::NoError && mainWindow)
mainWindow->addLog("解析可用认证方式失败:" + jsonError.errorString());
if (!doc.isArray() && mainWindow)
mainWindow->addLog("解析可用认证方式失败:可用认证方式不是列表");
if (jsonError.error != QJsonParseError::NoError)
qWarning().noquote() << "解析可用认证方式失败:" + jsonError.errorString();
if (!doc.isArray())
qWarning().noquote() << "解析可用认证方式失败:可用认证方式不是列表";
QJsonArray arr = doc.array();
for (QJsonValueRef v : arr) {
QJsonObject obj = v.toObject();
@@ -58,12 +60,10 @@ AuthInfoWindow::AuthInfoWindow(QWidget *parent)
}
});
connect(proc_, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
mainWindow->addLog(QString("获取可用认证方式失败:") + QMetaEnum::fromType<QProcess::ProcessError>().valueToKey(error));
qWarning().noquote()
<< QString("获取可用认证方式失败:")
+ QMetaEnum::fromType<QProcess::ProcessError>().valueToKey(error);
});
mainWindow = qobject_cast<MainWindow *>(parent);
if (!mainWindow)
mainWindow = qobject_cast<MainWindow *>(parent->parent());
}
AuthInfoWindow::~AuthInfoWindow()
@@ -77,8 +77,5 @@ void AuthInfoWindow::fetchAuthInfo(const QString& serverAddress, int port)
stderrBuf_.clear();
proc_->start(Utils::getCorePath(),
{"-protocol", "atrust", "-server", serverAddress, "-port", QString::number(port), "-auth-info"});
if (mainWindow)
{
mainWindow->addLog("正在获取可用认证的方式...");
}
qInfo().noquote() << "正在获取可用认证的方式...";
}

View File

@@ -4,8 +4,6 @@
#include <QDialog>
#include <QProcess>
class MainWindow;
class AuthInfoWindow : public QDialog
{
Q_OBJECT
@@ -22,7 +20,6 @@ signals:
private:
Ui::AuthInfoWindow *ui;
MainWindow *mainWindow = nullptr;
QProcess *proc_ = nullptr;
QByteArray stdoutBuf_;
QByteArray stderrBuf_;

View File

@@ -0,0 +1,59 @@
#include "corelogfile.h"
#include <QMetaObject>
#include <QThread>
CoreLogFile::CoreLogFile(const QString &filePath, QObject *parent)
: QObject(parent),
file(filePath)
{
if (file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
{
stream.setDevice(&file);
stream.setEncoding(QStringConverter::Utf8);
}
}
CoreLogFile::~CoreLogFile()
{
if (file.isOpen())
{
stream.flush();
file.close();
}
}
QString CoreLogFile::filePath() const
{
return file.fileName();
}
bool CoreLogFile::isOpen() const
{
return file.isOpen();
}
void CoreLogFile::appendOutput(const QString &output)
{
if (QThread::currentThread() != thread())
{
QMetaObject::invokeMethod(
this,
[this, output]() { appendOutput(output); },
Qt::QueuedConnection
);
return;
}
if (!file.isOpen())
{
return;
}
stream << output;
if (!output.endsWith('\n'))
{
stream << '\n';
}
stream.flush();
}

View File

@@ -0,0 +1,26 @@
#ifndef CORELOGFILE_H
#define CORELOGFILE_H
#include <QFile>
#include <QObject>
#include <QTextStream>
class CoreLogFile : public QObject
{
Q_OBJECT
public:
explicit CoreLogFile(const QString &filePath, QObject *parent = nullptr);
~CoreLogFile() override;
QString filePath() const;
bool isOpen() const;
void appendOutput(const QString &output);
private:
QFile file;
QTextStream stream;
};
#endif // CORELOGFILE_H

View File

@@ -2,6 +2,7 @@
#include "SingleApplication"
#include "application/applicationlogger.h"
#include "mainwindow.h"
#include "utils/utils.h"
@@ -17,6 +18,8 @@ int main(int argc, char *argv[])
QApplication::setApplicationVersion(PROJ_VER);
QLocale::setDefault(QLocale(QLocale::Chinese, QLocale::SimplifiedChineseScript, QLocale::China));
ApplicationLogger applicationLogger;
#if defined(Q_OS_WINDOWS)
QApplication::setFont(QFont("Microsoft YaHei UI", QApplication::font().pointSize()));
#endif
@@ -35,7 +38,7 @@ int main(int argc, char *argv[])
else
qDebug() << "Failed to load transaction file for" << translateModule;
MainWindow mainWindow;
MainWindow mainWindow(&applicationLogger);
QObject::connect(&app, &SingleApplication::aboutToQuit, &mainWindow, &MainWindow::cleanUpWhenQuit);

View File

@@ -5,6 +5,7 @@
#include <QNetworkInterface>
#include <QClipboard>
#include <QDesktopServices>
#include <QDebug>
#include <QFileInfo>
#include <QCoreApplication>
#include <QActionGroup>
@@ -12,13 +13,16 @@
#include "mainwindow.h"
#include "application/applicationlogger.h"
#include "infrastructure/corelogfile.h"
#include "ui_mainwindow.h"
#include "utils/utils.h"
#include "zjuconnectcontroller/zjuconnectcontroller.h"
MainWindow::MainWindow(QWidget *parent) :
MainWindow::MainWindow(ApplicationLogger *logger, QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
ui(new Ui::MainWindow),
applicationLogger(logger)
{
profileManager = new ProfileManager();
const QString overrideConfigPath = Utils::getArgValue(QCoreApplication::arguments(), "--config-path");
@@ -39,6 +43,12 @@ MainWindow::MainWindow(QWidget *parent) :
isFirstTimeSetMode = true;
ui->setupUi(this);
coreLogFile = new CoreLogFile(Utils::getLogFilePath(), this);
connect(applicationLogger, &ApplicationLogger::entryAdded, this,
[this](const QString &entry)
{
ui->logPlainTextEdit->appendPlainText(entry);
});
systemProxySession = new SystemProxySession(this);
connect(systemProxySession, &SystemProxySession::busyChanged, this,
[this](bool busy)
@@ -92,9 +102,9 @@ MainWindow::MainWindow(QWidget *parent) :
// 文件-打开日志文件
connect(ui->openLogAction, &QAction::triggered, this,
[&]()
[this]()
{
QString logFilePath = Utils::getLogFilePath();
const QString logFilePath = coreLogFile->filePath();
QFileInfo logFileInfo(logFilePath);
if (logFileInfo.exists())
@@ -103,7 +113,7 @@ MainWindow::MainWindow(QWidget *parent) :
}
else
{
QMessageBox::information(this, "日志文件", "日志文件还未生成,请先启动 VPN 连接");
QMessageBox::warning(this, "日志文件", "日志文件创建失败");
}
});
@@ -129,7 +139,7 @@ MainWindow::MainWindow(QWidget *parent) :
{
if (!enabled)
{
addLog("已清理系统代理设置");
qInfo().noquote() << "已清理系统代理设置";
}
},
Qt::SingleShotConnection);
@@ -154,7 +164,7 @@ MainWindow::MainWindow(QWidget *parent) :
}
Utils::clearClientData(currentProfileId);
addLog("已清理登录缓存");
qInfo().noquote() << "已清理登录缓存";
});
// 文件-设置授信设备
@@ -168,12 +178,12 @@ MainWindow::MainWindow(QWidget *parent) :
settings->value("ZJUConnect/ServerAddress").toString(),
settings->value("ZJUConnect/ServerPort").toInt(),
currentProfileId, true);
addLog("设置授信设备成功");
qInfo().noquote() << "设置授信设备成功";
QMessageBox::information(this, "成功", "已设置授信设备");
}
catch (const std::runtime_error &e)
{
addLog("设置授信设备失败:" + QString(e.what()));
qWarning().noquote() << "设置授信设备失败:" + QString(e.what());
QMessageBox::critical(this, "错误", "设置授信设备失败:\n" + QString(e.what()));
}
});
@@ -189,12 +199,12 @@ MainWindow::MainWindow(QWidget *parent) :
settings->value("ZJUConnect/ServerAddress").toString(),
settings->value("ZJUConnect/ServerPort").toInt(),
currentProfileId, false);
addLog("取消授信设备成功");
qInfo().noquote() << "取消授信设备成功";
QMessageBox::information(this, "成功", "已取消授信设备");
}
catch (const std::runtime_error &e)
{
addLog("取消授信设备失败:" + QString(e.what()));
qWarning().noquote() << "取消授信设备失败:" + QString(e.what());
QMessageBox::critical(this, "错误", "取消授信设备失败:\n" + QString(e.what()));
}
});
@@ -267,7 +277,7 @@ MainWindow::MainWindow(QWidget *parent) :
this, [&](QNetworkReply* reply) {
if (reply->error() != QNetworkReply::NoError)
{
addLog("检查 UI 更新失败。原因是:" + reply->errorString());
qWarning().noquote() << "检查 UI 更新失败。原因是:" + reply->errorString();
ui->versionLabel->setText(
"当前版本:" + QApplication::applicationVersion() + "\n检查 UI 更新失败\n"
);
@@ -286,7 +296,7 @@ MainWindow::MainWindow(QWidget *parent) :
{
latestVersion = latestVersion.mid(1);
}
addLog("检查 UI 更新成功。最新版本:" + latestVersion);
qInfo().noquote() << "检查 UI 更新成功。最新版本:" + latestVersion;
versionInfo.ui_latest = latestVersion;
updateVersionInfo();
@@ -319,7 +329,7 @@ MainWindow::MainWindow(QWidget *parent) :
this, [&](QNetworkReply* reply) {
if (reply->error() != QNetworkReply::NoError)
{
addLog("检查核心更新失败。原因是:" + reply->errorString());
qWarning().noquote() << "检查核心更新失败。原因是:" + reply->errorString();
ui->versionLabel->setText(
"当前版本:" + QApplication::applicationVersion() + "\n检查核心更新失败\n"
);
@@ -338,7 +348,7 @@ MainWindow::MainWindow(QWidget *parent) :
{
latestVersion = latestVersion.mid(1);
}
addLog("检查核心更新成功。最新版本:" + latestVersion);
qInfo().noquote() << "检查核心更新成功。最新版本:" + latestVersion;
versionInfo.core_latest = latestVersion;
updateVersionInfo();
@@ -349,7 +359,7 @@ MainWindow::MainWindow(QWidget *parent) :
if (latestVersionQ > nowVersionQ ||
(latestVersionQ == nowVersionQ && latestVersion.right(latestVersionSuffix) != nowVersion.right(nowVersionSuffix)))
{
addLog("核心版本存在更新,可手动更新或通知开发者更新。");
qInfo().noquote() << "核心版本存在更新,可手动更新或通知开发者更新。";
}
});
@@ -404,12 +414,6 @@ void MainWindow::changeEvent(QEvent *event)
}
}
void MainWindow::addLog(const QString &log)
{
QString timeString = QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss");
ui->logPlainTextEdit->appendPlainText(timeString + " " + log.trimmed());
}
void MainWindow::clearLog()
{
ui->logPlainTextEdit->clear();
@@ -624,7 +628,7 @@ bool MainWindow::switchProfile(const QString &profileId)
clearLog();
refreshProfileMenu();
addLog("已切换到配置:" + currentProfileId);
qInfo().noquote() << "已切换到配置:" + currentProfileId;
return true;
}
@@ -700,7 +704,7 @@ void MainWindow::renameCurrentProfile()
updateVersionInfo();
refreshProfileMenu();
clearLog();
addLog("当前配置已重命名为:" + currentProfileId);
qInfo().noquote() << "当前配置已重命名为:" + currentProfileId;
}
void MainWindow::deleteCurrentProfile()
@@ -740,7 +744,7 @@ void MainWindow::deleteCurrentProfile()
}
refreshProfileMenu();
addLog("已删除配置:" + removedProfileId);
qInfo().noquote() << "已删除配置:" + removedProfileId;
}
void MainWindow::checkUpdate()
@@ -748,11 +752,11 @@ void MainWindow::checkUpdate()
try
{
versionInfo.core_version = Utils::checkCoreVersion(this);
addLog("检查核心版本成功:" + versionInfo.core_version);
qInfo().noquote() << "检查核心版本成功:" + versionInfo.core_version;
}
catch (const std::runtime_error& e)
{
addLog("检查核心版本失败:" + QString(e.what()));
qWarning().noquote() << "检查核心版本失败:" + QString(e.what());
versionInfo.core_version = "错误";
}
QNetworkRequest request(QUrl("https://api.github.com/repos/" + Utils::REPO_NAME + "/releases/latest"));

View File

@@ -24,17 +24,18 @@ namespace Ui
class MainWindow;
}
class ApplicationLogger;
class CoreLogFile;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
explicit MainWindow(ApplicationLogger *logger, QWidget *parent = nullptr);
~MainWindow() override;
void addLog(const QString &log);
public slots:
void cleanUpWhenQuit();
@@ -100,6 +101,8 @@ private:
QAction *deleteProfileAction;
ConnectionSession *connectionSession = nullptr;
SystemProxySession *systemProxySession = nullptr;
ApplicationLogger *applicationLogger;
CoreLogFile *coreLogFile = nullptr;
QNetworkAccessManager *checkUpdateNAM;
QNetworkAccessManager *checkCoreUpdateNAM;
QSettings *settings;

View File

@@ -0,0 +1,60 @@
#include <QCoreApplication>
#include <QDebug>
#include <QLoggingCategory>
#include <QRegularExpression>
#include <QThread>
#include "application/applicationlogger.h"
Q_LOGGING_CATEGORY(backgroundLog, "logger.background")
namespace
{
bool publishesQtAndCoreEntries()
{
QStringList entries;
{
ApplicationLogger logger;
QObject::connect(&logger, &ApplicationLogger::entryAdded,
[&entries](const QString &entry) { entries.append(entry); });
qInfo().noquote() << "application event";
QThread *loggingThread = QThread::create(
[]() { qCWarning(backgroundLog).noquote() << "background warning"; }
);
loggingThread->start();
loggingThread->wait();
delete loggingThread;
QCoreApplication::processEvents();
logger.appendCoreOutput("standard output\ncontinued standard output");
logger.appendCoreOutput("error output");
}
const QString timestampPattern = R"(\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2})";
const bool entriesArePublished = entries.size() == 4
&& QRegularExpression(
"^\\[INFO\\] " + timestampPattern + " application event$"
).match(entries.at(0)).hasMatch()
&& QRegularExpression(
"^\\[WARNING\\] " + timestampPattern + " background warning$"
).match(entries.at(1)).hasMatch()
&& entries.at(2) == "[CORE] standard output\n[CORE] continued standard output"
&& entries.at(3) == "[CORE] error output";
if (!entriesArePublished)
{
qCritical() << "publishesQtAndCoreEntries failed";
return false;
}
return true;
}
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QCoreApplication::setApplicationName("LoggerTest");
QCoreApplication::setApplicationVersion("1.0");
return publishesQtAndCoreEntries() ? 0 : 1;
}

View File

@@ -0,0 +1,53 @@
#include <QCoreApplication>
#include <QDebug>
#include <QFile>
#include <QTemporaryDir>
#include "infrastructure/corelogfile.h"
namespace
{
bool writesOnlyRawCoreOutput()
{
QTemporaryDir temporaryDirectory;
if (!temporaryDirectory.isValid())
{
qCritical() << "Unable to create temporary directory";
return false;
}
const QString logPath = temporaryDirectory.filePath("core.log");
{
CoreLogFile logFile(logPath);
if (!logFile.isOpen() || logFile.filePath() != logPath)
{
qCritical() << "Core log file did not open the requested path";
return false;
}
logFile.appendOutput("standard output\ncontinued standard output");
logFile.appendOutput("error output");
}
QFile logFile(logPath);
if (!logFile.open(QIODevice::ReadOnly | QIODevice::Text))
{
qCritical() << "Unable to read generated core log";
return false;
}
const QString contents = QString::fromUtf8(logFile.readAll());
if (contents != "standard output\ncontinued standard output\nerror output\n")
{
qCritical().noquote() << "writesOnlyRawCoreOutput failed:\n" << contents;
return false;
}
return true;
}
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
return writesOnlyRawCoreOutput() ? 0 : 1;
}

View File

@@ -3,9 +3,8 @@
#include "core/coreoutputparser.h"
#include "utils/utils.h"
#include <qcontainerfwd.h>
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QStandardPaths>
@@ -13,19 +12,6 @@ ZjuConnectController::ZjuConnectController(QObject *parent) : QObject(parent)
{
zjuConnectProcess = new QProcess(this);
// 初始化日志文件
logFile = new QFile(Utils::getLogFilePath());
if (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
{
logStream = new QTextStream(logFile);
logStream->setEncoding(QStringConverter::Utf8);
QString startMsg = "=== Log started at " + QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss") +
" with " + QCoreApplication::applicationName() + " " + QCoreApplication::applicationVersion() +
" ===\n";
*logStream << startMsg;
logStream->flush();
}
connect(zjuConnectProcess, &QProcess::readyReadStandardOutput, this, [this]()
{
processOutput(standardOutputBuffer, zjuConnectProcess->readAllStandardOutput());
@@ -42,13 +28,12 @@ ZjuConnectController::ZjuConnectController(QObject *parent) : QObject(parent)
{
return;
}
QString timeString = QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss");
QString errorString = zjuConnectProcess->errorString();
emit outputRead(timeString + " 退出原因:" + errorString);
qWarning().noquote() << "退出原因:" + errorString;
if (errorString.contains("No such file or directory") || errorString.contains("not found") || errorString.contains("找不到"))
{
emit outputRead(timeString + " 核心路径:" + zjuConnectProcess->program());
qWarning().noquote() << "核心路径:" + zjuConnectProcess->program();
emit error(ZJU_ERROR::PROGRAM_NOT_FOUND);
}
});
@@ -58,8 +43,7 @@ ZjuConnectController::ZjuConnectController(QObject *parent) : QObject(parent)
processOutput(standardOutputBuffer, zjuConnectProcess->readAllStandardOutput(), true);
processOutput(standardErrorBuffer, zjuConnectProcess->readAllStandardError(), true);
stopRequested = false;
QString timeString = QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss");
emit outputRead(timeString + " 退出原因:" "进程已结束");
qInfo().noquote() << "退出原因:进程已结束";
emit finished();
});
}
@@ -96,12 +80,6 @@ void ZjuConnectController::processOutputLines(const QList<QByteArray> &lines)
const QString output = outputLines.join('\n');
emit outputRead(output);
if (logStream != nullptr)
{
*logStream << output << '\n';
logStream->flush();
}
for (const QString &line : outputLines)
{
switch (CoreOutputParser::parse(line))
@@ -225,16 +203,15 @@ void ZjuConnectController::start(const ConnectionProfile &profile)
}
const CoreCommand command = CoreCommandBuilder::build(profile, runtimePaths);
QString timeString = QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss");
emit outputRead(timeString + " VPN 启动!参数:" + command.loggableArguments.join(' '));
qInfo().noquote() << "VPN 启动!参数:" + command.loggableArguments.join(' ');
if (!profile.credentials.totpSecret.isEmpty())
{
emit outputRead(timeString + " 使用了 TOTP");
qInfo().noquote() << "使用了 TOTP";
}
if (!profile.credentials.certFile.isEmpty())
{
emit outputRead(timeString + " 使用了证书文件");
qInfo().noquote() << "使用了证书文件";
}
QString programToStart = profile.program;
@@ -292,24 +269,4 @@ void ZjuConnectController::writeInput(const QByteArray &data)
ZjuConnectController::~ZjuConnectController()
{
stop();
// 关闭日志文件
if (logStream != nullptr)
{
QString endMsg = "=== Log ended at " + QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss") + " ===\n";
*logStream << endMsg;
logStream->flush();
delete logStream;
logStream = nullptr;
}
if (logFile != nullptr)
{
if (logFile->isOpen())
{
logFile->close();
}
delete logFile;
logFile = nullptr;
}
}

View File

@@ -55,8 +55,6 @@ private:
QString graphFile;
QFile *logFile = nullptr;
QTextStream *logStream = nullptr;
CoreOutputBuffer standardOutputBuffer;
CoreOutputBuffer standardErrorBuffer;
bool stopRequested = false;

View File

@@ -1,6 +1,7 @@
#include <QMessageBox>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QDebug>
#include <QInputDialog>
#include <QLabel>
#include <QLineEdit>
@@ -8,6 +9,8 @@
#include <QApplication>
#include "mainwindow.h"
#include "application/applicationlogger.h"
#include "infrastructure/corelogfile.h"
#include "ui_mainwindow.h"
#include "sudowindow/sudowindow.h"
#include "loginwindow/loginwindow.h"
@@ -28,14 +31,13 @@ void MainWindow::initZjuConnect()
resetZjuConnectUi();
// 连接服务器
connect(connectionSession, &ConnectionSession::outputRead, this,
[&](const QString &output)
{
ui->logPlainTextEdit->appendPlainText(output);
});
connect(connectionSession, &ConnectionSession::outputRead,
applicationLogger, &ApplicationLogger::appendCoreOutput);
connect(connectionSession, &ConnectionSession::outputRead,
coreLogFile, &CoreLogFile::appendOutput);
connect(connectionSession, &ConnectionSession::savedSudoPasswordRejected, this,
[&]() { addLog("sudo 密码可能有误,不使用记住的密码"); });
[&]() { qWarning().noquote() << "sudo 密码可能有误,不使用记住的密码"; });
connect(connectionSession, &ConnectionSession::askSudoPass, this,
[&]()
@@ -50,18 +52,18 @@ void MainWindow::initZjuConnect()
connect(connectionSession, &ConnectionSession::graphCaptcha, this,
[&](const QString &graphFile) {
addLog("需要图形验证码");
qInfo().noquote() << "需要图形验证码";
graphCaptchaWindow = new GraphCaptchaWindow(this);
graphCaptchaWindow->setGraph(graphFile);
graphCaptchaWindow->show();
connect(graphCaptchaWindow, &GraphCaptchaWindow::finishCaptcha, this, [&](const QByteArray &captcha) {
addLog("图形验证码已提交");
qInfo().noquote() << "图形验证码已提交";
connectionSession->submitInput(captcha + "\n");
});
});
connect(connectionSession, &ConnectionSession::smsCode, this, [&](bool showSkipSecondaryAuthOption) {
addLog("需要短信验证码");
qInfo().noquote() << "需要短信验证码";
QDialog smsCodeDialog(this);
smsCodeDialog.setWindowTitle("短信验证码");
@@ -93,11 +95,12 @@ void MainWindow::initZjuConnect()
if (smsCodeAccepted)
{
addLog(skipSecondaryAuth ? "短信验证码已提交(跳过以后的短信验证)" : "短信验证码已提交");
qInfo().noquote()
<< (skipSecondaryAuth ? "短信验证码已提交(跳过以后的短信验证)" : "短信验证码已提交");
}
else
{
addLog("短信验证码输入已取消");
qInfo().noquote() << "短信验证码输入已取消";
}
QByteArray smsCodeInput = smsCode.toLocal8Bit();
if (skipSecondaryAuth)
@@ -108,11 +111,11 @@ void MainWindow::initZjuConnect()
});
connect(connectionSession, &ConnectionSession::totpCode, this, [&]() {
addLog("需要 TOTP 验证码");
qInfo().noquote() << "需要 TOTP 验证码";
bool accepted = false;
QString totp = QInputDialog::getText(
this, "TOTP 验证码", "请输入 TOTP 验证码:", QLineEdit::Normal, "", &accepted);
addLog(accepted ? "TOTP 验证码已提交" : "TOTP 验证码输入已取消");
qInfo().noquote() << (accepted ? "TOTP 验证码已提交" : "TOTP 验证码输入已取消");
connectionSession->submitInput(totp.toLocal8Bit() + "\n");
});
@@ -134,7 +137,7 @@ void MainWindow::initZjuConnect()
if (ssoUrl.startsWith("/"))
ssoUrl = "https://" + serverHost + ssoUrl;
addLog(QStringLiteral("单点登录:") + ssoUrl);
qInfo().noquote() << QStringLiteral("单点登录:") + ssoUrl;
ssoLoginWebView->setInitialUrl(QUrl::fromUserInput(ssoUrl));
ssoLoginWebView->setCallbackServerHost(serverHost);
ssoLoginWebView->show();
@@ -142,12 +145,12 @@ void MainWindow::initZjuConnect()
connect(connectionSession, &ConnectionSession::reconnectScheduled, this, [&](int)
{
addLog("正在尝试重新连接...");
qInfo().noquote() << "正在尝试重新连接...";
});
connect(connectionSession, &ConnectionSession::finished, this, [&](ZJU_ERROR error)
{
addLog("VPN 断开!");
qInfo().noquote() << "VPN 断开!";
if (error != ZJU_ERROR::NONE)
{
showNotification("VPN", "VPN 意外断开!", QSystemTrayIcon::MessageIcon::Warning);
@@ -322,7 +325,7 @@ void MainWindow::initZjuConnect()
).toBool();
if (suppressed)
{
addLog("跳过系统代理覆盖警告,因为已设置了不再提示");
qInfo().noquote() << "跳过系统代理覆盖警告,因为已设置了不再提示";
}
else
{
@@ -350,10 +353,9 @@ void MainWindow::initZjuConnect()
}
}
addLog(
"设置系统代理HTTP端口 " + QString::number(http_port)
+ "SOCKS5 端口 " + QString::number(socks_port)
);
qInfo().noquote()
<< "设置系统代理HTTP端口 " + QString::number(http_port)
+ "SOCKS5 端口 " + QString::number(socks_port);
systemProxySession->enable(proxyConfig);
},
Qt::SingleShotConnection);