UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

494 lines (493 loc) • 14.6 kB
import { get, set } from "./cache/memory/index.js"; import { logger } from "../logger/index.js"; import { parseUrl } from "./url.js"; //#region lib/util/stats.ts function makeTimingReport(data) { const count = data.length; const totalMs = data.reduce((a, c) => a + c, 0); const avgMs = count ? Math.round(totalMs / count) : 0; const maxMs = Math.max(0, ...data); const sorted = data.sort((a, b) => a - b); return { count, avgMs, medianMs: count ? sorted[Math.floor(count / 2)] : 0, maxMs, totalMs }; } var LookupStats = class LookupStats { static write(datasource, duration) { const data = get("lookup-stats") ?? {}; data[datasource] ??= []; data[datasource].push(duration); set("lookup-stats", data); } static async wrap(datasource, callback) { const start = Date.now(); const result = await callback(); const duration = Date.now() - start; LookupStats.write(datasource, duration); return result; } static getReport() { const report = {}; const data = get("lookup-stats") ?? {}; for (const [datasource, durations] of Object.entries(data)) report[datasource] = makeTimingReport(durations); return report; } static report() { const report = LookupStats.getReport(); logger.debug(report, "Lookup statistics"); } }; var GetDatasourceReleasesStats = class { static write(datasource, registryUrl, packageName, duration) { const data = get("get-releases-stats") ?? []; data.push({ datasource, registryUrl, packageName, duration }); set("get-releases-stats", data); } static async wrap(datasource, registryUrl, packageName, callback) { const start = Date.now(); const result = await callback(); const duration = Date.now() - start; this.write(datasource, registryUrl, packageName, duration); return result; } static getReport() { const data = get("get-releases-stats") ?? []; const durationData = { stats: [], datasources: {} }; for (const { datasource, registryUrl, packageName, duration } of data) { durationData.stats.push(duration); durationData.datasources[datasource] ??= { stats: [], registryUrls: {} }; durationData.datasources[datasource].stats.push(duration); durationData.datasources[datasource].registryUrls[registryUrl] ??= { stats: [], packages: {} }; durationData.datasources[datasource].registryUrls[registryUrl].stats.push(duration); durationData.datasources[datasource].registryUrls[registryUrl].packages[packageName] ??= []; durationData.datasources[datasource].registryUrls[registryUrl].packages[packageName].push(duration); } const report = { stats: makeTimingReport(durationData.stats), datasources: {} }; for (const [datasource, datasourceData] of Object.entries(durationData.datasources)) { report.datasources[datasource] = { stats: makeTimingReport(datasourceData.stats), registryUrls: {} }; for (const [registryUrl, registryUrlData] of Object.entries(datasourceData.registryUrls)) { report.datasources[datasource].registryUrls[registryUrl] = { stats: makeTimingReport(registryUrlData.stats), packages: {} }; for (const [packageName, packageNameData] of Object.entries(registryUrlData.packages)) report.datasources[datasource].registryUrls[registryUrl].packages[packageName] = makeTimingReport(packageNameData); } } return report; } static report() { const report = this.getReport(); const shortReport = { stats: report.stats, datasources: {} }; for (const [datasource, datasourceData] of Object.entries(report.datasources)) { shortReport.datasources[datasource] = { stats: datasourceData.stats, registryUrls: {} }; for (const [registryUrl, registryUrlData] of Object.entries(datasourceData.registryUrls)) shortReport.datasources[datasource].registryUrls[registryUrl] = { stats: registryUrlData.stats }; } logger.trace(report, "getReleases statistics with packages"); logger.debug(shortReport, "getReleases statistics summary"); } }; var PackageCacheStats = class PackageCacheStats { static writeSet(duration) { const data = get("package-cache-sets") ?? []; data.push(duration); set("package-cache-sets", data); } static async wrapSet(callback) { const start = Date.now(); const result = await callback(); const duration = Date.now() - start; PackageCacheStats.writeSet(duration); return result; } static writeGet(duration) { const data = get("package-cache-gets") ?? []; data.push(duration); set("package-cache-gets", data); } static async wrapGet(callback) { const start = Date.now(); const result = await callback(); const duration = Date.now() - start; PackageCacheStats.writeGet(duration); return result; } static getReport() { return { get: makeTimingReport(get("package-cache-gets") ?? []), set: makeTimingReport(get("package-cache-sets") ?? []) }; } static report() { const report = PackageCacheStats.getReport(); logger.debug(report, "Package cache statistics"); } }; var DatasourceCacheStats = class { static getData() { return get("datasource-cache-stats") ?? []; } static setData(data) { set("datasource-cache-stats", data); } static hit(datasource, registryUrl, packageName) { const data = this.getData(); data.push({ datasource, registryUrl, packageName, action: "hit" }); this.setData(data); } static miss(datasource, registryUrl, packageName) { const data = this.getData(); data.push({ datasource, registryUrl, packageName, action: "miss" }); this.setData(data); } static set(datasource, registryUrl, packageName) { const data = this.getData(); data.push({ datasource, registryUrl, packageName, action: "set" }); this.setData(data); } static skip(datasource, registryUrl, packageName) { const data = this.getData(); data.push({ datasource, registryUrl, packageName, action: "skip" }); this.setData(data); } static getReport() { const data = this.getData(); const result = { long: {}, short: {} }; for (const { datasource, registryUrl, packageName, action } of data) { result.long[datasource] ??= {}; result.long[datasource][registryUrl] ??= {}; result.long[datasource][registryUrl] ??= {}; result.long[datasource][registryUrl][packageName] ??= {}; result.short[datasource] ??= {}; result.short[datasource][registryUrl] ??= { hit: 0, miss: 0, set: 0, skip: 0 }; if (action === "hit") { result.long[datasource][registryUrl][packageName].read = "hit"; result.short[datasource][registryUrl].hit += 1; continue; } if (action === "miss") { result.long[datasource][registryUrl][packageName].read = "miss"; result.short[datasource][registryUrl].miss += 1; continue; } if (action === "set") { result.long[datasource][registryUrl][packageName].write = "set"; result.short[datasource][registryUrl].set += 1; continue; } /* v8 ignore else -- TODO: add tests #40625 */ if (action === "skip") { result.long[datasource][registryUrl][packageName].write = "skip"; result.short[datasource][registryUrl].skip += 1; continue; } } return result; } static report() { const { long, short } = this.getReport(); if (Object.keys(short).length > 0) logger.debug(short, "Datasource cache statistics"); if (Object.keys(long).length > 0) logger.trace(long, "Datasource cache detailed statistics"); } }; var HttpStats = class HttpStats { static write(data) { const httpRequests = get("http-requests") ?? []; httpRequests.push(data); set("http-requests", httpRequests); } static getDataPoints() { const httpRequests = get("http-requests") ?? []; // istanbul ignore next: sorting is hard and not worth testing httpRequests.sort((a, b) => { if (a.url < b.url) return -1; if (a.url > b.url) return 1; return 0; }); return httpRequests; } static getReport() { const dataPoints = HttpStats.getDataPoints(); const requests = dataPoints.length; const urls = {}; const rawRequests = []; const hostRequests = {}; for (const dataPoint of dataPoints) { const { url, reqMs, queueMs, status } = dataPoint; const method = dataPoint.method.toUpperCase(); const parsedUrl = parseUrl(url); if (!parsedUrl) { logger.debug({ url }, "Failed to parse URL during stats reporting"); continue; } const { hostname, origin, pathname } = parsedUrl; const baseUrl = `${origin}${pathname}`; urls[baseUrl] ??= {}; urls[baseUrl][method] ??= {}; urls[baseUrl][method][status] ??= 0; urls[baseUrl][method][status] += 1; rawRequests.push(`${method} ${url} ${status} ${reqMs} ${queueMs}`); hostRequests[hostname] ??= []; hostRequests[hostname].push(dataPoint); } const hosts = {}; for (const [hostname, dataPoints] of Object.entries(hostRequests)) { const count = dataPoints.length; const reqTimes = dataPoints.map((r) => r.reqMs); const queueTimes = dataPoints.map((r) => r.queueMs); const reqReport = makeTimingReport(reqTimes); const queueReport = makeTimingReport(queueTimes); hosts[hostname] = { count, reqAvgMs: reqReport.avgMs, reqMedianMs: reqReport.medianMs, reqMaxMs: reqReport.maxMs, queueAvgMs: queueReport.avgMs, queueMedianMs: queueReport.medianMs, queueMaxMs: queueReport.maxMs }; } return { urls, rawRequests, hostRequests, hosts, requests }; } static report() { const { urls, rawRequests, hostRequests, hosts, requests } = HttpStats.getReport(); logger.trace({ rawRequests, hostRequests }, "HTTP full statistics"); logger.debug({ hosts, requests }, "HTTP statistics"); logger.trace({ urls }, "HTTP URL statistics"); } }; function sortObject(obj) { const result = {}; for (const key of Object.keys(obj).sort()) result[key] = obj[key]; return result; } var HttpCacheStats = class HttpCacheStats { static getData() { return get("http-cache-stats") ?? {}; } static read(key) { return this.getData()?.[key] ?? { hit: 0, miss: 0 }; } static write(key, data) { const stats = get("http-cache-stats") ?? {}; stats[key] = data; set("http-cache-stats", stats); } static getBaseUrl(url) { const parsedUrl = parseUrl(url); if (!parsedUrl) { logger.debug({ url }, "Failed to parse URL during cache stats"); return null; } const { origin, pathname } = parsedUrl; return `${origin}${pathname}`; } static incLocalHits(url) { const baseUrl = HttpCacheStats.getBaseUrl(url); /* v8 ignore else -- TODO: add tests #40625 */ if (baseUrl) { const host = baseUrl; const stats = HttpCacheStats.read(host); stats.localHit ??= 0; stats.localHit += 1; HttpCacheStats.write(host, stats); } } static incLocalMisses(url) { const baseUrl = HttpCacheStats.getBaseUrl(url); /* v8 ignore else -- TODO: add tests #40625 */ if (baseUrl) { const host = baseUrl; const stats = HttpCacheStats.read(host); stats.localMiss ??= 0; stats.localMiss += 1; HttpCacheStats.write(host, stats); } } static incRemoteHits(url) { const baseUrl = HttpCacheStats.getBaseUrl(url); /* v8 ignore else -- TODO: add tests #40625 */ if (baseUrl) { const host = baseUrl; const stats = HttpCacheStats.read(host); stats.hit += 1; HttpCacheStats.write(host, stats); } } static incRemoteMisses(url) { const baseUrl = HttpCacheStats.getBaseUrl(url); /* v8 ignore else -- TODO: add tests #40625 */ if (baseUrl) { const host = baseUrl; const stats = HttpCacheStats.read(host); stats.miss += 1; HttpCacheStats.write(host, stats); } } static report() { const data = HttpCacheStats.getData(); let report = {}; for (const [url, stats] of Object.entries(data)) { const parsedUrl = parseUrl(url); /* v8 ignore else -- TODO: add tests #40625 */ if (parsedUrl) { const { origin, pathname } = parsedUrl; report[origin] ??= {}; report[origin][pathname] = stats; } } for (const [host, hostStats] of Object.entries(report)) report[host] = sortObject(hostStats); report = sortObject(report); logger.debug(report, "HTTP cache statistics"); } }; /* v8 ignore next: temporary code */ var ObsoleteCacheHitLogger = class { static getData() { return get("obsolete-cache-stats") ?? {}; } static write(url) { const data = this.getData(); if (!data[url]) data[url] = { count: 0 }; data[url].count++; set("obsolete-cache-stats", data); } static report() { const hits = this.getData(); logger.debug({ count: Object.keys(hits).length, hits }, "Cache fallback URLs"); } }; var AbandonedPackageStats = class { static getData() { return get("abandonment-stats") ?? []; } static setData(data) { set("abandonment-stats", data); } static write(datasource, packageName, mostRecentTimestamp) { const data = this.getData(); data.push({ datasource, packageName, mostRecentTimestamp }); this.setData(data); } static getReport() { const data = this.getData(); const result = {}; for (const { datasource, packageName, mostRecentTimestamp } of data) { result[datasource] ??= {}; result[datasource][packageName] = mostRecentTimestamp; } const sortedResult = {}; for (const datasource of Object.keys(result).sort()) { sortedResult[datasource] = {}; for (const packageName of Object.keys(result[datasource]).sort()) sortedResult[datasource][packageName] = result[datasource][packageName]; } return sortedResult; } static report() { const report = this.getReport(); if (Object.keys(report).length > 0) logger.debug(report, "Abandoned package statistics"); } }; var GitOperationStats = class GitOperationStats { static write(operationType, duration) { const data = get("git-operations-stats") ?? {}; data[operationType] ??= []; data[operationType].push(duration); set("git-operations-stats", data); } static getReport() { const report = {}; const data = get("git-operations-stats") ?? {}; for (const [operationType, durations] of Object.entries(data)) { report[operationType] = makeTimingReport(durations); report[operationType].totalMs = Math.ceil(report[operationType].totalMs); } return report; } static report() { const report = GitOperationStats.getReport(); logger.debug(report, "Git operations statistics"); } }; //#endregion export { AbandonedPackageStats, DatasourceCacheStats, GetDatasourceReleasesStats, GitOperationStats, HttpCacheStats, HttpStats, LookupStats, ObsoleteCacheHitLogger, PackageCacheStats }; //# sourceMappingURL=stats.js.map