Skip to content

Commit b06101c

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 b06101c

File tree

3 files changed

+139
-5
lines changed

3 files changed

+139
-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: 137 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 = fs::current_path();
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,97 @@ 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+
std::stringstream ssArchiveCheckDate, ssPrevArchiveCheckDate;
204+
205+
ssArchiveCheckDate << ArchiveCheckDate;
206+
ssPrevArchiveCheckDate << PrevArchiveCheckDate;
207+
208+
if (fDebug) LogPrintf("INFO: Scraper: Logger: ArchiveCheckDate %s, PrevArchiveCheckDate %s", ssArchiveCheckDate.str(), ssPrevArchiveCheckDate.str());
209+
210+
if (fImmediate || ArchiveCheckDate > PrevArchiveCheckDate)
211+
{
212+
{
213+
LOCK(cs_log);
214+
215+
if (logfile.is_open())
216+
{
217+
logfile.flush();
218+
logfile.close();
219+
}
220+
221+
plogfile = pathDataDir / "scraper.log";
222+
pfile_temp = pathDataDir / ("scraper-" + DateTimeStrFormat("%Y%m%d%H%M%S", nTime) + ".log");
223+
pfile_out = pathDataDir / ("scraper-" + DateTimeStrFormat("%Y%m%d%H%M%S", nTime) + ".log.gz");
224+
225+
try
226+
{
227+
fs::rename(plogfile, pfile_temp);
228+
}
229+
catch(...)
230+
{
231+
LogPrintf("ERROR: Scraper: Logger: Failed to rename logging file\n");
232+
return false;
233+
}
234+
235+
PrevArchiveCheckDate = ArchiveCheckDate;
236+
}
237+
238+
std::ifstream infile(pfile_temp.string().c_str(), std::ios_base::in | std::ios_base::binary);
239+
240+
if (!infile)
241+
{
242+
LogPrintf("ERROR: logger: Failed to open archive log file for compression %s.", pfile_temp.string());
243+
return false;
244+
}
245+
246+
std::ofstream outgzfile(pfile_out.string().c_str(), std::ios_base::out | std::ios_base::binary);
247+
248+
if (!outgzfile)
249+
{
250+
LogPrintf("ERROR: logger: Failed to open archive gzip file %s.", pfile_out.string());
251+
return false;
252+
}
253+
254+
boostio::filtering_ostream out;
255+
out.push(boostio::gzip_compressor());
256+
out.push(outgzfile);
257+
258+
boost::iostreams::copy(infile, out);
259+
260+
infile.close();
261+
outgzfile.flush();
262+
outgzfile.close();
263+
264+
fs::remove(pfile_temp);
265+
266+
return true;
267+
}
268+
else
269+
return false;
270+
}
180271
};
181272

273+
boost::gregorian::date logger::PrevArchiveCheckDate = boost::posix_time::from_time_t(GetAdjustedTime()).date();
274+
CCriticalSection logger::cs_log;
275+
276+
182277

183278
void _log(logattribute eType, const std::string& sCall, const std::string& sMessage)
184279
{
@@ -555,6 +650,7 @@ void Scraper(bool bSingleShot)
555650
_log(logattribute::INFO, "Scraper", "Running in single shot mode.");
556651

557652
// This is necessary to maintain compatibility with Windows.
653+
pathDataDir.imbue(std::locale(std::locale(), new std::codecvt_utf8_utf16<wchar_t>()));
558654
pathScraper.imbue(std::locale(std::locale(), new std::codecvt_utf8_utf16<wchar_t>()));
559655

560656
// Hash check
@@ -643,6 +739,15 @@ void Scraper(bool bSingleShot)
643739
if (fDebug3) _log(logattribute::INFO, "ENDLOCK", "cs_Scraper");
644740
}
645741

742+
// Need the log archive check here, because we don't run housekeeping in this while loop.
743+
logger log;
744+
745+
fs::path plogfile_out;
746+
747+
if (log.archive(false, plogfile_out))
748+
_log(logattribute::INFO, "Scraper", "Archived scraper.log to " + plogfile_out.string());
749+
750+
646751
sbage = data.sbage();
647752
_log(logattribute::INFO, "Scraper", "Superblock not needed. age=" + std::to_string(sbage));
648753
_log(logattribute::INFO, "Scraper", "Sleeping for " + std::to_string(nScraperSleep / 1000) +" seconds");
@@ -932,6 +1037,13 @@ bool ScraperHousekeeping()
9321037
+ ", Popularity: " + std::to_string(network_hash.second));
9331038
}
9341039

1040+
logger log;
1041+
1042+
fs::path plogfile_out;
1043+
1044+
if (log.archive(false, plogfile_out))
1045+
_log(logattribute::INFO, "ScraperHousekeeping", "Archived scraper.log to " + plogfile_out.string());
1046+
9351047
return true;
9361048
}
9371049

@@ -4229,3 +4341,24 @@ UniValue deletecscrapermanifest(const UniValue& params, bool fHelp)
42294341
}
42304342

42314343

4344+
UniValue archivescraperlog(const UniValue& params, bool fHelp)
4345+
{
4346+
if (fHelp || params.size() != 0 )
4347+
throw std::runtime_error(
4348+
"archivescraperlog takes no arguments and results in immediate archiving of the scraper log\n"
4349+
);
4350+
4351+
logger log;
4352+
4353+
fs::path pfile_out;
4354+
bool ret = log.archive(true, pfile_out);
4355+
4356+
if(!ret)
4357+
return UniValue(ret);
4358+
else
4359+
return UniValue(pfile_out.c_str());
4360+
4361+
return UniValue(ret);
4362+
}
4363+
4364+

0 commit comments

Comments
 (0)