@ledgerhq/coin-modules-monitoring
Version:
Push monitoring metrics to Datadog
150 lines • 5.08 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.measureCalls = measureCalls;
/* eslint-disable @typescript-eslint/consistent-type-assertions */
const node_http_1 = __importDefault(require("node:http"));
const node_https_1 = __importDefault(require("node:https"));
function average(values) {
return values.reduce((previous, current) => previous + current) / values.length;
}
function computeStats(values) {
if (values.length === 0) {
return { min: 0, average: 0, max: 0, p90: 0, p99: 0, p50: 0, p95: 0 };
}
const sortedValues = values.slice().sort((a, b) => a - b);
const valuesCount = sortedValues.length;
const quantile = (p) => {
const rank = Math.ceil((p / 100) * valuesCount);
const index = Math.min(Math.max(rank - 1, 0), valuesCount - 1);
return sortedValues[index];
};
return {
min: sortedValues[0],
average: average(values),
max: sortedValues[valuesCount - 1],
p50: quantile(50),
p90: quantile(90),
p95: quantile(95),
p99: quantile(99),
};
}
function cpuPercentDelta(diffUserUs, diffSysUs, elapsedMs) {
const totalMs = (diffUserUs + diffSysUs) / 1000;
return elapsedMs > 0 ? (totalMs / elapsedMs) * 100 : 0;
}
function bytesToMB(bytes) {
return bytes / (1024 * 1024);
}
function setImmediateInterval(fn, ms) {
return setInterval((() => {
fn();
return fn;
})(), ms);
}
async function measureDuring(fn) {
const cpuSamples = [];
const memSamples = [];
let prevCpu = process.cpuUsage();
let prevTs = Date.now();
const { rss: previousMemory } = process.memoryUsage();
const tick = () => {
const now = Date.now();
const elapsed = now - prevTs;
const curCpu = process.cpuUsage();
const diffUser = curCpu.user - prevCpu.user;
const diffSys = curCpu.system - prevCpu.system;
const cpu = cpuPercentDelta(diffUser, diffSys, elapsed);
cpuSamples.push(cpu);
const { rss } = process.memoryUsage();
memSamples.push(bytesToMB(Math.max(rss - previousMemory, 0)));
prevCpu = curCpu;
prevTs = now;
};
const timer = setImmediateInterval(tick, 30);
try {
const result = await fn();
clearInterval(timer);
return { result, cpuSamples, memSamples };
}
catch (err) {
clearInterval(timer);
throw err;
}
}
function createCallCounter() {
const counters = {};
let started = false;
const originalHttpRequest = node_http_1.default.request;
const originalHttpsRequest = node_https_1.default.request;
const originalFetch = globalThis.fetch;
function start() {
if (started)
return;
node_http_1.default.request = patchedRequestOnHttp;
node_https_1.default.request = patchedRequestOnHttps;
globalThis.fetch = patchedFetch;
started = true;
}
function bump(url) {
const key = new URL(url).hostname;
counters[key] = (counters[key] ?? 0) + 1;
}
const patchedRequestOnHttp = (...args) => {
const request = originalHttpRequest.apply(node_http_1.default, args);
bump(request.protocol + "//" + request.host + request.path);
return request;
};
const patchedRequestOnHttps = (...args) => {
const request = originalHttpsRequest.apply(node_https_1.default, args);
bump(request.protocol + "//" + request.host + request.path);
return request;
};
const patchedFetch = (input, init) => {
const url = typeof input === "string" ? input : input instanceof Request ? input.url : input.href;
bump(url);
return originalFetch(input, init);
};
function stop() {
if (!started)
return;
node_http_1.default.request = originalHttpRequest;
node_https_1.default.request = originalHttpsRequest;
globalThis.fetch = originalFetch;
started = false;
}
function reset() {
for (const k in counters)
delete counters[k];
}
function snapshot() {
return { ...counters };
}
function total() {
return Object.values(counters).reduce((sum, v) => sum + v, 0);
}
return { start, stop, reset, snapshot, total };
}
async function measureCalls(phase) {
const localCounter = createCallCounter();
localCounter.reset();
localCounter.start();
try {
const { result, cpuSamples, memSamples } = await measureDuring(() => phase());
localCounter.stop();
return {
result,
totalCalls: localCounter.total(),
callsByDomain: localCounter.snapshot(),
cpu: computeStats(cpuSamples),
memory: computeStats(memSamples),
};
}
catch (err) {
localCounter.stop();
throw err;
}
}
//# sourceMappingURL=measure.js.map