UNPKG

@azure/monitor-opentelemetry

Version:
309 lines 14.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PerformanceCounterMetrics = void 0; const tslib_1 = require("tslib"); // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. const os = tslib_1.__importStar(require("node:os")); const api_1 = require("@opentelemetry/api"); const monitor_opentelemetry_exporter_1 = require("@azure/monitor-opentelemetry-exporter"); const sdk_metrics_1 = require("@opentelemetry/sdk-metrics"); const types_js_1 = require("./types.js"); const utils_js_1 = require("./quickpulse/utils.js"); const logger_js_1 = require("../shared/logging/logger.js"); const process = tslib_1.__importStar(require("node:process")); /** * Azure Monitor PerformanceCounter Metrics */ class PerformanceCounterMetrics { /** * Creates performance counter instruments. * @param options - Distro configuration. * @param config - Application Insights configuration. */ constructor(config, options) { this.collectionInterval = 60000; // 60 seconds this.lastExceptionRate = { count: 0, time: 0 }; this.totalCount = 0; this.intervalExecutionTime = 0; this.totalExceptionCount = 0; this.internalConfig = config; this.lastCpus = os.cpus(); this.lastCpusProcess = os.cpus(); this.lastAppCpuUsage = process.cpuUsage(); this.lastHrtime = process.hrtime(); this.lastRequestRate = { count: this.totalCount, time: +new Date(), executionInterval: this.intervalExecutionTime, }; const meterProviderConfig = { resource: this.internalConfig.resource, }; this.meterProvider = new sdk_metrics_1.MeterProvider(meterProviderConfig); this.azureExporter = new monitor_opentelemetry_exporter_1.AzureMonitorMetricExporter(this.internalConfig.azureMonitorExporterOptions); const metricReaderOptions = { exporter: this.azureExporter, exportIntervalMillis: (options === null || options === void 0 ? void 0 : options.collectionInterval) || this.collectionInterval, }; this.metricReader = new sdk_metrics_1.PeriodicExportingMetricReader(metricReaderOptions); this.meterProvider.addMetricReader(this.metricReader); this.meter = this.meterProvider.getMeter("AzureMonitorPerformanceCountersMeter"); this.lastRequestRate = { count: 0, time: 0, executionInterval: 0 }; // Create Instruments this.requestDurationHistogram = this.meter.createHistogram(types_js_1.PerformanceCounterMetricNames.REQUEST_DURATION, { valueType: api_1.ValueType.DOUBLE }); this.requestRateGauge = this.meter.createObservableGauge(types_js_1.PerformanceCounterMetricNames.REQUEST_RATE, { description: "Incoming Requests Average Execution Time", valueType: api_1.ValueType.DOUBLE, }); this.memoryPrivateBytesGauge = this.meter.createObservableGauge(types_js_1.PerformanceCounterMetricNames.PRIVATE_BYTES, { description: "Amount of memory process has used in bytes", valueType: api_1.ValueType.INT }); this.memoryAvailableBytesGauge = this.meter.createObservableGauge(types_js_1.PerformanceCounterMetricNames.AVAILABLE_BYTES, { description: "Amount of available memory in bytes", valueType: api_1.ValueType.INT }); this.processorTimeGauge = this.meter.createObservableGauge(types_js_1.PerformanceCounterMetricNames.PROCESSOR_TIME, { description: "Processor time as a percentage", valueType: api_1.ValueType.DOUBLE, }); this.processTimeGauge = this.meter.createObservableGauge(types_js_1.PerformanceCounterMetricNames.PROCESS_TIME_STANDARD, { description: "Process CPU usage as a percentage", valueType: api_1.ValueType.DOUBLE, }); this.processTimeNormalizedGauge = this.meter.createObservableGauge(types_js_1.PerformanceCounterMetricNames.PROCESS_TIME_NORMALIZED, { description: "Process CPU usage normalized as a percentage", valueType: api_1.ValueType.DOUBLE, }); this.exceptionCountGauge = this.meter.createObservableGauge(types_js_1.PerformanceCounterMetricNames.EXCEPTION_RATE, { description: "Exception Rate", valueType: api_1.ValueType.DOUBLE, }); // Add callbacks this.requestRateGaugeCallback = this.getRequestRate.bind(this); this.memoryPrivateBytesGaugeCallback = this.getPrivateMemory.bind(this); this.memoryAvailableBytesGaugeCallback = this.getAvailableMemory.bind(this); this.processorTimeGaugeCallback = this.getProcessorTime.bind(this); this.processTimeGaugeCallback = this.getProcessTime.bind(this); this.processTimeNormalizedGaugeCallback = this.getNormalizedProcessTime.bind(this); this.exceptionCountGaugeCallback = this.getExceptionRate.bind(this); this.memoryPrivateBytesGauge.addCallback(this.memoryPrivateBytesGaugeCallback); this.memoryAvailableBytesGauge.addCallback(this.memoryAvailableBytesGaugeCallback); this.processTimeGauge.addCallback(this.processTimeGaugeCallback); this.processTimeNormalizedGauge.addCallback(this.processTimeNormalizedGaugeCallback); this.processorTimeGauge.addCallback(this.processorTimeGaugeCallback); this.requestRateGauge.addCallback(this.requestRateGaugeCallback); this.exceptionCountGauge.addCallback(this.exceptionCountGaugeCallback); } /** * Shutdown Meter Provider it will return no-op Meters after being called. */ async shutdown() { return this.meterProvider.shutdown(); } /** * Force flush Meter Provider. */ async flush() { return this.meterProvider.forceFlush(); } /** *Get OpenTelemetry MeterProvider */ getMeterProvider() { return this.meterProvider; } /** * Record Span metrics * @internal */ recordSpan(span) { if (span.kind !== api_1.SpanKind.SERVER) { return; } this.totalCount++; const durationMs = span.duration[0]; this.intervalExecutionTime += durationMs; this.requestDurationHistogram.record(durationMs); if (span.events) { span.events.forEach((event) => { event.attributes = event.attributes || {}; if (event.name === "exception") { this.totalExceptionCount++; } }); } } /** * Record Log metrics * @internal */ recordLog(logRecord) { const columns = (0, utils_js_1.getLogData)(logRecord); if ((0, utils_js_1.isExceptionData)(columns)) { this.totalExceptionCount++; } } getRequestRate(observableResult) { const currentTime = +new Date(); const intervalRequests = this.totalCount - this.lastRequestRate.count || 0; const elapsedMs = currentTime - this.lastRequestRate.time; if (elapsedMs > 0) { const elapsedSeconds = elapsedMs / 1000; const requestsPerSec = intervalRequests / elapsedSeconds; observableResult.observe(requestsPerSec); } this.lastRequestRate = { count: this.totalCount, time: currentTime, executionInterval: this.lastRequestRate.executionInterval, }; } getPrivateMemory(observableResult) { if (process === null || process === void 0 ? void 0 : process.memoryUsage) { observableResult.observe(process.memoryUsage().rss); } else { logger_js_1.Logger.getInstance().debug("Couldn't report Private Memory. Process is not defined."); } } getAvailableMemory(observableResult) { observableResult.observe(os.freemem()); } getTotalCombinedCpu(cpus, lastCpus) { let totalUser = 0; let totalSys = 0; let totalNice = 0; let totalIdle = 0; let totalIrq = 0; for (let i = 0; !!cpus && i < cpus.length; i++) { const cpu = cpus[i]; const lastCpu = lastCpus[i]; const times = cpu.times; const lastTimes = lastCpu.times; // user cpu time (or) % CPU time spent in user space let user = times.user - lastTimes.user; user = user > 0 ? user : 0; // Avoid negative values totalUser += user; // system cpu time (or) % CPU time spent in kernel space let sys = times.sys - lastTimes.sys; sys = sys > 0 ? sys : 0; // Avoid negative values totalSys += sys; // user nice cpu time (or) % CPU time spent on low priority processes let nice = times.nice - lastTimes.nice; nice = nice > 0 ? nice : 0; // Avoid negative values totalNice += nice; // idle cpu time (or) % CPU time spent idle let idle = times.idle - lastTimes.idle; idle = idle > 0 ? idle : 0; // Avoid negative values totalIdle += idle; // irq (or) % CPU time spent servicing/handling hardware interrupts let irq = times.irq - lastTimes.irq; irq = irq > 0 ? irq : 0; // Avoid negative values totalIrq += irq; } const combinedTotal = totalUser + totalSys + totalNice + totalIdle + totalIrq; return { combinedTotal: combinedTotal, totalUser: totalUser, totalIdle: totalIdle, }; } getProcessorTime(observableResult) { // this reports total ms spent in each category since the OS was booted, to calculate percent it is necessary // to find the delta since the last measurement const cpus = os.cpus(); if (cpus && cpus.length && this.lastCpus && cpus.length === this.lastCpus.length) { const cpuTotals = this.getTotalCombinedCpu(cpus, this.lastCpus); const value = cpuTotals.combinedTotal > 0 ? ((cpuTotals.combinedTotal - cpuTotals.totalIdle) / cpuTotals.combinedTotal) * 100 : 0; observableResult.observe(value); } this.lastCpus = cpus; } getNormalizedProcessTime(observableResult) { // this reports total ms spent in each category since the OS was booted, to calculate percent it is necessary // to find the delta since the last measurement const cpus = os.cpus(); if (cpus && cpus.length && this.lastCpusProcess && cpus.length === this.lastCpusProcess.length) { // Calculate % of total cpu time (user + system) this App Process used (Only supported by node v6.1.0+) let appCpuPercent = undefined; const appCpuUsage = process.cpuUsage(); const hrtime = process.hrtime(); const totalApp = appCpuUsage.user - this.lastAppCpuUsage.user + (appCpuUsage.system - this.lastAppCpuUsage.system) || 0; if (typeof this.lastHrtime !== "undefined" && this.lastHrtime.length === 2) { const elapsedTime = (hrtime[0] - this.lastHrtime[0]) * 1e6 + (hrtime[1] - this.lastHrtime[1]) / 1e3 || 0; // convert to microseconds appCpuPercent = (100 * totalApp) / (elapsedTime * cpus.length); } // Set previous this.lastAppCpuUsage = appCpuUsage; this.lastHrtime = hrtime; const cpuTotals = this.getTotalCombinedCpu(cpus, this.lastCpusProcess); let value = 0; if (appCpuPercent !== undefined) { value = appCpuPercent; } else { value = (cpuTotals.totalUser / cpuTotals.combinedTotal) * 100; } observableResult.observe(value); } this.lastCpusProcess = cpus; } getProcessTime(observableResult) { // this reports total ms spent in each category since the OS was booted, to calculate percent it is necessary // to find the delta since the last measurement if (process) { const cpus = os.cpus(); if (cpus && cpus.length && this.lastCpusProcess && cpus.length === this.lastCpusProcess.length) { // Calculate % of total cpu time (user + system) this App Process used (Only supported by node v6.1.0+) let appCpuPercent = undefined; const appCpuUsage = process.cpuUsage(); const hrtime = process.hrtime(); const totalApp = appCpuUsage.user - this.lastAppCpuUsage.user + (appCpuUsage.system - this.lastAppCpuUsage.system) || 0; if (typeof this.lastHrtime !== "undefined" && this.lastHrtime.length === 2) { const elapsedTime = (hrtime[0] - this.lastHrtime[0]) * 1e6 + (hrtime[1] - this.lastHrtime[1]) / 1e3 || 0; // convert to microseconds appCpuPercent = (100 * totalApp) / elapsedTime; } // Set previous this.lastAppCpuUsage = appCpuUsage; this.lastHrtime = hrtime; const cpuTotals = this.getTotalCombinedCpu(cpus, this.lastCpusProcess); let value = 0; if (appCpuPercent !== undefined) { value = appCpuPercent; } else { value = (cpuTotals.totalUser / cpuTotals.combinedTotal) * 100; } observableResult.observe(value); } this.lastCpusProcess = cpus; } else { logger_js_1.Logger.getInstance().debug("Couldn't report process time. Process is not defined."); } } getExceptionRate(observableResult) { const currentTime = +new Date(); const intervalData = this.totalExceptionCount - this.lastExceptionRate.count || 0; const elapsedMs = currentTime - this.lastExceptionRate.time; if (elapsedMs > 0) { const elapsedSeconds = elapsedMs / 1000; const dataPerSec = intervalData / elapsedSeconds; observableResult.observe(dataPerSec); } this.lastExceptionRate = { count: this.totalExceptionCount, time: currentTime, }; } } exports.PerformanceCounterMetrics = PerformanceCounterMetrics; //# sourceMappingURL=performanceCounters.js.map