Merge #12567: util: Print timestamp strings in logs using ISO 8601 formatting

a7324bd79 Format timestamps using ISO 8601 formatting (e.g. "2018-02-28T12:34:56Z") (practicalswift)

Pull request description:

  Print timestamp strings in logs using [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatting (e.g. `2018-02-28T12:34:56Z`):
  * `Z` is the zone designator for the zero [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time) offset.
  * `T` is the delimiter used to separate date and time.

  This makes it clear for the end-user that the date/time logged is specified in UTC and not in the local time zone.

  Before this patch:

  ```
  2018-02-28 12:34:56 New outbound peer connected: version: 70015, blocks=1286123, peer=0
  ```

  After this patch:

  ```
  2018-02-28T12:34:56Z New outbound peer connected: version: 70015, blocks=1286123, peer=0
  ```

Tree-SHA512: 52b53c3d3d11ddf4af521a3b5f90a79f6e6539ee5955ec56a5aa2c6a5cf29cecf166d8cb43277c62553c3325a31bcea83691acbb4e86429c523f8aff8d7b210a
Signed-off-by: pasta <pasta@dashboost.org>
This commit is contained in:
Wladimir J. van der Laan 2018-03-10 14:08:15 +01:00 committed by pasta
parent 666f6459cf
commit 2564c17330
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984
9 changed files with 51 additions and 19 deletions

View File

@ -1679,7 +1679,7 @@ bool AppInitMain()
} }
if (!fLogTimestamps) if (!fLogTimestamps)
LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime())); LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime()));
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
LogPrintf("Using data directory %s\n", GetDataDir().string()); LogPrintf("Using data directory %s\n", GetDataDir().string());
LogPrintf("Using config file %s\n", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string()); LogPrintf("Using config file %s\n", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string());

View File

@ -755,7 +755,7 @@ bool PaymentServer::verifyExpired(const payments::PaymentDetails& requestDetails
{ {
bool fVerified = (requestDetails.has_expires() && (int64_t)requestDetails.expires() < GetTime()); bool fVerified = (requestDetails.has_expires() && (int64_t)requestDetails.expires() < GetTime());
if (fVerified) { if (fVerified) {
const QString requestExpires = QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", (int64_t)requestDetails.expires())); const QString requestExpires = QString::fromStdString(FormatISO8601DateTime((int64_t)requestDetails.expires()));
qWarning() << QString("PaymentServer::%1: Payment request expired \"%2\".") qWarning() << QString("PaymentServer::%1: Payment request expired \"%2\".")
.arg(__func__) .arg(__func__)
.arg(requestExpires); .arg(requestExpires);

View File

@ -164,10 +164,27 @@ BOOST_AUTO_TEST_CASE(util_DateTimeStrFormat)
BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0), "1970-01-01 00:00:00"); BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0), "1970-01-01 00:00:00");
BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0x7FFFFFFF), "2038-01-19 03:14:07"); BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0x7FFFFFFF), "2038-01-19 03:14:07");
BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 1317425777), "2011-09-30 23:36:17"); BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 1317425777), "2011-09-30 23:36:17");
BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", 1317425777), "2011-09-30T23:36:17Z");
BOOST_CHECK_EQUAL(DateTimeStrFormat("%H:%M:%SZ", 1317425777), "23:36:17Z");
BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M", 1317425777), "2011-09-30 23:36"); BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M", 1317425777), "2011-09-30 23:36");
BOOST_CHECK_EQUAL(DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", 1317425777), "Fri, 30 Sep 2011 23:36:17 +0000"); BOOST_CHECK_EQUAL(DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", 1317425777), "Fri, 30 Sep 2011 23:36:17 +0000");
} }
BOOST_AUTO_TEST_CASE(util_FormatISO8601DateTime)
{
BOOST_CHECK_EQUAL(FormatISO8601DateTime(1317425777), "2011-09-30T23:36:17Z");
}
BOOST_AUTO_TEST_CASE(util_FormatISO8601Date)
{
BOOST_CHECK_EQUAL(FormatISO8601Date(1317425777), "2011-09-30");
}
BOOST_AUTO_TEST_CASE(util_FormatISO8601Time)
{
BOOST_CHECK_EQUAL(FormatISO8601Time(1317425777), "23:36:17Z");
}
struct TestArgsManager : public ArgsManager struct TestArgsManager : public ArgsManager
{ {
TestArgsManager() { m_network_only_args.clear(); } TestArgsManager() { m_network_only_args.clear(); }

View File

@ -98,3 +98,15 @@ std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime)
ss << boost::posix_time::from_time_t(nTime); ss << boost::posix_time::from_time_t(nTime);
return ss.str(); return ss.str();
} }
std::string FormatISO8601DateTime(int64_t nTime) {
return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
}
std::string FormatISO8601Date(int64_t nTime) {
return DateTimeStrFormat("%Y-%m-%d", nTime);
}
std::string FormatISO8601Time(int64_t nTime) {
return DateTimeStrFormat("%H:%M:%SZ", nTime);
}

View File

@ -34,6 +34,14 @@ void MilliSleep(int64_t n);
template <typename T> template <typename T>
T GetTime(); T GetTime();
/**
* ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date,Time}
* helper functions if possible.
*/
std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime); std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime);
std::string FormatISO8601DateTime(int64_t nTime);
std::string FormatISO8601Date(int64_t nTime);
std::string FormatISO8601Time(int64_t nTime);
#endif // BITCOIN_UTILTIME_H #endif // BITCOIN_UTILTIME_H

View File

@ -1376,13 +1376,12 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
LogPrintf("%s: invalid block=%s height=%d log2_work=%.8f date=%s\n", __func__, LogPrintf("%s: invalid block=%s height=%d log2_work=%.8f date=%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime()));
pindexNew->GetBlockTime()));
CBlockIndex *tip = chainActive.Tip(); CBlockIndex *tip = chainActive.Tip();
assert (tip); assert (tip);
LogPrintf("%s: current best=%s height=%d log2_work=%.8f date=%s\n", __func__, LogPrintf("%s: current best=%s height=%d log2_work=%.8f date=%s\n", __func__,
tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0), tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0),
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); FormatISO8601DateTime(tip->GetBlockTime()));
CheckForkWarningConditions(); CheckForkWarningConditions();
} }
@ -2675,7 +2674,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
std::string strMessage = strprintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__, std::string strMessage = strprintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime()), FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); GuessVerificationProgress(chainParams.TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
strMessage += strprintf(" evodb_cache=%.1fMiB", evoDb->GetMemoryUsage() * (1.0 / (1<<20))); strMessage += strprintf(" evodb_cache=%.1fMiB", evoDb->GetMemoryUsage() * (1.0 / (1<<20)));
if (!warningMessages.empty()) if (!warningMessages.empty())
@ -4371,7 +4370,7 @@ bool LoadChainTip(const CChainParams& chainparams)
LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n", LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n",
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(),
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()),
GuessVerificationProgress(chainparams.TxData(), chainActive.Tip())); GuessVerificationProgress(chainparams.TxData(), chainActive.Tip()));
return true; return true;
} }
@ -5016,7 +5015,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
} }
std::string CBlockFileInfo::ToString() const { std::string CBlockFileInfo::ToString() const {
return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date(nTimeFirst), FormatISO8601Date(nTimeLast));
} }
CBlockFileInfo* GetBlockFileInfo(size_t n) CBlockFileInfo* GetBlockFileInfo(size_t n)

View File

@ -27,10 +27,6 @@
#include <univalue.h> #include <univalue.h>
std::string static EncodeDumpTime(int64_t nTime) {
return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime);
}
int64_t static DecodeDumpTime(const std::string &str) { int64_t static DecodeDumpTime(const std::string &str) {
static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0); static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
static const std::locale loc(std::locale::classic(), static const std::locale loc(std::locale::classic(),
@ -908,16 +904,16 @@ UniValue dumpwallet(const JSONRPCRequest& request)
// produce output // produce output
file << strprintf("# Wallet dump created by Dash Core %s\n", CLIENT_BUILD); file << strprintf("# Wallet dump created by Dash Core %s\n", CLIENT_BUILD);
file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime())); file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime()));
file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString()); file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString());
file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime())); file << strprintf("# mined on %s\n", FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()));
file << "\n"; file << "\n";
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
obj.pushKV("dashcoreversion", CLIENT_BUILD); obj.pushKV("dashcoreversion", CLIENT_BUILD);
obj.pushKV("lastblockheight", chainActive.Height()); obj.pushKV("lastblockheight", chainActive.Height());
obj.pushKV("lastblockhash", chainActive.Tip()->GetBlockHash().ToString()); obj.pushKV("lastblockhash", chainActive.Tip()->GetBlockHash().ToString());
obj.pushKV("lastblocktime", EncodeDumpTime(chainActive.Tip()->GetBlockTime())); obj.pushKV("lastblocktime", FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()));
// add the base58check encoded extended master if the wallet uses HD // add the base58check encoded extended master if the wallet uses HD
CHDChain hdChainCurrent; CHDChain hdChainCurrent;
@ -966,7 +962,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
const CKeyID &keyid = it->second; const CKeyID &keyid = it->second;
std::string strTime = EncodeDumpTime(it->first); std::string strTime = FormatISO8601DateTime(it->first);
std::string strAddr = EncodeDestination(keyid); std::string strAddr = EncodeDestination(keyid);
CKey key; CKey key;
if (pwallet->GetKey(keyid, key)) { if (pwallet->GetKey(keyid, key)) {
@ -989,7 +985,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
// get birth times for scripts with metadata // get birth times for scripts with metadata
auto it = pwallet->m_script_metadata.find(scriptid); auto it = pwallet->m_script_metadata.find(scriptid);
if (it != pwallet->m_script_metadata.end()) { if (it != pwallet->m_script_metadata.end()) {
create_time = EncodeDumpTime(it->second.nCreateTime); create_time = FormatISO8601DateTime(it->second.nCreateTime);
} }
if(pwallet->GetCScript(scriptid, script)) { if(pwallet->GetCScript(scriptid, script)) {
file << strprintf("%s %s script=1", HexStr(script.begin(), script.end()), create_time); file << strprintf("%s %s script=1", HexStr(script.begin(), script.end()), create_time);

View File

@ -13,7 +13,7 @@ import re
import sys import sys
# Matches on the date format at the start of the log event # Matches on the date format at the start of the log event
TIMESTAMP_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{6}") TIMESTAMP_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z")
LogEvent = namedtuple('LogEvent', ['timestamp', 'source', 'event']) LogEvent = namedtuple('LogEvent', ['timestamp', 'source', 'event'])

View File

@ -405,7 +405,7 @@ class BitcoinTestFramework():
ll = int(self.options.loglevel) if self.options.loglevel.isdigit() else self.options.loglevel.upper() ll = int(self.options.loglevel) if self.options.loglevel.isdigit() else self.options.loglevel.upper()
ch.setLevel(ll) ch.setLevel(ll)
# Format logs the same as dashd's debug.log with microprecision (so log files can be concatenated and sorted) # Format logs the same as dashd's debug.log with microprecision (so log files can be concatenated and sorted)
formatter = logging.Formatter(fmt='%(asctime)s.%(msecs)03d000 %(name)s (%(levelname)s): %(message)s', datefmt='%Y-%m-%d %H:%M:%S') formatter = logging.Formatter(fmt='%(asctime)s.%(msecs)03d000Z %(name)s (%(levelname)s): %(message)s', datefmt='%Y-%m-%dT%H:%M:%S')
formatter.converter = time.gmtime formatter.converter = time.gmtime
fh.setFormatter(formatter) fh.setFormatter(formatter)
ch.setFormatter(formatter) ch.setFormatter(formatter)