summaryrefslogtreecommitdiff
path: root/ui/batch_status_handler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ui/batch_status_handler.cpp')
-rw-r--r--ui/batch_status_handler.cpp192
1 files changed, 68 insertions, 124 deletions
diff --git a/ui/batch_status_handler.cpp b/ui/batch_status_handler.cpp
index 89d28084..c0958025 100644
--- a/ui/batch_status_handler.cpp
+++ b/ui/batch_status_handler.cpp
@@ -5,10 +5,8 @@
// **************************************************************************
#include "batch_status_handler.h"
-#include <wx/ffile.h>
#include <zen/file_handling.h>
#include <zen/file_traverser.h>
-#include <wx+/string_conv.h>
#include <wx+/app_main.h>
#include <wx+/format_unit.h>
#include <wx+/shell_execute.h>
@@ -17,6 +15,7 @@
#include "../lib/ffs_paths.h"
#include "../lib/resolve_path.h"
#include "../lib/status_handler_impl.h"
+#include "../lib/generate_logfile.h"
using namespace zen;
@@ -44,130 +43,62 @@ private:
const Zstring prefix_;
std::vector<Zstring>& logfiles_;
};
-}
-class LogFile //throw FileError
+void limitLogfileCount(const Zstring& logdir, const std::wstring& jobname, size_t maxCount) //throw()
{
-public:
- LogFile(const Zstring& logfileDirectory,
- const std::wstring& jobName,
- const std::wstring& timestamp) :
- jobName_(jobName), //throw FileError
- logfileName(findUnusedLogname(logfileDirectory, jobName, timestamp))
- {
- logFile.Open(toWx(logfileName), L"w");
- if (!logFile.IsOpened())
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(logfileName)));
+ std::vector<Zstring> logFiles;
+ FindLogfiles traverseCallback(toZ(jobname), logFiles);
- //write header
- const wxString& headerLine = wxString(L"FreeFileSync - ") + _("Batch execution") + L" - " + formatTime<wxString>(FORMAT_DATE);
- logFile.Write(headerLine + L'\n');
- logFile.Write(wxString().Pad(headerLine.Len(), L'=') + L'\n');
+ traverseFolder(logdir, //throw();
+ traverseCallback);
- //logItemStart = formatTime<wxString>(L"[%X] ") + _("Start");
+ if (logFiles.size() <= maxCount)
+ return;
- totalTime.Start(); //measure total time
- }
+ //delete oldest logfiles
+ std::nth_element(logFiles.begin(), logFiles.end() - maxCount, logFiles.end()); //take advantage of logfile naming convention to find oldest files
- void writeLog(const ErrorLog& log, const std::wstring& finalStatus,
- int itemsSynced, Int64 dataSynced,
- int itemsTotal, Int64 dataTotal)
- {
- //assemble results box
- std::vector<wxString> results;
- results.push_back(finalStatus);
- results.push_back(L"");
- if (itemsTotal != 0 || dataTotal != 0) //=: sync phase was reached and there were actual items to sync
- {
- results.push_back(L" " + _("Items processed:") + L" " + toGuiString(itemsSynced) + L" (" + filesizeToShortString(dataSynced) + L")");
+ std::for_each(logFiles.begin(), logFiles.end() - maxCount,
+ [](const Zstring& filename) { try { removeFile(filename); } catch (FileError&) {} });
+}
- if (itemsSynced != itemsTotal ||
- dataSynced != dataTotal)
- results.push_back(L" " + _("Items remaining:") + L" " + toGuiString(itemsTotal - itemsSynced) + L" (" + filesizeToShortString(dataTotal - dataSynced) + L")");
- }
- results.push_back(L" " + _("Total time:") + L" " + wxTimeSpan::Milliseconds(totalTime.Time()).Format());
- //write results box
- size_t sepLineLen = 0;
- std::for_each(results.begin(), results.end(), [&](const wxString& str) { sepLineLen = std::max(sepLineLen, str.size()); });
+std::unique_ptr<FileOutput> prepareNewLogfile(const Zstring& logfileDirectory, //throw FileError
+ const std::wstring& jobName,
+ const std::wstring& timestamp) //return value always bound!
+{
+ //create logfile directory if required
+ Zstring logfileDir = logfileDirectory.empty() ?
+ getConfigDir() + Zstr("Logs") :
+ getFormattedDirectoryName(logfileDirectory);
- logFile.Write(wxString().Pad(sepLineLen, L'_') + L"\n\n");
- std::for_each(results.begin(), results.end(), [&](const wxString& str) { logFile.Write(str + L'\n'); });
- logFile.Write(wxString().Pad(sepLineLen, L'_') + L"\n\n");
+ makeDirectory(logfileDir); //throw FileError
- //logFile.Write(logItemStart + L"\n\n");
+ //assemble logfile name
+ const Zstring body = appendSeparator(logfileDir) + toZ(jobName) + Zstr(" ") + utfCvrtTo<Zstring>(timestamp);
- //write log items
- const auto& entries = log.getEntries();
- for (auto iter = entries.begin(); iter != entries.end(); ++iter)
+ //ensure uniqueness
+ for (int i = 0;; ++i)
+ try
{
- const std::string& msg = utfCvrtTo<std::string>(formatMessage(*iter));
- logFile.Write(msg.c_str(), msg.size()); //better do UTF8 conversion ourselves rather than to rely on wxWidgets
- logFile.Write(L'\n');
- }
+ const Zstring& filename = i == 0 ?
+ body + Zstr(".log") :
+ body + Zstr('_') + numberTo<Zstring>(i) + Zstr(".log");
- ////write footer
- //logFile.Write(L'\n');
- //logFile.Write(formatTime<wxString>(L"[%X] ") + _("Stop") + L" (" + _("Total time:") + L" " + wxTimeSpan::Milliseconds(totalTime.Time()).Format() + L")\n");
- }
-
- void limitLogfileCount(size_t maxCount) const //throw()
- {
- std::vector<Zstring> logFiles;
- FindLogfiles traverseCallback(toZ(jobName_), logFiles);
-
- traverseFolder(beforeLast(logfileName, FILE_NAME_SEPARATOR), //throw();
- traverseCallback);
-
- if (logFiles.size() <= maxCount)
- return;
-
- //delete oldest logfiles
- std::nth_element(logFiles.begin(), logFiles.end() - maxCount, logFiles.end()); //take advantage of logfile naming convention to find oldest files
-
- std::for_each(logFiles.begin(), logFiles.end() - maxCount,
- [](const Zstring& filename) { try { removeFile(filename); } catch (FileError&) {} });
- }
-
- //Zstring getLogfileName() const { return logfileName; }
-
-private:
- static Zstring findUnusedLogname(const Zstring& logfileDirectory,
- const std::wstring& jobName,
- const std::wstring& timestamp)
- {
- //create logfile directory
- Zstring logfileDir = logfileDirectory.empty() ?
- getConfigDir() + Zstr("Logs") :
- getFormattedDirectoryName(logfileDirectory);
-
- if (!dirExists(logfileDir))
- createDirectory(logfileDir); //throw FileError; create recursively if necessary
-
- //assemble logfile name
- const Zstring logfileName = appendSeparator(logfileDir) + toZ(jobName) + Zstr(" ") + utfCvrtTo<Zstring>(timestamp);
-
- //ensure uniqueness
- Zstring output = logfileName + Zstr(".log");
-
- for (int i = 1; somethingExists(output); ++i)
- output = logfileName + Zstr('_') + numberTo<Zstring>(i) + Zstr(".log");
- return output;
- }
-
- const wxString jobName_;
- const Zstring logfileName;
- wxFFile logFile;
- wxStopWatch totalTime;
-};
+ return make_unique<FileOutput>(filename, FileOutput::ACC_CREATE_NEW); //throw FileError, ErrorTargetExisting
+ //*no* file system race-condition!
+ }
+ catch (const ErrorTargetExisting&) {}
+}
+}
//##############################################################################################################################
BatchStatusHandler::BatchStatusHandler(bool showProgress,
const std::wstring& jobName,
const std::wstring& timestamp,
- const wxString& logfileDirectory,
+ const wxString& logfileDirectory, //may be empty
size_t logFileCountMax,
const xmlAccess::OnError handleError,
const SwitchToGui& switchBatchToGui, //functionality to change from batch mode to GUI mode
@@ -179,19 +110,23 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress,
switchToGuiRequested(false),
handleError_(handleError),
returnCode_(returnCode),
- syncStatusFrame(*this, *this, nullptr, showProgress, jobName, execWhenFinished, execFinishedHistory)
+ syncStatusFrame(*this, *this, nullptr, showProgress, jobName, execWhenFinished, execFinishedHistory),
+ jobName_(jobName)
{
if (logFileCountMax > 0) //init log file: starts internal timer!
if (!tryReportingError([&]
{
- logFile.reset(new LogFile(toZ(logfileDirectory), jobName, timestamp)); //throw FileError
- logFile->limitLogfileCount(logFileCountMax); //throw()
+ logFile = prepareNewLogfile(toZ(logfileDirectory), jobName, timestamp); //throw FileError; return value always bound!
+
+ limitLogfileCount(beforeLast(logFile->getFilename(), FILE_NAME_SEPARATOR), jobName_, logFileCountMax); //throw()
}, *this))
{
returnCode_ = FFS_RC_ABORTED;
throw BatchAbortProcess();
}
+ totalTime.Start(); //measure total time
+
//::wxSetEnv(L"logfile", logFile->getLogfileName());
}
@@ -224,14 +159,26 @@ BatchStatusHandler::~BatchStatusHandler()
errorLog.logMsg(finalStatus, TYPE_INFO);
}
+ const Utf8String logStream = generateLogStream(errorLog, jobName_, finalStatus,
+ getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING),
+ getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING), totalTime.Time() / 1000);
//print the results list: logfile
if (logFile.get())
{
- logFile->writeLog(errorLog, finalStatus,
- getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING),
- getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING));
+ try
+ {
+ if (!logStream.empty())
+ logFile->write(&*logStream.begin(), logStream.size()); //throw FileError
+ }
+ catch (FileError&) {}
+
logFile.reset(); //close file now: user may do something with it in "on completion"
}
+ try
+ {
+ saveToLastSyncsLog(logStream); //throw FileError
+ }
+ catch (FileError&) {}
//decide whether to stay on status screen or exit immediately...
if (switchToGuiRequested) //-> avoid recursive yield() calls, thous switch not before ending batch mode
@@ -326,11 +273,11 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool&
bool dontWarnAgain = false;
switch (showWarningDlg(syncStatusFrame.getAsWindow(),
- ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_SWITCH | ReturnWarningDlg::BUTTON_ABORT,
+ ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_SWITCH | ReturnWarningDlg::BUTTON_CANCEL,
warningMessage + L"\n\n" + _("Press \"Switch\" to resolve issues in FreeFileSync main dialog."),
dontWarnAgain))
{
- case ReturnWarningDlg::BUTTON_ABORT:
+ case ReturnWarningDlg::BUTTON_CANCEL:
abortThisProcess();
break;
@@ -368,7 +315,7 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er
bool ignoreNextErrors = false;
switch (showErrorDlg(syncStatusFrame.getAsWindow(),
- ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_ABORT,
+ ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_CANCEL,
errorMessage, &ignoreNextErrors))
{
case ReturnErrorDlg::BUTTON_IGNORE:
@@ -380,7 +327,7 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er
case ReturnErrorDlg::BUTTON_RETRY:
return ProcessCallback::RETRY;
- case ReturnErrorDlg::BUTTON_ABORT:
+ case ReturnErrorDlg::BUTTON_CANCEL:
errorLog.logMsg(errorMessage, TYPE_ERROR);
abortThisProcess();
}
@@ -414,21 +361,18 @@ void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage)
forceUiRefresh();
bool ignoreNextErrors = false;
- switch (showErrorDlg(syncStatusFrame.getAsWindow(),
- ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_ABORT,
- errorMessage, &ignoreNextErrors))
+ switch (showFatalErrorDlg(syncStatusFrame.getAsWindow(),
+ ReturnFatalErrorDlg::BUTTON_IGNORE | ReturnFatalErrorDlg::BUTTON_CANCEL,
+ errorMessage, &ignoreNextErrors))
{
- case ReturnErrorDlg::BUTTON_IGNORE:
+ case ReturnFatalErrorDlg::BUTTON_IGNORE:
if (ignoreNextErrors) //falsify only
handleError_ = xmlAccess::ON_ERROR_IGNORE;
break;
- case ReturnErrorDlg::BUTTON_ABORT:
+ case ReturnFatalErrorDlg::BUTTON_CANCEL:
abortThisProcess();
break;
-
- case ReturnErrorDlg::BUTTON_RETRY:
- assert(false);
}
}
break;
bgstack15