summaryrefslogtreecommitdiff
path: root/lib/generate_logfile.h
blob: 31f7bd437f561612014488044826b23a176800d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// **************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl.html       *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
// **************************************************************************

#ifndef GEN_LOGFILE_H_93172643216748973216458732165415
#define GEN_LOGFILE_H_93172643216748973216458732165415

#include <zen/error_log.h>
#include <zen/file_io.h>
#include <zen/serialize.h>
#include <zen/format_unit.h>
#include "ffs_paths.h"


namespace zen
{
struct SummaryInfo
{
    std::wstring jobName; //may be empty
    std::wstring finalStatus;
    int itemsSynced;
    Int64 dataSynced;
    int itemsTotal;
    Int64 dataTotal;
    long totalTime; //unit: [sec]
};

void saveLogToFile(const SummaryInfo& summary, //throw FileError
                   const ErrorLog& log,
                   FileOutput& fileOut);

void saveToLastSyncsLog(const SummaryInfo& summary,  //throw FileError
                        const ErrorLog& log,
                        size_t maxBytesToWrite);





//####################### implementation #######################
namespace
{
std::wstring generateLogHeader(const SummaryInfo& s)
{
    assert(s.itemsSynced <= s.itemsTotal);
    assert(s.dataSynced  <= s.dataTotal);

    std::wstring output;

    //write header
    std::wstring headerLine = formatTime<std::wstring>(FORMAT_DATE);
    if (!s.jobName.empty())
        headerLine += L" - " + s.jobName;
    headerLine += L": " + s.finalStatus;

    //assemble results box
    std::vector<std::wstring> results;
    results.push_back(headerLine);
    results.push_back(L"");

    const wchar_t tabSpace[] = L"    ";

    std::wstring itemsProc = tabSpace + _("Items processed:") + L" " + toGuiString(s.itemsSynced); //show always, even if 0!
    if (s.itemsSynced != 0 || s.dataSynced != 0) //[!] don't show 0 bytes processed if 0 items were processed
        itemsProc += + L" (" + filesizeToShortString(s.dataSynced) + L")";
    results.push_back(itemsProc);

    if (s.itemsTotal != 0 || s.dataTotal != 0) //=: sync phase was reached and there were actual items to sync
    {
        if (s.itemsSynced != s.itemsTotal ||
            s.dataSynced  != s.dataTotal)
            results.push_back(tabSpace + _("Items remaining:") + L" " + toGuiString(s.itemsTotal - s.itemsSynced) + L" (" + filesizeToShortString(s.dataTotal - s.dataSynced) + L")");
    }

    results.push_back(tabSpace + _("Total time:") + L" " + copyStringTo<std::wstring>(wxTimeSpan::Seconds(s.totalTime).Format()));

    //calculate max width, this considers UTF-16 only, not true Unicode...but maybe good idea? those 2-char-UTF16 codes are usually wider than fixed width chars anyway!
    size_t sepLineLen = 0;
    std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { sepLineLen = std::max(sepLineLen, str.size()); });

    output.resize(output.size() + sepLineLen + 1, L'_');
    output += L'\n';

    std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { output += L'|'; output += str; output += L'\n'; });

    output += L'|';
    output.resize(output.size() + sepLineLen, L'_');
    output += L'\n';

    return output;
}
}


inline
void saveLogToFile(const SummaryInfo& summary, //throw FileError
                   const ErrorLog& log,
                   FileOutput& fileOut)
{
    Utf8String header = utfCvrtTo<Utf8String>(generateLogHeader(summary));
    replace(header, '\n', LINE_BREAK); //don't replace line break any earlier
    header += LINE_BREAK; //make sure string is not empty!

    fileOut.write(&*header.begin(), header.size()); //throw FileError

    //write log items one after the other instead of creating one big string: memory allocation might fail; think 1 million entries!
    for (auto iter = log.begin(); iter != log.end(); ++iter)
    {
        Utf8String msg = replaceCpy(utfCvrtTo<Utf8String>(formatMessage<std::wstring>(*iter)), '\n', LINE_BREAK);
        msg += LINE_BREAK; //make sure string is not empty!

        fileOut.write(&*msg.begin(), msg.size()); //throw FileError
    }
}


inline
void saveToLastSyncsLog(const SummaryInfo& summary,  //throw FileError
                        const ErrorLog& log,
                        size_t maxBytesToWrite) //log may be *huge*, e.g. 1 million items; LastSyncs.log *must not* create performance problems!
{
    const Zstring filename = getConfigDir() + Zstr("LastSyncs.log");

    Utf8String newStream = utfCvrtTo<Utf8String>(generateLogHeader(summary));
    replace(newStream, '\n', LINE_BREAK); //don't replace line break any earlier
    newStream += LINE_BREAK;

    //check size of "newStream": memory allocation might fail - think 1 million entries!
    for (auto iter = log.begin(); iter != log.end(); ++iter)
    {
        newStream += replaceCpy(utfCvrtTo<Utf8String>(formatMessage<std::wstring>(*iter)), '\n', LINE_BREAK);
        newStream += LINE_BREAK;

        if (newStream.size() > maxBytesToWrite)
        {
            newStream += "[...]";
            newStream += LINE_BREAK;
            break;
        }
    }

    //fill up the rest of permitted space by appending old log
    if (newStream.size() < maxBytesToWrite)
    {
        Utf8String oldStream;
        try
        {
            oldStream = loadBinStream<Utf8String>(filename); //throw FileError, ErrorNotExisting
        }
        catch (const ErrorNotExisting&) {}

        if (!oldStream.empty())
        {
            newStream += LINE_BREAK;
            newStream += LINE_BREAK;
            newStream += oldStream; //impliticly limited by "maxBytesToWrite"!

            //truncate size if required
            if (newStream.size() > maxBytesToWrite)
            {
                //but do not cut in the middle of a row
                auto iter = std::search(newStream.cbegin() + maxBytesToWrite, newStream.cend(), std::begin(LINE_BREAK), std::end(LINE_BREAK) - 1);
                if (iter != newStream.cend())
                {
                    newStream.resize(iter - newStream.cbegin());
                    newStream += LINE_BREAK;

                    newStream += "[...]";
                    newStream += LINE_BREAK;
                }
            }
        }
    }

    saveBinStream(filename, newStream); //throw FileError
}
}

#endif //GEN_LOGFILE_H_93172643216748973216458732165415
bgstack15