@azure/monitor-opentelemetry
Version:
Azure Monitor OpenTelemetry (Node.js)
309 lines • 14.6 kB
JavaScript
"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