@salesforce/apex-node
Version:
Salesforce JS library for Apex
164 lines • 5.9 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HeapMonitor = exports.NullHeapMonitor = void 0;
/*
* Copyright (c) 2024, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
const core_1 = require("@salesforce/core");
const node_v8_1 = __importDefault(require("node:v8"));
const node_timers_1 = require("node:timers");
const INTERVAL_DEFAULT = 500;
const INTERVAL_MIN = 100;
const INTERVAL_MAX = 10_000;
class NullHeapMonitor {
checkHeapSize() { }
startMonitoring() { }
stopMonitoring() { }
[Symbol.dispose]() { }
}
exports.NullHeapMonitor = NullHeapMonitor;
/**
* Class responsible for monitoring heap memory usage.
*/
class HeapMonitor {
static instance;
logger;
intervalId;
isMonitoring;
interval;
/**
* Private constructor to enforce singleton pattern.
*/
constructor() {
this.logger = core_1.Logger.childFromRoot('heap-monitor', {
tag: 'heap-monitor'
});
this.isMonitoring = false;
// Check for SF_HEAP_MONITOR_INTERVAL environment variable
this.interval = INTERVAL_DEFAULT; // default value
const envInterval = process.env.SF_HEAP_MONITOR_INTERVAL;
if (envInterval && Number.isInteger(Number(envInterval))) {
this.interval = Number(envInterval);
}
if (this.interval < INTERVAL_MIN || this.interval > INTERVAL_MAX) {
this.logger.warn(`Interval if ${this.interval} found in SF_HEAP_MONITOR_INTERVAL must be between: ${INTERVAL_MIN} and ${INTERVAL_MAX}. Using default of ${INTERVAL_DEFAULT}`);
this.interval = INTERVAL_DEFAULT;
}
}
/**
* Returns the singleton instance of HeapMonitor.
* @returns The singleton instance.
*/
static getInstance() {
if (!HeapMonitor.instance) {
if (core_1.Logger.getRoot().shouldLog(core_1.LoggerLevel.DEBUG)) {
HeapMonitor.instance = new HeapMonitor();
}
else {
HeapMonitor.instance = new NullHeapMonitor();
}
}
return HeapMonitor.instance;
}
/**
* Checks the current heap size and logs the details.
* @param [applicationArea] - Optional application area to be included in the log.
*/
checkHeapSize(applicationArea) {
if (!this.logger.shouldLog(core_1.LoggerLevel.DEBUG)) {
return;
}
const heapStats = node_v8_1.default.getHeapStatistics();
const heapSpaceStats = node_v8_1.default.getHeapSpaceStatistics();
const memoryUsage = process.memoryUsage();
const logRecord = {
msg: 'Memory usage',
applicationArea,
rss: Number(memoryUsage.rss),
heapTotal: Number(memoryUsage.heapTotal),
heapUsed: Number(memoryUsage.heapUsed),
external: Number(memoryUsage.external)
};
// Convert heapStats properties to numbers and add to logRecord
for (const [key, value] of Object.entries(heapStats)) {
logRecord[key] = Number(value);
}
// Flatten heapSpaces into individual properties
heapSpaceStats.forEach((space) => {
logRecord[`${space.space_name}_total`] = Number(space.space_size);
logRecord[`${space.space_name}_used`] = Number(space.space_used_size);
logRecord[`${space.space_name}_available`] = Number(space.space_available_size);
});
this.logger.debug(logRecord);
}
/**
* Starts monitoring the heap memory usage at regular intervals.
* WARNING: Monitoring is done on a schedule interval, which must stopped/cleared
* or the monitor will continue to run. The symptom of this will be a node process
* that seems to be hanging.
*
* One must call stopMonitoring or capture the instance in a scope with the
* Typescript "using"
* @example
* ```typescript
* async function main() {
* await using(HeapMonitor.getInstance(), async (heapMonitor) => {
* heapMonitor.startMonitoring();
*
* // Simulate some work
* await new Promise(resolve => setTimeout(resolve, 2000));
*
* // No need to explicitly call dispose, it will be called automatically
* });
* }
*
* main().catch(console.error);
* ```
**/
startMonitoring() {
if (!this.isMonitoring) {
this.isMonitoring = true;
if (!this.logger.shouldLog(core_1.LoggerLevel.DEBUG)) {
return;
}
this.checkHeapSize();
this.intervalId = setInterval(() => this.checkHeapSize(), this.interval);
}
}
/**
* Stops monitoring the heap memory usage.
*
* WARNING: It is imperative that a heap monitor be stopped
* before the node process exits.
*
* See @{link startMonitoring}
*/
stopMonitoring() {
if (this.isMonitoring) {
this.isMonitoring = false;
if (this.intervalId) {
(0, node_timers_1.clearInterval)(this.intervalId);
this.intervalId = undefined;
}
}
}
/**
* dispose method that will be called when instance of HeapMonitor
* is captured by Typescript "using" keyword.
*
* See {@link startMonitoring}
*/
[Symbol.dispose]() {
this.stopMonitoring();
this.logger.debug('HeapMonitor disposed');
HeapMonitor.instance = null;
}
}
exports.HeapMonitor = HeapMonitor;
//# sourceMappingURL=heapMonitor.js.map