UNPKG

@salesforce/apex-node

Version:

Salesforce JS library for Apex

164 lines 5.9 kB
"use strict"; 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