Skip to content

Commit 8bb9f8c

Browse files
committed
scraper.log archiving
This commit implements a simple log archiver for the scraper log. If people like this I will do something similar for the main log as well. Currently it runs in the housekeeping loop and automatically renames the scraper.log to scraper-YYYYMMDDHHMMSS.log then compresses the archive log to a gz file. A lock is held only during the rename, and a new scraper.log is started immediately. Stream compression (just like the rest of the scraper) is used to minimize memory consumption. I have also provided an rpc command, archivescraperlog, which takes no arguments and can be used to immediately roll the current log to an archive file and start a new one non-disruptively.
1 parent ed8b51f commit 8bb9f8c

File tree

3 files changed

+123
-5
lines changed

3 files changed

+123
-5
lines changed

src/rpcserver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ static const CRPCCommand vRPCCommands[] =
399399
{ "sendscraperfilemanifest", &sendscraperfilemanifest, false, cat_developer },
400400
{ "savescraperfilemanifest", &savescraperfilemanifest, false, cat_developer },
401401
{ "deletecscrapermanifest", &deletecscrapermanifest, false, cat_developer },
402+
{ "archivescraperlog", &archivescraperlog, false, cat_developer },
402403

403404
// Network commands
404405
{ "addnode", &addnode, false, cat_network },

src/rpcserver.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ extern UniValue getmpart(const UniValue& params, bool fHelp);
219219
extern UniValue sendscraperfilemanifest(const UniValue& params, bool fHelp);
220220
extern UniValue savescraperfilemanifest(const UniValue& params, bool fHelp);
221221
extern UniValue deletecscrapermanifest(const UniValue& params, bool fHelp);
222-
222+
extern UniValue archivescraperlog(const UniValue& params, bool fHelp);
223223

224224
// Network
225225
extern UniValue addnode(const UniValue& params, bool fHelp);

src/scraper/scraper.cpp

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@
1414
#include <boost/exception/diagnostic_information.hpp>
1515
#include <boost/iostreams/stream.hpp>
1616
#include <boost/iostreams/device/array.hpp>
17+
#include <boost/date_time.hpp>
18+
#include <boost/date_time/gregorian/gregorian.hpp>
19+
#include <boost/date_time/gregorian/greg_date.hpp>
1720

18-
fs::path pathScraper = fs::current_path() / "Scraper";
21+
fs::path pathDataDir = GetDataDir();
22+
fs::path pathScraper = pathDataDir / "Scraper";
1923

2024
extern bool fShutdown;
2125
extern bool fDebug;
@@ -144,25 +148,32 @@ extern std::string PackBinarySuperblock(std::string sBlock);
144148

145149
class logger
146150
{
151+
147152
private:
148153

149-
//std::ofstream logfile;
154+
static CCriticalSection cs_log;
155+
156+
static boost::gregorian::date PrevArchiveCheckDate;
157+
150158
fs::ofstream logfile;
151159

152160
public:
153161

154162
logger()
155163
{
156-
fs::path plogfile = GetDataDir() / "scraper.log";
164+
LOCK(cs_log);
157165

166+
fs::path plogfile = pathDataDir / "scraper.log";
158167
logfile.open(plogfile.c_str(), std::ios_base::out | std::ios_base::app);
159168

160169
if (!logfile.is_open())
161-
printf("Logging : Failed to open logging file\n");
170+
LogPrintf("ERROR: Scraper: Logger: Failed to open logging file\n");
162171
}
163172

164173
~logger()
165174
{
175+
LOCK(cs_log);
176+
166177
if (logfile.is_open())
167178
{
168179
logfile.flush();
@@ -172,13 +183,90 @@ class logger
172183

173184
void output(const std::string& tofile)
174185
{
186+
LOCK(cs_log);
187+
175188
if (logfile.is_open())
176189
logfile << tofile << std::endl;
177190

178191
return;
179192
}
193+
194+
195+
196+
bool archive(bool fImmediate, fs::path pfile_out)
197+
{
198+
int64_t nTime = GetAdjustedTime();
199+
boost::gregorian::date ArchiveCheckDate = boost::posix_time::from_time_t(nTime).date();
200+
fs::path plogfile;
201+
fs::path pfile_temp;
202+
203+
if (fImmediate || ArchiveCheckDate > PrevArchiveCheckDate)
204+
{
205+
{
206+
LOCK(cs_log);
207+
208+
if (logfile.is_open())
209+
{
210+
logfile.flush();
211+
logfile.close();
212+
}
213+
214+
plogfile = pathDataDir / "scraper.log";
215+
pfile_temp = pathDataDir / ("scraper-" + DateTimeStrFormat("%Y%m%d%H%M%S", nTime) + ".log");
216+
pfile_out = pathDataDir / ("scraper-" + DateTimeStrFormat("%Y%m%d%H%M%S", nTime) + ".log.gz");
217+
218+
try
219+
{
220+
fs::rename(plogfile, pfile_temp);
221+
}
222+
catch(...)
223+
{
224+
LogPrintf("ERROR: Scraper: Logger: Failed to rename logging file\n");
225+
return false;
226+
}
227+
228+
PrevArchiveCheckDate = ArchiveCheckDate;
229+
}
230+
231+
std::ifstream infile(pfile_temp.string().c_str(), std::ios_base::in | std::ios_base::binary);
232+
233+
if (!infile)
234+
{
235+
LogPrintf("ERROR: logger: Failed to open archive log file for compression %s.", pfile_temp.string());
236+
return false;
237+
}
238+
239+
std::ofstream outgzfile(pfile_out.string().c_str(), std::ios_base::out | std::ios_base::binary);
240+
241+
if (!outgzfile)
242+
{
243+
LogPrintf("ERROR: logger: Failed to open archive gzip file %s.", pfile_out.string());
244+
return false;
245+
}
246+
247+
boostio::filtering_ostream out;
248+
out.push(boostio::gzip_compressor());
249+
out.push(outgzfile);
250+
251+
boost::iostreams::copy(infile, out);
252+
253+
infile.close();
254+
outgzfile.flush();
255+
outgzfile.close();
256+
257+
fs::remove(pfile_temp);
258+
259+
return true;
260+
}
261+
else
262+
return false;
263+
}
180264
};
181265

266+
boost::gregorian::date logger::PrevArchiveCheckDate = boost::posix_time::from_time_t(GetAdjustedTime()).date();
267+
CCriticalSection logger::cs_log;
268+
269+
182270

183271
void _log(logattribute eType, const std::string& sCall, const std::string& sMessage)
184272
{
@@ -555,6 +643,7 @@ void Scraper(bool bSingleShot)
555643
_log(logattribute::INFO, "Scraper", "Running in single shot mode.");
556644

557645
// This is necessary to maintain compatibility with Windows.
646+
pathDataDir.imbue(std::locale(std::locale(), new std::codecvt_utf8_utf16<wchar_t>()));
558647
pathScraper.imbue(std::locale(std::locale(), new std::codecvt_utf8_utf16<wchar_t>()));
559648

560649
// Hash check
@@ -932,6 +1021,13 @@ bool ScraperHousekeeping()
9321021
+ ", Popularity: " + std::to_string(network_hash.second));
9331022
}
9341023

1024+
logger log;
1025+
1026+
fs::path plogfile_out;
1027+
1028+
if (log.archive(false, plogfile_out))
1029+
_log(logattribute::INFO, "ScraperHousekeeping", "Archived scraper.log to " + plogfile_out.string());
1030+
9351031
return true;
9361032
}
9371033

@@ -4229,3 +4325,24 @@ UniValue deletecscrapermanifest(const UniValue& params, bool fHelp)
42294325
}
42304326

42314327

4328+
UniValue archivescraperlog(const UniValue& params, bool fHelp)
4329+
{
4330+
if (fHelp || params.size() != 0 )
4331+
throw std::runtime_error(
4332+
"archivescraperlog takes no arguments and results in immediate archiving of the scraper log\n"
4333+
);
4334+
4335+
logger log;
4336+
4337+
fs::path pfile_out;
4338+
bool ret = log.archive(true, pfile_out);
4339+
4340+
if(!ret)
4341+
return UniValue(ret);
4342+
else
4343+
return UniValue(pfile_out.c_str());
4344+
4345+
return UniValue(ret);
4346+
}
4347+
4348+

0 commit comments

Comments
 (0)