diff --git a/app.js b/app.js index 0f9a43f..4977b33 100755 --- a/app.js +++ b/app.js @@ -179,7 +179,7 @@ function loadHistoricalDataForChain(chain) { function verifyRpcConnection() { if (!global.activeBlockchain) { - debugLog(`Trying to verify RPC connection...`); + debugLog(`Verifying RPC connection...`); coreApi.getNetworkInfo().then(function(getnetworkinfo) { coreApi.getBlockchainInfo().then(function(getblockchaininfo) { diff --git a/app/api/rpcApi.js b/app/api/rpcApi.js index edda9ca..33d3bea 100644 --- a/app/api/rpcApi.js +++ b/app/api/rpcApi.js @@ -26,6 +26,8 @@ var rpcQueue = async.queue(function(task, callback) { var minRpcVersions = {getblockstats:"0.17.0"}; +global.rpcStats = {}; + function getBlockchainInfo() { @@ -321,6 +323,8 @@ function getRpcMethodHelp(methodName) { function getRpcData(cmd) { + var startTime = new Date().getTime(); + return new Promise(function(resolve, reject) { debugLog(`RPC: ${cmd}`); @@ -335,11 +339,15 @@ function getRpcData(cmd) { callback(); + logStats(cmd, false, new Date().getTime() - startTime, false); + return; } resolve(result); + logStats(cmd, false, new Date().getTime() - startTime, true); + callback(); }); }; @@ -349,6 +357,8 @@ function getRpcData(cmd) { } function getRpcDataWithParams(request) { + var startTime = new Date().getTime(); + return new Promise(function(resolve, reject) { debugLog(`RPC: ${JSON.stringify(request)}`); @@ -361,11 +371,15 @@ function getRpcDataWithParams(request) { callback(); + logStats(request.method, true, new Date().getTime() - startTime, false); + return; } resolve(result[0]); + logStats(request.method, true, new Date().getTime() - startTime, true); + callback(); }); }; @@ -380,6 +394,26 @@ function unsupportedPromise(minRpcVersionNeeded) { }); } +function logStats(cmd, hasParams, dt, success) { + if (!global.rpcStats[cmd]) { + global.rpcStats[cmd] = {count:0, withParams:0, time:0, successes:0, failures:0}; + } + + global.rpcStats[cmd].count++; + global.rpcStats[cmd].time += dt; + + if (hasParams) { + global.rpcStats[cmd].withParams++; + } + + if (success) { + global.rpcStats[cmd].successes++; + + } else { + global.rpcStats[cmd].failures++; + } +} + module.exports = { getBlockchainInfo: getBlockchainInfo, diff --git a/routes/baseActionsRouter.js b/routes/baseActionsRouter.js index 02d5d66..a0320b2 100644 --- a/routes/baseActionsRouter.js +++ b/routes/baseActionsRouter.js @@ -846,7 +846,7 @@ router.get("/tx/:transactionId", function(req, res, next) { }).catch(function(err) { res.locals.userMessage = "Failed to load transaction with txid=" + txid + ": " + err; - + res.locals.pageErrors.push(utils.logError("1237y4ewssgt", err)); res.render("transaction"); @@ -1438,6 +1438,7 @@ router.get("/tools", function(req, res, next) { router.get("/admin", function(req, res, next) { res.locals.memstats = v8.getHeapStatistics(); + res.locals.rpcStats = global.rpcStats; res.render("admin"); diff --git a/views/admin.pug b/views/admin.pug index f252d8a..e1ec683 100644 --- a/views/admin.pug +++ b/views/admin.pug @@ -7,5 +7,93 @@ block content h1.h3 Admin hr - pre - code.json #{JSON.stringify(memstats, null, 4)} \ No newline at end of file + ul.nav.nav-tabs.mb-3 + li.nav-item + a.nav-link.active(data-toggle="tab", href="#tab-details", role="tab") Details + li.nav-item + a.nav-link(data-toggle="tab", href="#tab-json", role="tab") JSON + + div.tab-content + div.tab-pane.active(id="tab-details", role="tabpanel") + div.card.shadow-sm.mb-3 + div.card-body + h3.h6 Memory Stats + hr + + div.clearfix + div.row + div.summary-table-label Heap Size + div.summary-table-content.text-monospace + - var data = utils.formatLargeNumber(memstats.total_heap_size, 2); + span #{data[0]} + small #{data[1].abbreviation}B + + div.row + div.summary-table-label Used Heap + div.summary-table-content.text-monospace + - var data = utils.formatLargeNumber(memstats.used_heap_size, 2); + span #{data[0]} + small #{data[1].abbreviation}B + + div.row + div.summary-table-label Heap Limit + div.summary-table-content.text-monospace + - var data = utils.formatLargeNumber(memstats.heap_size_limit, 2); + span #{data[0]} + small #{data[1].abbreviation}B + + div.row + div.summary-table-label Physical Size + div.summary-table-content.text-monospace + - var data = utils.formatLargeNumber(memstats.total_physical_size, 2); + span #{data[0]} + small #{data[1].abbreviation}B + + div.row + div.summary-table-label Available Size + div.summary-table-content.text-monospace + - var data = utils.formatLargeNumber(memstats.total_available_size, 2); + span #{data[0]} + small #{data[1].abbreviation}B + + div.card.shadow-sm.mb-3 + div.card-body + h3.h6 RPC Stats + hr + + table.table.table-hover.table-striped + thead + tr + th Method + th.text-right Count + th.text-right Time + small (s) + th.text-right Avg Time + small (ms) + th.text-right Successes / Failures + th.text-right Success Rate + + tbody + each item, itemName in rpcStats + tr.text-monospace + td #{itemName} + td.text-right #{item.count.toLocaleString()} + td.text-right #{(item.time / 1000).toLocaleString()} + td.text-right #{(item.time / item.count).toLocaleString()} + td.text-right + span.text-success #{item.successes.toLocaleString()} + span.mx-1 / + span.text-danger #{item.failures.toLocaleString()} + + td.text-right + span #{new Decimal(item.successes).dividedBy(new Decimal(item.successes + item.failures)).times(100).toDP(1)}% + + div.tab-pane(id="tab-json", role="tabpanel") + div.card.shadow-sm.mb-3 + div.card-body + h3.h6 Memory Stats + hr + + div.highlight + pre + code.json #{JSON.stringify(memstats, null, 4)} \ No newline at end of file diff --git a/views/block-analysis-search.pug b/views/block-analysis-search.pug index 133bc83..bcdd98c 100644 --- a/views/block-analysis-search.pug +++ b/views/block-analysis-search.pug @@ -7,17 +7,29 @@ block content h1.h3 Block Analysis hr - p Search for a block by height or hash to see a summary analysis of the transactions within that block. + div.card.shadow-sm.mb-huge + div.card-body + h6.mb-3 Search for a block by height or hash to see a summary analysis of the transactions within that block. - div.mb-huge - form.form.form-inline(method="get", action="/block-analysis") - input(type="hidden", name="_csrf", value=csrfToken) + div.mb-3 + form.form.form-inline(method="get", action="/block-analysis") + input(type="hidden", name="_csrf", value=csrfToken) - div.input-group - input.form-control(id="input-value", type="text", name="query", placeholder="block height/hash", value=(query), style="width: 400px;") - - div.input-group-append - button.btn.btn-primary(type="submit") Go + div.input-group + input.form-control(id="input-value", type="text", name="query", placeholder="block height/hash", value=(query), style="width: 400px;") + + div.input-group-append + button.btn.btn-primary(type="submit") Go + + hr.my-4 + + h6 Selection of example blocks: + + - var heights = [0, 170, 100000, 210000, 420000, 481824]; + ul.text-monospace + each height in heights + li + a(href=`/block-analysis/${height}`) Block ##{height.toLocaleString()} block endOfBody script. diff --git a/views/layout.pug b/views/layout.pug index 8b3af52..02d65e9 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -86,6 +86,12 @@ html(lang="en") i.fas.fa-search ul.navbar-nav + li.nav-item.dropdown + a.nav-link.dropdown-toggle(href="javascript:void(0)", role="button", data-toggle="dropdown", aria-haspopup="true", aria-expanded="false") + span Admin Tools + div.dropdown-menu.dropdown-menu-right.shadow(aria-label="Admin Tools Items") + a.dropdown-item(href="/admin") Admin Dashboard + li.nav-item.dropdown a.nav-link.dropdown-toggle(href="javascript:void(0)", id="navbarDropdown", role="button", data-toggle="dropdown", aria-haspopup="true", aria-expanded="false") i.fas.fa-cog.mr-1 diff --git a/views/search.pug b/views/search.pug index 5b14663..b4f68fd 100644 --- a/views/search.pug +++ b/views/search.pug @@ -7,12 +7,13 @@ block content h1.h3 Search hr - div.mb-huge - form.form.form-inline(method="post", action="/search") - input(type="hidden", name="_csrf", value=csrfToken) + div.card.shadow-sm.mb-huge + div.card-body + form.form.form-inline(method="post", action="/search") + input(type="hidden", name="_csrf", value=csrfToken) - div.input-group - input.form-control(type="text", name="query", placeholder="block height/hash, txid, address", value=(query), style="width: 400px;") - - div.input-group-append - button.btn.btn-primary(type="submit") Search \ No newline at end of file + div.input-group + input.form-control(type="text", name="query", placeholder="block height/hash, txid, address", value=(query), style="width: 400px;") + + div.input-group-append + button.btn.btn-primary(type="submit") Search \ No newline at end of file