@itwin/core-backend
Version:
iTwin.js backend components
153 lines • 6.27 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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