@azure/monitor-opentelemetry-exporter
Version:
Application Insights exporter for the OpenTelemetry JavaScript (Node.js) SDK
284 lines • 14.6 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { diag } from "@opentelemetry/api";
import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import * as ai from "../../utils/constants/applicationinsights.js";
import { StatsbeatMetrics } from "./statsbeatMetrics.js";
import { StatsbeatCounter, STATSBEAT_LANGUAGE, NetworkStatsbeat } from "./types.js";
import { AzureMonitorStatsbeatExporter } from "./statsbeatExporter.js";
import { ENV_DISABLE_STATSBEAT } from "../../Declarations/Constants.js";
import { getAttachType } from "../../utils/metricUtils.js";
export class NetworkStatsbeatMetrics extends StatsbeatMetrics {
constructor(options) {
super();
this.disableNonEssentialStatsbeat = !!process.env[ENV_DISABLE_STATSBEAT];
this.isInitialized = false;
this.statsCollectionShortInterval = 900000; // 15 minutes
this.networkStatsbeatCollection = [];
this.attach = getAttachType();
this.connectionString = super.getConnectionString(options.endpointUrl);
this.networkStatsbeatMeterProvider = new MeterProvider();
const exporterConfig = {
connectionString: this.connectionString,
};
this.networkAzureExporter = new AzureMonitorStatsbeatExporter(exporterConfig);
// Exports Network Statsbeat every 15 minutes
const networkMetricReaderOptions = {
exporter: this.networkAzureExporter,
exportIntervalMillis: options.networkCollectionInterval || this.statsCollectionShortInterval, // 15 minutes
};
this.networkMetricReader = new PeriodicExportingMetricReader(networkMetricReaderOptions);
this.networkStatsbeatMeterProvider.addMetricReader(this.networkMetricReader);
this.networkStatsbeatMeter = this.networkStatsbeatMeterProvider.getMeter("Azure Monitor Network Statsbeat");
this.endpointUrl = options.endpointUrl;
this.runtimeVersion = process.version;
this.language = STATSBEAT_LANGUAGE;
this.version = ai.packageVersion;
this.host = this.getShortHost(options.endpointUrl);
this.cikey = options.instrumentationKey;
this.successCountGauge = this.networkStatsbeatMeter.createObservableGauge(StatsbeatCounter.SUCCESS_COUNT);
this.failureCountGauge = this.networkStatsbeatMeter.createObservableGauge(StatsbeatCounter.FAILURE_COUNT);
this.retryCountGauge = this.networkStatsbeatMeter.createObservableGauge(StatsbeatCounter.RETRY_COUNT);
this.throttleCountGauge = this.networkStatsbeatMeter.createObservableGauge(StatsbeatCounter.THROTTLE_COUNT);
this.exceptionCountGauge = this.networkStatsbeatMeter.createObservableGauge(StatsbeatCounter.EXCEPTION_COUNT);
this.averageDurationGauge = this.networkStatsbeatMeter.createObservableGauge(StatsbeatCounter.AVERAGE_DURATION);
if (!this.disableNonEssentialStatsbeat) {
this.readFailureGauge = this.networkStatsbeatMeter.createObservableGauge(StatsbeatCounter.READ_FAILURE_COUNT);
this.writeFailureGauge = this.networkStatsbeatMeter.createObservableGauge(StatsbeatCounter.WRITE_FAILURE_COUNT);
}
this.isInitialized = true;
this.initialize();
this.commonProperties = {
os: this.os,
rp: this.resourceProvider,
cikey: this.cikey,
runtimeVersion: this.runtimeVersion,
language: this.language,
version: this.version,
attach: this.attach,
};
this.networkProperties = {
endpoint: this.endpointUrl,
host: this.host,
};
}
shutdown() {
return this.networkStatsbeatMeterProvider.shutdown();
}
async initialize() {
var _a, _b;
try {
await super.getResourceProvider();
// Add network observable callbacks
this.successCountGauge.addCallback(this.successCallback.bind(this));
this.networkStatsbeatMeter.addBatchObservableCallback(this.failureCallback.bind(this), [
this.failureCountGauge,
]);
this.networkStatsbeatMeter.addBatchObservableCallback(this.retryCallback.bind(this), [
this.retryCountGauge,
]);
this.networkStatsbeatMeter.addBatchObservableCallback(this.throttleCallback.bind(this), [
this.throttleCountGauge,
]);
this.networkStatsbeatMeter.addBatchObservableCallback(this.exceptionCallback.bind(this), [
this.exceptionCountGauge,
]);
if (!this.disableNonEssentialStatsbeat) {
(_a = this.readFailureGauge) === null || _a === void 0 ? void 0 : _a.addCallback(this.readFailureCallback.bind(this));
(_b = this.writeFailureGauge) === null || _b === void 0 ? void 0 : _b.addCallback(this.writeFailureCallback.bind(this));
}
this.averageDurationGauge.addCallback(this.durationCallback.bind(this));
}
catch (error) {
diag.debug("Call to get the resource provider failed.");
}
}
// Observable gauge callbacks
successCallback(observableResult) {
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
const attributes = Object.assign(Object.assign({}, this.commonProperties), this.networkProperties);
observableResult.observe(counter.totalSuccesfulRequestCount, attributes);
counter.totalSuccesfulRequestCount = 0;
}
failureCallback(observableResult) {
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
/*
Takes the failureCountGauge, value (of the counter), and attributes
create a unqiue counter based on statusCode as well
append statusCode to attributes so the newly created attributes are unique.
*/
const attributes = Object.assign(Object.assign(Object.assign({}, this.networkProperties), this.commonProperties), { statusCode: 0 });
// For each { statusCode -> count } mapping, call observe, passing the count and attributes that include the statusCode
for (let i = 0; i < counter.totalFailedRequestCount.length; i++) {
attributes.statusCode = counter.totalFailedRequestCount[i].statusCode;
observableResult.observe(this.failureCountGauge, counter.totalFailedRequestCount[i].count, Object.assign({}, attributes));
counter.totalFailedRequestCount[i].count = 0;
}
}
retryCallback(observableResult) {
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
const attributes = Object.assign(Object.assign(Object.assign({}, this.networkProperties), this.commonProperties), { statusCode: 0 });
for (let i = 0; i < counter.retryCount.length; i++) {
attributes.statusCode = counter.retryCount[i].statusCode;
observableResult.observe(this.retryCountGauge, counter.retryCount[i].count, Object.assign({}, attributes));
counter.retryCount[i].count = 0;
}
}
throttleCallback(observableResult) {
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
const attributes = Object.assign(Object.assign(Object.assign({}, this.networkProperties), this.commonProperties), { statusCode: 0 });
for (let i = 0; i < counter.throttleCount.length; i++) {
attributes.statusCode = counter.throttleCount[i].statusCode;
observableResult.observe(this.throttleCountGauge, counter.throttleCount[i].count, Object.assign({}, attributes));
counter.throttleCount[i].count = 0;
}
}
exceptionCallback(observableResult) {
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
const attributes = Object.assign(Object.assign(Object.assign({}, this.networkProperties), this.commonProperties), { exceptionType: "" });
for (let i = 0; i < counter.exceptionCount.length; i++) {
attributes.exceptionType = counter.exceptionCount[i].exceptionType;
observableResult.observe(this.exceptionCountGauge, counter.exceptionCount[i].count, Object.assign({}, attributes));
counter.exceptionCount[i].count = 0;
}
}
durationCallback(observableResult) {
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
const attributes = Object.assign(Object.assign({}, this.networkProperties), this.commonProperties);
for (let i = 0; i < this.networkStatsbeatCollection.length; i++) {
const currentCounter = this.networkStatsbeatCollection[i];
currentCounter.time = Number(new Date());
const intervalRequests = currentCounter.totalRequestCount - currentCounter.lastRequestCount || 0;
currentCounter.averageRequestExecutionTime =
(currentCounter.intervalRequestExecutionTime -
currentCounter.lastIntervalRequestExecutionTime) /
intervalRequests || 0;
currentCounter.lastIntervalRequestExecutionTime = currentCounter.intervalRequestExecutionTime; // reset
currentCounter.lastRequestCount = currentCounter.totalRequestCount;
currentCounter.lastTime = currentCounter.time;
}
observableResult.observe(counter.averageRequestExecutionTime, attributes);
counter.averageRequestExecutionTime = 0;
}
readFailureCallback(observableResult) {
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
const attributes = Object.assign(Object.assign({}, this.commonProperties), this.networkProperties);
observableResult.observe(counter.totalReadFailureCount, attributes);
counter.totalReadFailureCount = 0;
}
writeFailureCallback(observableResult) {
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
const attributes = Object.assign(Object.assign({}, this.commonProperties), this.networkProperties);
observableResult.observe(counter.totalWriteFailureCount, attributes);
counter.totalWriteFailureCount = 0;
}
// Public methods to increase counters
countSuccess(duration) {
if (!this.isInitialized) {
return;
}
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
counter.totalRequestCount++;
counter.totalSuccesfulRequestCount++;
counter.intervalRequestExecutionTime += duration;
}
countFailure(duration, statusCode) {
if (!this.isInitialized) {
return;
}
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
const currentStatusCounter = counter.totalFailedRequestCount.find((statusCounter) => statusCode === statusCounter.statusCode);
if (currentStatusCounter) {
currentStatusCounter.count++;
}
else {
counter.totalFailedRequestCount.push({ statusCode: statusCode, count: 1 });
}
counter.totalRequestCount++;
counter.intervalRequestExecutionTime += duration;
}
countRetry(statusCode) {
if (!this.isInitialized) {
return;
}
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
const currentStatusCounter = counter.retryCount.find((statusCounter) => statusCode === statusCounter.statusCode);
if (currentStatusCounter) {
currentStatusCounter.count++;
}
else {
counter.retryCount.push({ statusCode: statusCode, count: 1 });
}
}
countThrottle(statusCode) {
if (!this.isInitialized) {
return;
}
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
const currentStatusCounter = counter.throttleCount.find((statusCounter) => statusCode === statusCounter.statusCode);
if (currentStatusCounter) {
currentStatusCounter.count++;
}
else {
counter.throttleCount.push({ statusCode: statusCode, count: 1 });
}
}
countReadFailure() {
if (!this.isInitialized || this.disableNonEssentialStatsbeat) {
return;
}
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
counter.totalReadFailureCount++;
}
countWriteFailure() {
if (!this.isInitialized || this.disableNonEssentialStatsbeat) {
return;
}
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
counter.totalWriteFailureCount++;
}
countException(exceptionType) {
if (!this.isInitialized) {
return;
}
const counter = this.getNetworkStatsbeatCounter(this.endpointUrl, this.host);
const currentErrorCounter = counter.exceptionCount.find((exceptionCounter) => exceptionType.name === exceptionCounter.exceptionType);
if (currentErrorCounter) {
currentErrorCounter.count++;
}
else {
counter.exceptionCount.push({ exceptionType: exceptionType.name, count: 1 });
}
}
// Gets a networkStatsbeat counter if one exists for the given endpoint
getNetworkStatsbeatCounter(endpoint, host) {
// Check if the counter is available
for (let i = 0; i < this.networkStatsbeatCollection.length; i++) {
// Same object
if (endpoint === this.networkStatsbeatCollection[i].endpoint &&
host === this.networkStatsbeatCollection[i].host) {
return this.networkStatsbeatCollection[i];
}
}
// Create a new counter if not found
const newCounter = new NetworkStatsbeat(endpoint, host);
this.networkStatsbeatCollection.push(newCounter);
return newCounter;
}
getShortHost(originalHost) {
let shortHost = originalHost;
try {
const hostRegex = new RegExp(/^https?:\/\/(?:www\.)?([^/.-]+)/);
const res = hostRegex.exec(originalHost);
if (res !== null && res.length > 1) {
shortHost = res[1];
}
shortHost = shortHost.replace(".in.applicationinsights.azure.com", "");
}
catch (error) {
diag.debug("Failed to get the short host name.");
}
return shortHost;
}
}
//# sourceMappingURL=networkStatsbeatMetrics.js.map