UNPKG

@itwin/core-backend

Version:
153 lines • 6.27 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import * as os from "os"; import * as process from "process"; import { Logger } from "@itwin/core-bentley"; import { BackendLoggerCategory } from "./BackendLoggerCategory"; import { IModelHost } from "./IModelHost"; import { IModelNative } from "./internal/NativePlatform"; // cspell:ignore ppid elap const loggerCategory = BackendLoggerCategory.DevTools; /** Utility to format the JSON created by DevTools.stats() to include the appropriate units * @internal */ export class DevToolsStatsFormatter { static _megaByteProps = ["totalmem", "freemem", "rss", "heapTotal", "heapUsed", "external"]; static _percentProps = ["user", "nice", "sys", "idle", "irq", "cpuUsage"]; static _mHzProps = ["speed"]; static _secondsProps = ["uptime"]; /** Replacer that includes units - can be used with JSON.stringify() */ static _replacer = (key, value) => { if (DevToolsStatsFormatter._megaByteProps.includes(key)) return `${value.toFixed()} MB`; if (DevToolsStatsFormatter._percentProps.includes(key)) return `${value.toFixed()}%`; if (DevToolsStatsFormatter._mHzProps.includes(key)) return `${value.toString()} MHz`; if (DevToolsStatsFormatter._secondsProps.includes(key)) return `${value.toFixed()} secs`; return value; }; /** Converts the input stats to another JSON object with the appropriate units setup for various fields */ static toFormattedJson(stats) { // Serialize the stats to a string with a replacer that sets up units during the serialization const statsStr = JSON.stringify(stats, DevToolsStatsFormatter._replacer); // Deserialize back to JSON return JSON.parse(statsStr); } } /** * Internal diagnostic utility * @internal */ export class DevTools { /** Receives a ping and returns true */ static ping() { Logger.logInfo(loggerCategory, "Received ping at backend"); return true; } static hrtimeToMS(hrtime) { return hrtime[0] * 1000 + hrtime[1] / 1000000; } static bytesToMegaBytes(bytes) { const megaBytes = bytes / Math.pow(1024, 2); return Math.round(megaBytes * 100) / 100; } static evaluateCpuUsage() { const NUMBER_OF_CPUS = os.cpus().length; const startTime = process.hrtime(); const startUsage = process.cpuUsage(); // spin the CPU for 500 milliseconds const now = Date.now(); while (Date.now() - now < 500) ; const elapTime = process.hrtime(startTime); const elapUsage = process.cpuUsage(startUsage); const elapTimeMS = this.hrtimeToMS(elapTime); const elapUserMS = elapUsage.user / 1000; // microseconds to milliseconds const elapSystMS = elapUsage.system / 1000; const cpuPercent = Math.round((100 * (elapUserMS + elapSystMS) / elapTimeMS / NUMBER_OF_CPUS)); return cpuPercent; } static evaluateCpus() { // Create a clone const srcCpus = os.cpus(); const cpus = new Array(srcCpus.length); let ii = 0; for (const srcCpu of srcCpus) cpus[ii++] = { ...srcCpu }; // Evaluate cpu usage as percentages for (const cpu of Object.values(cpus)) { const total = Object.values(cpu.times).reduce((_total, currValue) => _total += currValue, 0); const cpuTimes = cpu.times; for (const type of Object.keys(cpuTimes)) { const cpuPercent = Math.round(100 * cpuTimes[type] / total); cpuTimes[type] = cpuPercent; } } return cpus; } static evaluateMemoryUsage() { // Create a clone const memUsage = { ...process.memoryUsage() }; const memUsageObj = memUsage; // Evaluate memory usage as mega bytes for (const type of Object.keys(memUsageObj)) { memUsageObj[type] = this.bytesToMegaBytes(memUsageObj[type]); } return memUsage; } static evaluateProcessStats() { return { uptime: process.uptime(), pid: process.pid, ppid: process.ppid, memoryUsage: this.evaluateMemoryUsage(), }; } static evaluateOsStats() { return { platform: os.platform(), hostname: os.hostname(), totalmem: this.bytesToMegaBytes(os.totalmem()), freemem: this.bytesToMegaBytes(os.freemem()), uptime: os.uptime(), cpus: this.evaluateCpus(), cpuUsage: this.evaluateCpuUsage(), }; } /** Returns JSON object with backend statistics */ static stats() { try { const stats = { os: this.evaluateOsStats(), process: this.evaluateProcessStats(), }; return stats; } catch (error) { Logger.logError(loggerCategory, "Could not fetch stats at backend"); throw error; } } /** Sets up a log level at the backend and returns the old log level */ static setLogLevel(inLoggerCategory, newLevel) { const oldLevel = Logger.getLevel(inLoggerCategory); Logger.logInfo(loggerCategory, `Setting log level`, () => ({ loggerCategory: inLoggerCategory, oldLevel, newLevel })); Logger.setLevel(inLoggerCategory, newLevel); IModelNative.platform.clearLogLevelCache(); return oldLevel; } /** Obtains the backend application and iTwin.js Core versions */ static async versions() { const availableRpcs = []; return { application: IModelHost.applicationVersion, iTwinJs: require("../../package.json").version, // eslint-disable-line @typescript-eslint/no-require-imports availableRpcs, // filled in on the frontend }; } } //# sourceMappingURL=DevTools.js.map