diff --git a/README.md b/README.md index 157b7f14..b56f83b9 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,6 @@ const appInsights = new ApplicationInsightsClient(config); | enableAutoCollectExceptions | Sets the state of exception tracking. If true uncaught exceptions will be sent to Application Insights | true| | enableAutoCollectPerformance | Sets the state of performance tracking. If true performance counters will be collected every second and sent to Application Insights | true| | enableAutoCollectStandardMetrics | Sets the state of Standard Metrics tracking. If true Standard Metrics will be collected every minute and sent to Application Insights | true| -| enableAutoCollectHeartbeat | Sets the state of request tracking. If true HeartBeat metric data will be collected every 15 minutes and sent to Application Insights | true| | instrumentations| Allow configuration of OpenTelemetry Instrumentations. | {"http": { enabled: true },"azureSdk": { enabled: false },"mongoDb": { enabled: false },"mySql": { enabled: false },"postgreSql": { enabled: false },"redis": { enabled: false }}| | logInstrumentations| Allow configuration of Log Instrumentations. | {"console": { enabled: false },"bunyan": { enabled: false },"winston": { enabled: false }}| | extendedMetrics | Enable/Disable specific extended Metrics(gc, heap and loop). |{"gc":false,"heap":false,"loop":false}| @@ -125,7 +124,6 @@ All these properties except aadTokenCredential and resource could be configured "azureMonitorExporterConfig": {"connectionString":""}, "samplingRatio": 0.8, "enableAutoCollectExceptions": true, - "enableAutoCollectHeartbeat": true, "instrumentations":{ "azureSdk": { "enabled": false diff --git a/package-lock.json b/package-lock.json index 4372452b..94b7b666 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,8 @@ "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.1", "@opentelemetry/api": "^1.4.1", "@opentelemetry/core": "^1.11.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.39.1", + "@opentelemetry/exporter-trace-otlp-http": "^0.39.1", "@opentelemetry/instrumentation": "^0.35.1", "@opentelemetry/instrumentation-http": "^0.35.1", "@opentelemetry/instrumentation-mongodb": "^0.34.0", @@ -24,6 +26,7 @@ "@opentelemetry/instrumentation-pg": "^0.34.0", "@opentelemetry/instrumentation-redis": "^0.34.1", "@opentelemetry/instrumentation-redis-4": "^0.34.1", + "@opentelemetry/otlp-exporter-base": "^0.39.1", "@opentelemetry/resources": "^1.11.0", "@opentelemetry/sdk-metrics": "^1.11.0", "@opentelemetry/sdk-trace-base": "^1.11.0", @@ -949,6 +952,17 @@ "node": ">=8.0.0" } }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.39.1.tgz", + "integrity": "sha512-9BJ8lMcOzEN0lu+Qji801y707oFO4xT3db6cosPvl+k7ItUHKN5ofWqtSbM9gbt1H4JJ/4/2TVrqI9Rq7hNv6Q==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/@opentelemetry/api-metrics": { "version": "0.33.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api-metrics/-/api-metrics-0.33.0.tgz", @@ -979,6 +993,132 @@ "node": ">=14" } }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.39.1.tgz", + "integrity": "sha512-Uj2i6t5v9aexV03xvVobwLV0Yxn7lQcCxBGN5KKxcs8BTZYSfjdwhrFjsOxvEQ2cXugL0aIzCuTKxrlXYTmFwA==", + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/otlp-exporter-base": "0.39.1", + "@opentelemetry/otlp-transformer": "0.39.1", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/sdk-metrics": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.39.1.tgz", + "integrity": "sha512-AEhnJfVmo1g+7NxszAuf3c6vddld2DGH2+IM4XrPxCklucCsIpuStuC5EVZbCXXXBMpAY+n3t04QMxIQqNrcSw==", + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/otlp-exporter-base": "0.39.1", + "@opentelemetry/otlp-transformer": "0.39.1", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/sdk-trace-base": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.13.0.tgz", + "integrity": "sha512-moTiQtc0uPR1hQLt6gLDJH9IIkeBhgRb71OKjNHZPE1VF45fHtD6nBDi5J/DkTHTwYP5X3kBJLa3xN7ub6J4eg==", + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "engines": { + "node": ">=14" + } + }, "node_modules/@opentelemetry/instrumentation": { "version": "0.35.1", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.35.1.tgz", @@ -1157,6 +1297,114 @@ "node": ">=14" } }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.39.1.tgz", + "integrity": "sha512-Pv5X8fbi6jD/RJBePyn7MnCSuE6MbPB6dl+7YYBWJ5RcMGYMwvLXjd4h2jWsPV2TSUg38H/RoSP0aXvQ06Y7iw==", + "dependencies": { + "@opentelemetry/core": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.39.1.tgz", + "integrity": "sha512-0hgVnXXz5efI382B/24NxD4b6Zxlh7nxCdJkxkdmQMbn0yRiwoq/ZT+QG8eUL6JNzsBAV1WJlF5aJNsL8skHvw==", + "dependencies": { + "@opentelemetry/api-logs": "0.39.1", + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/sdk-logs": "0.39.1", + "@opentelemetry/sdk-metrics": "1.13.0", + "@opentelemetry/sdk-trace-base": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.13.0.tgz", + "integrity": "sha512-moTiQtc0uPR1hQLt6gLDJH9IIkeBhgRb71OKjNHZPE1VF45fHtD6nBDi5J/DkTHTwYP5X3kBJLa3xN7ub6J4eg==", + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "engines": { + "node": ">=14" + } + }, "node_modules/@opentelemetry/propagator-b3": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.12.0.tgz", @@ -1199,15 +1447,108 @@ "node": ">=14" } }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.39.1.tgz", + "integrity": "sha512-/gmgKfZ1ZVFporKuwsewqIyvaUIGpv76JZ7lBpHQQPb37IMpaXO6pdqFI4ebHAWfNIm3akMyhmdtzivcgF3lgw==", + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.5.0", + "@opentelemetry/api-logs": ">=0.38.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "engines": { + "node": ">=14" + } + }, "node_modules/@opentelemetry/sdk-metrics": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.12.0.tgz", - "integrity": "sha512-zOy88Jfk88eTxqu+9ypHLs184dGydJocSWtvWMY10QKVVaxhC3SLKa0uxI/zBtD9S+x0LP65wxrTSfSoUNtCOA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.13.0.tgz", + "integrity": "sha512-MOjZX6AnSOqLliCcZUrb+DQKjAWXBiGeICGbHAGe5w0BB18PJIeIo995lO5JSaFfHpmUMgJButTPfJJD27W3Vg==", "dependencies": { - "@opentelemetry/core": "1.12.0", - "@opentelemetry/resources": "1.12.0", + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", "lodash.merge": "4.6.2" }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", "engines": { "node": ">=14" } @@ -5645,6 +5986,14 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==" }, + "@opentelemetry/api-logs": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.39.1.tgz", + "integrity": "sha512-9BJ8lMcOzEN0lu+Qji801y707oFO4xT3db6cosPvl+k7ItUHKN5ofWqtSbM9gbt1H4JJ/4/2TVrqI9Rq7hNv6Q==", + "requires": { + "@opentelemetry/api": "^1.0.0" + } + }, "@opentelemetry/api-metrics": { "version": "0.33.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api-metrics/-/api-metrics-0.33.0.tgz", @@ -5666,6 +6015,88 @@ "@opentelemetry/semantic-conventions": "1.12.0" } }, + "@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.39.1.tgz", + "integrity": "sha512-Uj2i6t5v9aexV03xvVobwLV0Yxn7lQcCxBGN5KKxcs8BTZYSfjdwhrFjsOxvEQ2cXugL0aIzCuTKxrlXYTmFwA==", + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/otlp-exporter-base": "0.39.1", + "@opentelemetry/otlp-transformer": "0.39.1", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/sdk-metrics": "1.13.0" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "requires": { + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==" + } + } + }, + "@opentelemetry/exporter-trace-otlp-http": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.39.1.tgz", + "integrity": "sha512-AEhnJfVmo1g+7NxszAuf3c6vddld2DGH2+IM4XrPxCklucCsIpuStuC5EVZbCXXXBMpAY+n3t04QMxIQqNrcSw==", + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/otlp-exporter-base": "0.39.1", + "@opentelemetry/otlp-transformer": "0.39.1", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/sdk-trace-base": "1.13.0" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "requires": { + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/sdk-trace-base": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.13.0.tgz", + "integrity": "sha512-moTiQtc0uPR1hQLt6gLDJH9IIkeBhgRb71OKjNHZPE1VF45fHtD6nBDi5J/DkTHTwYP5X3kBJLa3xN7ub6J4eg==", + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==" + } + } + }, "@opentelemetry/instrumentation": { "version": "0.35.1", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.35.1.tgz", @@ -5814,6 +6245,76 @@ } } }, + "@opentelemetry/otlp-exporter-base": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.39.1.tgz", + "integrity": "sha512-Pv5X8fbi6jD/RJBePyn7MnCSuE6MbPB6dl+7YYBWJ5RcMGYMwvLXjd4h2jWsPV2TSUg38H/RoSP0aXvQ06Y7iw==", + "requires": { + "@opentelemetry/core": "1.13.0" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "requires": { + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==" + } + } + }, + "@opentelemetry/otlp-transformer": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.39.1.tgz", + "integrity": "sha512-0hgVnXXz5efI382B/24NxD4b6Zxlh7nxCdJkxkdmQMbn0yRiwoq/ZT+QG8eUL6JNzsBAV1WJlF5aJNsL8skHvw==", + "requires": { + "@opentelemetry/api-logs": "0.39.1", + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/sdk-logs": "0.39.1", + "@opentelemetry/sdk-metrics": "1.13.0", + "@opentelemetry/sdk-trace-base": "1.13.0" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "requires": { + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/sdk-trace-base": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.13.0.tgz", + "integrity": "sha512-moTiQtc0uPR1hQLt6gLDJH9IIkeBhgRb71OKjNHZPE1VF45fHtD6nBDi5J/DkTHTwYP5X3kBJLa3xN7ub6J4eg==", + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==" + } + } + }, "@opentelemetry/propagator-b3": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.12.0.tgz", @@ -5844,14 +6345,71 @@ "@opentelemetry/semantic-conventions": "1.12.0" } }, + "@opentelemetry/sdk-logs": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.39.1.tgz", + "integrity": "sha512-/gmgKfZ1ZVFporKuwsewqIyvaUIGpv76JZ7lBpHQQPb37IMpaXO6pdqFI4ebHAWfNIm3akMyhmdtzivcgF3lgw==", + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "requires": { + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==" + } + } + }, "@opentelemetry/sdk-metrics": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.12.0.tgz", - "integrity": "sha512-zOy88Jfk88eTxqu+9ypHLs184dGydJocSWtvWMY10QKVVaxhC3SLKa0uxI/zBtD9S+x0LP65wxrTSfSoUNtCOA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.13.0.tgz", + "integrity": "sha512-MOjZX6AnSOqLliCcZUrb+DQKjAWXBiGeICGbHAGe5w0BB18PJIeIo995lO5JSaFfHpmUMgJButTPfJJD27W3Vg==", "requires": { - "@opentelemetry/core": "1.12.0", - "@opentelemetry/resources": "1.12.0", + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", "lodash.merge": "4.6.2" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "requires": { + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==" + } } }, "@opentelemetry/sdk-trace-base": { diff --git a/package.json b/package.json index 3b5fcf15..6ab70acc 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,8 @@ "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.1", "@opentelemetry/api": "^1.4.1", "@opentelemetry/core": "^1.11.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.39.1", + "@opentelemetry/exporter-trace-otlp-http": "^0.39.1", "@opentelemetry/instrumentation": "^0.35.1", "@opentelemetry/instrumentation-http": "^0.35.1", "@opentelemetry/instrumentation-mongodb": "^0.34.0", @@ -80,6 +82,7 @@ "@opentelemetry/instrumentation-pg": "^0.34.0", "@opentelemetry/instrumentation-redis": "^0.34.1", "@opentelemetry/instrumentation-redis-4": "^0.34.1", + "@opentelemetry/otlp-exporter-base": "^0.39.1", "@opentelemetry/resources": "^1.11.0", "@opentelemetry/sdk-metrics": "^1.11.0", "@opentelemetry/sdk-trace-base": "^1.11.0", diff --git a/src/agent/agentLoader.ts b/src/agent/agentLoader.ts index ab0c38cf..b4e77360 100644 --- a/src/agent/agentLoader.ts +++ b/src/agent/agentLoader.ts @@ -32,7 +32,6 @@ export class AgentLoader { this._config = new ApplicationInsightsConfig(); this._config.azureMonitorExporterConfig.disableOfflineStorage = false; this._config.enableAutoCollectExceptions = true; - this._config.enableAutoCollectHeartbeat = true; this._config.enableAutoCollectPerformance = true; this._config.enableAutoCollectStandardMetrics = true; this._config.samplingRatio = 1; // Sample all telemetry by default diff --git a/src/metrics/handlers/customMetricsHandler.ts b/src/metrics/handlers/customMetricsHandler.ts index f5c752fb..fd7e0e5f 100644 --- a/src/metrics/handlers/customMetricsHandler.ts +++ b/src/metrics/handlers/customMetricsHandler.ts @@ -6,14 +6,16 @@ import { PeriodicExportingMetricReader, PeriodicExportingMetricReaderOptions, } from "@opentelemetry/sdk-metrics"; +import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; import { ApplicationInsightsConfig } from "../../shared"; + export class CustomMetricsHandler { private _config: ApplicationInsightsConfig; private _collectionInterval = 60000; // 60 seconds private _meterProvider: MeterProvider; - private _azureExporter: AzureMonitorMetricExporter; - private _metricReader: PeriodicExportingMetricReader; + private _azureMonitorExporter: AzureMonitorMetricExporter; + private _otlpExporter: OTLPMetricExporter; private _meter: Meter; constructor(config: ApplicationInsightsConfig, options?: { collectionInterval: number }) { @@ -22,13 +24,22 @@ export class CustomMetricsHandler { resource: this._config.resource, }; this._meterProvider = new MeterProvider(meterProviderConfig); - this._azureExporter = new AzureMonitorMetricExporter(this._config.azureMonitorExporterConfig); + this._azureMonitorExporter = new AzureMonitorMetricExporter(this._config.azureMonitorExporterConfig); const metricReaderOptions: PeriodicExportingMetricReaderOptions = { - exporter: this._azureExporter as any, + exporter: this._azureMonitorExporter, exportIntervalMillis: options?.collectionInterval || this._collectionInterval, }; - this._metricReader = new PeriodicExportingMetricReader(metricReaderOptions); - this._meterProvider.addMetricReader(this._metricReader); + const azureMonitorMetricReader = new PeriodicExportingMetricReader(metricReaderOptions); + this._meterProvider.addMetricReader(azureMonitorMetricReader); + + if (config.otlpMetricExporterConfig?.enabled) { + this._otlpExporter = new OTLPMetricExporter(config.otlpMetricExporterConfig.baseConfig); + const otlpMetricReader = new PeriodicExportingMetricReader({ + exporter: this._otlpExporter, + exportIntervalMillis: options?.collectionInterval || this._collectionInterval, + }); + this._meterProvider.addMetricReader(otlpMetricReader); + } this._meter = this._meterProvider.getMeter("ApplicationInsightsCustomMetricsMeter"); } diff --git a/src/metrics/handlers/heartBeatHandler.ts b/src/metrics/handlers/heartBeatHandler.ts deleted file mode 100644 index 2a9a6114..00000000 --- a/src/metrics/handlers/heartBeatHandler.ts +++ /dev/null @@ -1,113 +0,0 @@ -import * as crypto from "crypto"; -import * as os from "os"; -import { AzureMonitorMetricExporter } from "@azure/monitor-opentelemetry-exporter"; -import { - Meter, - ObservableCallback, - ObservableGauge, - ObservableResult, -} from "@opentelemetry/api"; -import { - MeterProvider, - PeriodicExportingMetricReader, - PeriodicExportingMetricReaderOptions, -} from "@opentelemetry/sdk-metrics"; -import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; -import { ApplicationInsightsConfig } from "../../shared"; - -const HeartBeatMetricName = "HeartbeatState"; - -export class HeartBeatHandler { - private _collectionInterval = 900000; - private _config: ApplicationInsightsConfig; - private _meterProvider: MeterProvider; - private _azureExporter: AzureMonitorMetricExporter; - private _metricReader: PeriodicExportingMetricReader; - private _meter: Meter; - private _metricGauge: ObservableGauge; - private _metricGaugeCallback: ObservableCallback; - private _machineProperties: { [key: string]: string }; - private _uniqueProcessId: string; - - constructor(config: ApplicationInsightsConfig, options?: { collectionInterval: number }) { - this._config = config; - this._meterProvider = new MeterProvider(); - this._azureExporter = new AzureMonitorMetricExporter(this._config.azureMonitorExporterConfig); - const metricReaderOptions: PeriodicExportingMetricReaderOptions = { - exporter: this._azureExporter as any, - exportIntervalMillis: options?.collectionInterval || this._collectionInterval, - }; - this._metricReader = new PeriodicExportingMetricReader(metricReaderOptions); - this._meterProvider.addMetricReader(this._metricReader); - this._meter = this._meterProvider.getMeter("ApplicationInsightsHeartBeatMeter"); - this._metricGauge = this._meter.createObservableGauge(HeartBeatMetricName); - this._metricGaugeCallback = this._trackHeartBeat.bind(this); - this._metricGauge.addCallback(this._metricGaugeCallback); - } - - /** - * @deprecated This should not be used - */ - public enable(isEnabled: boolean) { - // No Op - } - - /** - * @deprecated This should not be used - */ - public start() { - // No Op - } - - public async shutdown(): Promise { - this._metricGauge.removeCallback(this._metricGaugeCallback); - await this._meterProvider.shutdown(); - } - - public async flush(): Promise { - await this._meterProvider.forceFlush(); - } - - private async _trackHeartBeat(observableResult: ObservableResult) { - this._machineProperties = this._getMachineProperties(); - observableResult.observe(0, this._machineProperties); - } - - private _getMachineProperties(): { [key: string]: string } { - const properties: { [key: string]: string } = {}; - const sdkVersion = - String( - this._config.resource.attributes[ - SemanticResourceAttributes.TELEMETRY_SDK_VERSION - ] - ) || null; - properties["sdkVersion"] = sdkVersion; - properties["osType"] = os.type(); - properties["osVersion"] = os.release(); - // Random GUID that would help in analysis when app has stopped and restarted. - if (!this._uniqueProcessId) { - this._uniqueProcessId = crypto.randomBytes(16).toString("hex"); - } - properties["processSessionId"] = this._uniqueProcessId; - - if (process.env.WEBSITE_SITE_NAME) { - properties["appSrv_SiteName"] = process.env.WEBSITE_SITE_NAME; - } - if (process.env.WEBSITE_HOME_STAMPNAME) { - properties["appSrv_wsStamp"] = process.env.WEBSITE_HOME_STAMPNAME; - } - if (process.env.WEBSITE_HOSTNAME) { - properties["appSrv_wsHost"] = process.env.WEBSITE_HOSTNAME; - } - if (process.env.WEBSITE_OWNER_NAME) { - properties["appSrv_wsOwner"] = process.env.WEBSITE_OWNER_NAME; - } - if (process.env.WEBSITE_RESOURCE_GROUP) { - properties["appSrv_ResourceGroup"] = process.env.WEBSITE_RESOURCE_GROUP; - } - if (process.env.WEBSITE_SLOT_NAME) { - properties["appSrv_SlotName"] = process.env.WEBSITE_SLOT_NAME; - } - return properties; - } -} diff --git a/src/metrics/handlers/index.ts b/src/metrics/handlers/index.ts index 26e3b51d..c6528aa6 100644 --- a/src/metrics/handlers/index.ts +++ b/src/metrics/handlers/index.ts @@ -2,6 +2,5 @@ // Licensed under the MIT license. export { CustomMetricsHandler } from "./customMetricsHandler"; -export { HeartBeatHandler } from "./heartBeatHandler"; export { PerformanceCounterMetricsHandler } from "./performanceCounterMetricsHandler"; export { StandardMetricsHandler } from "./standardMetricsHandler"; diff --git a/src/metrics/handlers/performanceCounterMetricsHandler.ts b/src/metrics/handlers/performanceCounterMetricsHandler.ts index f8bb3e8e..bf7af984 100644 --- a/src/metrics/handlers/performanceCounterMetricsHandler.ts +++ b/src/metrics/handlers/performanceCounterMetricsHandler.ts @@ -10,24 +10,21 @@ import { PeriodicExportingMetricReaderOptions, View, } from "@opentelemetry/sdk-metrics"; +import { ReadableSpan } from "@opentelemetry/sdk-trace-base"; import { MetricName, - NativeMetricsCounter, PerformanceCounter, } from "../types"; import { ProcessMetrics } from "../collection/processMetrics"; import { RequestMetrics } from "../collection/requestMetrics"; import { ApplicationInsightsConfig } from "../../shared"; -import { NativePerformanceMetrics } from "../collection/nativePerformanceMetrics"; -import { ReadableSpan } from "@opentelemetry/sdk-trace-base"; export class PerformanceCounterMetricsHandler { private _config: ApplicationInsightsConfig; private _collectionInterval = 60000; // 60 seconds private _meterProvider: MeterProvider; - private _azureExporter: AzureMonitorMetricExporter; - private _metricReader: PeriodicExportingMetricReader; + private _azureMonitorExporter: AzureMonitorMetricExporter; private _meter: Meter; private _processMetrics: ProcessMetrics; private _requestMetrics: RequestMetrics; @@ -39,22 +36,22 @@ export class PerformanceCounterMetricsHandler { views: this._getViews(), }; this._meterProvider = new MeterProvider(meterProviderConfig); - this._azureExporter = new AzureMonitorMetricExporter(this._config.azureMonitorExporterConfig); + this._azureMonitorExporter = new AzureMonitorMetricExporter(this._config.azureMonitorExporterConfig); const metricReaderOptions: PeriodicExportingMetricReaderOptions = { - exporter: this._azureExporter as any, + exporter: this._azureMonitorExporter, exportIntervalMillis: options?.collectionInterval || this._collectionInterval, }; - this._metricReader = new PeriodicExportingMetricReader(metricReaderOptions); - this._meterProvider.addMetricReader(this._metricReader); + const azureMonitorMetricReader = new PeriodicExportingMetricReader(metricReaderOptions); + this._meterProvider.addMetricReader(azureMonitorMetricReader); this._meter = this._meterProvider.getMeter("ApplicationInsightsPerfMetricsMeter"); this._processMetrics = new ProcessMetrics(this._meter); this._requestMetrics = new RequestMetrics(this._meter); } - /** - * @deprecated This should not be used - */ - public start() { + /** + * @deprecated This should not be used + */ + public start() { // No Op } diff --git a/src/metrics/handlers/standardMetricsHandler.ts b/src/metrics/handlers/standardMetricsHandler.ts index 57adb8dd..e996ac92 100644 --- a/src/metrics/handlers/standardMetricsHandler.ts +++ b/src/metrics/handlers/standardMetricsHandler.ts @@ -10,19 +10,21 @@ import { } from "@opentelemetry/sdk-metrics"; import { ReadableSpan } from "@opentelemetry/sdk-trace-base"; import { SemanticAttributes, SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; +import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; import { ApplicationInsightsConfig } from "../../shared"; import { DependencyMetrics } from "../collection/dependencyMetrics"; import { ExceptionMetrics } from "../collection/exceptionMetrics"; import { RequestMetrics } from "../collection/requestMetrics"; import { TraceMetrics } from "../collection/traceMetrics"; -import { IMetricDependencyDimensions, IMetricRequestDimensions, IMetricTraceDimensions, IStandardMetricBaseDimensions, MetricName, PreAggregatedMetricPropertyNames, StandardMetric, StandardMetricIds } from "../types"; +import { IMetricDependencyDimensions, IMetricRequestDimensions, IMetricTraceDimensions, IStandardMetricBaseDimensions, MetricName, StandardMetric, StandardMetricIds } from "../types"; + export class StandardMetricsHandler { private _config: ApplicationInsightsConfig; private _collectionInterval = 60000; // 60 seconds private _meterProvider: MeterProvider; - private _azureExporter: AzureMonitorMetricExporter; - private _metricReader: PeriodicExportingMetricReader; + private _azureMonitorExporter: AzureMonitorMetricExporter; + private _otlpExporter: OTLPMetricExporter; private _meter: Meter; private _dependencyMetrics: DependencyMetrics; private _requestMetrics: RequestMetrics; @@ -36,13 +38,23 @@ export class StandardMetricsHandler { views: this._getViews(), }; this._meterProvider = new MeterProvider(meterProviderConfig); - this._azureExporter = new AzureMonitorMetricExporter(this._config.azureMonitorExporterConfig); + this._azureMonitorExporter = new AzureMonitorMetricExporter(this._config.azureMonitorExporterConfig); const metricReaderOptions: PeriodicExportingMetricReaderOptions = { - exporter: this._azureExporter as any, + exporter: this._azureMonitorExporter, exportIntervalMillis: options?.collectionInterval || this._collectionInterval, }; - this._metricReader = new PeriodicExportingMetricReader(metricReaderOptions); - this._meterProvider.addMetricReader(this._metricReader); + const azureMonitorMetricReader = new PeriodicExportingMetricReader(metricReaderOptions); + this._meterProvider.addMetricReader(azureMonitorMetricReader); + + if (config.otlpMetricExporterConfig?.enabled) { + this._otlpExporter = new OTLPMetricExporter(config.otlpMetricExporterConfig.baseConfig); + const otlpMetricReader = new PeriodicExportingMetricReader({ + exporter: this._otlpExporter, + exportIntervalMillis: options?.collectionInterval || this._collectionInterval, + }); + this._meterProvider.addMetricReader(otlpMetricReader); + } + this._meter = this._meterProvider.getMeter("ApplicationInsightsStandardMetricsMeter"); this._requestMetrics = new RequestMetrics(this._meter); this._dependencyMetrics = new DependencyMetrics(this._meter); diff --git a/src/metrics/metricHandler.ts b/src/metrics/metricHandler.ts index 9a9761fe..ce730ade 100644 --- a/src/metrics/metricHandler.ts +++ b/src/metrics/metricHandler.ts @@ -6,7 +6,6 @@ import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions" import { ApplicationInsightsConfig } from "../shared"; import { CustomMetricsHandler, - HeartBeatHandler, StandardMetricsHandler, PerformanceCounterMetricsHandler, } from "./handlers"; @@ -16,7 +15,6 @@ export class MetricHandler { private _config: ApplicationInsightsConfig; private _perfCounterMetricsHandler: PerformanceCounterMetricsHandler; private _standardMetricsHandler: StandardMetricsHandler; - private _heartbeatHandler: HeartBeatHandler; private _customMetricsHandler: CustomMetricsHandler; constructor(config: ApplicationInsightsConfig) { @@ -28,9 +26,6 @@ export class MetricHandler { if (this._config.enableAutoCollectPerformance) { this._perfCounterMetricsHandler = new PerformanceCounterMetricsHandler(this._config); } - if (this._config.enableAutoCollectHeartbeat) { - this._heartbeatHandler = new HeartBeatHandler(this._config); - } } /** @@ -44,12 +39,10 @@ export class MetricHandler { this._customMetricsHandler?.shutdown(); this._perfCounterMetricsHandler?.shutdown(); this._standardMetricsHandler?.shutdown(); - this._heartbeatHandler?.shutdown(); } public async flush(): Promise { await this._customMetricsHandler?.flush(); - await this._heartbeatHandler?.flush(); await this._standardMetricsHandler?.flush(); await this._perfCounterMetricsHandler?.flush(); } diff --git a/src/metrics/statsbeat/statsbeat.ts b/src/metrics/statsbeat/statsbeat.ts index d0c29e21..516cf8e6 100644 --- a/src/metrics/statsbeat/statsbeat.ts +++ b/src/metrics/statsbeat/statsbeat.ts @@ -82,7 +82,6 @@ export class Statsbeat { this._azureVm = new AzureVirtualMachine(); this._statsbeatConfig = new ApplicationInsightsConfig(); this._statsbeatConfig.connectionString = this._connectionString; - this._statsbeatConfig.enableAutoCollectHeartbeat = false; this._statsbeatConfig.enableAutoCollectPerformance = false; this._statsbeatConfig.enableAutoCollectStandardMetrics = false; diff --git a/src/shared/configuration/applicationInsightsConfig.ts b/src/shared/configuration/applicationInsightsConfig.ts index c7e07c86..c46f51be 100644 --- a/src/shared/configuration/applicationInsightsConfig.ts +++ b/src/shared/configuration/applicationInsightsConfig.ts @@ -10,6 +10,7 @@ import { InstrumentationsConfig, LEGACY_ENV_IKEY, LogInstrumentationsConfig, + OTLPExporterConfig, } from "./types"; import { JsonConfig } from "./jsonConfig"; import { Logger } from "../logging"; @@ -25,14 +26,13 @@ const DEFAULT_ROLE_NAME = "Web"; export class ApplicationInsightsConfig implements IConfig { private _disableStatsbeat: boolean; private _resource?: Resource; - - /** Azure Monitor Exporter Configuration */ public azureMonitorExporterConfig?: AzureMonitorExporterOptions; + public otlpTraceExporterConfig?: OTLPExporterConfig; + public otlpMetricExporterConfig?: OTLPExporterConfig; public samplingRatio: number; public enableAutoCollectExceptions: boolean; public enableAutoCollectPerformance: boolean; public enableAutoCollectStandardMetrics: boolean; - public enableAutoCollectHeartbeat: boolean; public extendedMetrics: { [type: string]: boolean }; public instrumentations: InstrumentationsConfig; public logInstrumentations: LogInstrumentationsConfig; @@ -78,6 +78,8 @@ export class ApplicationInsightsConfig implements IConfig { constructor() { this.azureMonitorExporterConfig = {}; + this.otlpMetricExporterConfig = {}; + this.otlpTraceExporterConfig = {}; // Load config values from env variables and JSON if available this.azureMonitorExporterConfig.connectionString = process.env[ENV_connectionString]; this._disableStatsbeat = !!process.env[ENV_noStatsbeat]; @@ -132,8 +134,6 @@ export class ApplicationInsightsConfig implements IConfig { this.enableAutoCollectExceptions !== undefined ? this.enableAutoCollectExceptions : true; - this.enableAutoCollectHeartbeat = - this.enableAutoCollectHeartbeat !== undefined ? this.enableAutoCollectHeartbeat : true; this.enableAutoCollectPerformance = this.enableAutoCollectPerformance !== undefined ? this.enableAutoCollectPerformance @@ -170,7 +170,7 @@ export class ApplicationInsightsConfig implements IConfig { let detectResourceConfig: ResourceDetectionConfig = { detectors: [envDetectorSync] }; - const envResource =detectResourcesSync(detectResourceConfig); + const envResource = detectResourcesSync(detectResourceConfig); resource = resource.merge(envResource); resource.attributes[SemanticResourceAttributes.SERVICE_NAME] = resource.attributes[SemanticResourceAttributes.SERVICE_NAME] || DEFAULT_ROLE_NAME; @@ -200,6 +200,14 @@ export class ApplicationInsightsConfig implements IConfig { jsonConfig.azureMonitorExporterConfig !== undefined ? jsonConfig.azureMonitorExporterConfig : this.azureMonitorExporterConfig; + this.otlpMetricExporterConfig = + jsonConfig.otlpMetricExporterConfig !== undefined + ? jsonConfig.otlpMetricExporterConfig + : this.otlpMetricExporterConfig; + this.otlpTraceExporterConfig = + jsonConfig.otlpTraceExporterConfig !== undefined + ? jsonConfig.otlpTraceExporterConfig + : this.otlpTraceExporterConfig; this.connectionString = jsonConfig.connectionString !== undefined ? jsonConfig.connectionString @@ -208,10 +216,6 @@ export class ApplicationInsightsConfig implements IConfig { jsonConfig.enableAutoCollectExceptions !== undefined ? jsonConfig.enableAutoCollectExceptions : this.enableAutoCollectExceptions; - this.enableAutoCollectHeartbeat = - jsonConfig.enableAutoCollectHeartbeat !== undefined - ? jsonConfig.enableAutoCollectHeartbeat - : this.enableAutoCollectHeartbeat; this.enableAutoCollectPerformance = jsonConfig.enableAutoCollectPerformance !== undefined ? jsonConfig.enableAutoCollectPerformance diff --git a/src/shared/configuration/jsonConfig.ts b/src/shared/configuration/jsonConfig.ts index 8229f5ea..12817b50 100644 --- a/src/shared/configuration/jsonConfig.ts +++ b/src/shared/configuration/jsonConfig.ts @@ -2,20 +2,19 @@ import * as fs from "fs"; import * as path from "path"; import { AzureMonitorExporterOptions } from "@azure/monitor-opentelemetry-exporter"; import { Logger } from "../logging"; -import { IConfig, InstrumentationsConfig, LogInstrumentationsConfig } from "./types"; +import { IConfig, InstrumentationsConfig, LogInstrumentationsConfig, OTLPExporterConfig } from "./types"; const ENV_CONFIGURATION_FILE = "APPLICATIONINSIGHTS_CONFIGURATION_FILE"; export class JsonConfig implements IConfig { private static _instance: JsonConfig; - - /** Azure Monitor Exporter Configuration */ public azureMonitorExporterConfig?: AzureMonitorExporterOptions; + public otlpTraceExporterConfig?: OTLPExporterConfig; + public otlpMetricExporterConfig?: OTLPExporterConfig; public samplingRatio: number; public enableAutoCollectExceptions: boolean; public enableAutoCollectPerformance: boolean; public enableAutoCollectStandardMetrics: boolean; - public enableAutoCollectHeartbeat: boolean; public instrumentations: InstrumentationsConfig; public logInstrumentations: LogInstrumentationsConfig; public extendedMetrics: { [type: string]: boolean }; @@ -61,7 +60,9 @@ export class JsonConfig implements IConfig { try { const jsonConfig: IConfig = JSON.parse(fs.readFileSync(tempDir, "utf8")); this.azureMonitorExporterConfig = jsonConfig.azureMonitorExporterConfig; - + this.otlpMetricExporterConfig = jsonConfig.otlpMetricExporterConfig; + this.otlpTraceExporterConfig = jsonConfig.otlpTraceExporterConfig; + if (jsonConfig.connectionString !== undefined) { this.connectionString = jsonConfig.connectionString; } @@ -69,7 +70,6 @@ export class JsonConfig implements IConfig { this.enableAutoCollectExceptions = jsonConfig.enableAutoCollectExceptions; this.enableAutoCollectPerformance = jsonConfig.enableAutoCollectPerformance; this.enableAutoCollectStandardMetrics = jsonConfig.enableAutoCollectStandardMetrics; - this.enableAutoCollectHeartbeat = jsonConfig.enableAutoCollectHeartbeat; this.disableOfflineStorage = jsonConfig.disableOfflineStorage; this.storageDirectory = jsonConfig.storageDirectory; this.instrumentations = jsonConfig.instrumentations; diff --git a/src/shared/configuration/types.ts b/src/shared/configuration/types.ts index 600dec63..99270b41 100644 --- a/src/shared/configuration/types.ts +++ b/src/shared/configuration/types.ts @@ -1,5 +1,6 @@ import { TokenCredential } from "@azure/core-auth"; import { AzureMonitorExporterOptions } from "@azure/monitor-opentelemetry-exporter"; +import { OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; import { InstrumentationConfig } from "@opentelemetry/instrumentation"; import { Resource } from "@opentelemetry/resources"; @@ -11,6 +12,10 @@ export const ENV_QUCKPULSE_HOST = "APPINSIGHTS_QUICKPULSE_HOST"; export interface IConfig { /** Azure Monitor Exporter Configuration */ azureMonitorExporterConfig?: AzureMonitorExporterOptions; + /** OTLP Trace Exporter Configuration */ + otlpTraceExporterConfig?: OTLPExporterConfig; + /** OTLP Metric Exporter Configuration */ + otlpMetricExporterConfig?: OTLPExporterConfig; /** The rate of telemetry items tracked that should be transmitted (Default 1.0) */ samplingRatio?: number; /** @@ -28,11 +33,6 @@ export interface IConfig { * if true Standard metrics will be collected every minute and sent to Application Insights */ enableAutoCollectStandardMetrics?: boolean; - /** - * Sets the state of request tracking (enabled by default) - * if true HeartBeat metric data will be collected every 15 minutes and sent to Application Insights - */ - enableAutoCollectHeartbeat?: boolean; /** * OpenTelemetry Instrumentations configuration included as part of Application Insights (azureSdk, http, mongoDb, mySql, postgreSql, redis, redis4) */ @@ -89,3 +89,8 @@ export const enum ExtendedMetricType { heap = "heap", loop = "loop", } + +export interface OTLPExporterConfig { + baseConfig?: OTLPExporterNodeConfigBase, + enabled?: boolean +} diff --git a/src/traces/traceHandler.ts b/src/traces/traceHandler.ts index bf047e97..cc4bbe4c 100644 --- a/src/traces/traceHandler.ts +++ b/src/traces/traceHandler.ts @@ -1,10 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. import { RequestOptions } from "http"; -import { - AzureMonitorExporterOptions, - AzureMonitorTraceExporter, -} from "@azure/monitor-opentelemetry-exporter"; +import { AzureMonitorTraceExporter, } from "@azure/monitor-opentelemetry-exporter"; import { Instrumentation } from "@opentelemetry/instrumentation"; import { createAzureSdkInstrumentation } from "@azure/opentelemetry-instrumentation-azure-sdk"; import { MongoDBInstrumentation } from "@opentelemetry/instrumentation-mongodb"; @@ -15,6 +12,7 @@ import { RedisInstrumentation as Redis4Instrumentation } from "@opentelemetry/in import { NodeTracerProvider, NodeTracerConfig } from "@opentelemetry/sdk-trace-node"; import { BatchSpanProcessor, BufferConfig, SpanProcessor, Tracer } from "@opentelemetry/sdk-trace-base"; import { HttpInstrumentation, HttpInstrumentationConfig, IgnoreOutgoingRequestFunction } from "@opentelemetry/instrumentation-http"; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { ApplicationInsightsSampler } from "./applicationInsightsSampler"; import { ApplicationInsightsConfig } from "../shared"; import { MetricHandler } from "../metrics/metricHandler"; @@ -24,8 +22,8 @@ import { Util } from "../shared/util"; export class TraceHandler { - private _exporter: AzureMonitorTraceExporter; - private _spanProcessor: BatchSpanProcessor; + private _azureMonitorExporter: AzureMonitorTraceExporter; + private _otlpExporter: OTLPTraceExporter; private _config: ApplicationInsightsConfig; private _metricHandler: MetricHandler; private _instrumentations: Instrumentation[]; @@ -54,25 +52,26 @@ export class TraceHandler { forceFlushTimeoutMillis: 30000, }; this._tracerProvider = new NodeTracerProvider(tracerConfig); - const exporterConfig: AzureMonitorExporterOptions = { - connectionString: this._config.connectionString, - aadTokenCredential: this._config.aadTokenCredential, - storageDirectory: this._config.storageDirectory, - disableOfflineStorage: this._config.disableOfflineStorage, - }; - this._exporter = new AzureMonitorTraceExporter(exporterConfig); + this._azureMonitorExporter = new AzureMonitorTraceExporter(config.azureMonitorExporterConfig); const bufferConfig: BufferConfig = { maxExportBatchSize: 512, scheduledDelayMillis: 5000, exportTimeoutMillis: 30000, maxQueueSize: 2048, }; - this._spanProcessor = new BatchSpanProcessor(this._exporter, bufferConfig); - this._tracerProvider.addSpanProcessor(this._spanProcessor); + let azureMonitorSpanProcessor = new BatchSpanProcessor(this._azureMonitorExporter, bufferConfig); + this._tracerProvider.addSpanProcessor(azureMonitorSpanProcessor); if (this._metricHandler) { const azureSpanProcessor = new AzureSpanProcessor(this._metricHandler); this._tracerProvider.addSpanProcessor(azureSpanProcessor); } + + if (config.otlpTraceExporterConfig?.enabled) { + this._otlpExporter = new OTLPTraceExporter(config.otlpTraceExporterConfig.baseConfig); + let otlpSpanProcessor = new BatchSpanProcessor(this._otlpExporter, bufferConfig); + this._tracerProvider.addSpanProcessor(otlpSpanProcessor); + } + this._tracerProvider.register(); // TODO: Check for conflicts with multiple handlers available this._tracer = this._tracerProvider.getTracer("ApplicationInsightsTracer"); diff --git a/test/unitTests/metrics/customMetricsHandler.tests.ts b/test/unitTests/metrics/customMetricsHandler.tests.ts index 1995d1cc..90ddf6ae 100644 --- a/test/unitTests/metrics/customMetricsHandler.tests.ts +++ b/test/unitTests/metrics/customMetricsHandler.tests.ts @@ -8,12 +8,14 @@ import { ExportResultCode } from "@opentelemetry/core"; describe("#CustomMetricsHandler", () => { let autoCollect: CustomMetricsHandler; let exportStub: sinon.SinonStub; + let otlpExportStub: sinon.SinonStub; before(() => { const config = new ApplicationInsightsConfig(); config.azureMonitorExporterConfig.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; + config.otlpMetricExporterConfig.enabled = true; autoCollect = new CustomMetricsHandler(config, { collectionInterval: 100 }); - exportStub = sinon.stub(autoCollect["_azureExporter"], "export").callsFake( + exportStub = sinon.stub(autoCollect["_azureMonitorExporter"], "export").callsFake( (spans: any, resultCallback: any) => new Promise((resolve, reject) => { resultCallback({ @@ -22,15 +24,26 @@ describe("#CustomMetricsHandler", () => { resolve(); }) ); + otlpExportStub = sinon.stub(autoCollect["_otlpExporter"], "export").callsFake( + (spans: any, resultCallback: any) => + new Promise((resolve, reject) => { + resultCallback({ + code: ExportResultCode.SUCCESS, + }); + resolve(null); + }) + ); }); afterEach(() => { exportStub.resetHistory(); + otlpExportStub.resetHistory(); }); after(() => { autoCollect.shutdown(); exportStub.restore(); + otlpExportStub.restore(); }); it("should create a meter", () => { @@ -48,6 +61,10 @@ describe("#CustomMetricsHandler", () => { assert.strictEqual(metrics.length, 1, "metrics count"); assert.equal(metrics[0].descriptor.name, "testCounter"); assert.equal(metrics[0].descriptor.description, "testDescription"); + + assert.ok(otlpExportStub.called); + assert.strictEqual(otlpExportStub.args[0][0].scopeMetrics.length, 1, "scopeMetrics count"); + assert.strictEqual(otlpExportStub.args[0][0].scopeMetrics[0].metrics.length, 1, "metrics count"); }); it("should not collect when disabled", async () => { diff --git a/test/unitTests/metrics/heartbeat.tests.ts b/test/unitTests/metrics/heartbeat.tests.ts deleted file mode 100644 index 24d9b3e1..00000000 --- a/test/unitTests/metrics/heartbeat.tests.ts +++ /dev/null @@ -1,169 +0,0 @@ -import * as assert from "assert"; -import * as sinon from "sinon"; -import * as os from "os"; - -import { HeartBeatHandler } from "../../../src/metrics/handlers/heartBeatHandler"; -import { ApplicationInsightsConfig } from "../../../src/shared"; -import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; -import { ExportResultCode } from "@opentelemetry/core"; - -describe("AutoCollection/HeartBeat", () => { - let originalEnv: NodeJS.ProcessEnv; - let config: ApplicationInsightsConfig; - let heartbeat: HeartBeatHandler; - let exportStub: sinon.SinonStub; - - before(() => { - config = new ApplicationInsightsConfig(); - config.azureMonitorExporterConfig.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; - heartbeat = new HeartBeatHandler(config, { collectionInterval: 100 }); - exportStub = sinon.stub(heartbeat["_azureExporter"], "export").callsFake( - (spans: any, resultCallback: any) => - new Promise((resolve, reject) => { - resultCallback({ - code: ExportResultCode.SUCCESS, - }); - resolve(); - }) - ); - }); - - beforeEach(() => { - originalEnv = process.env; - }); - - afterEach(() => { - process.env = originalEnv; - exportStub.resetHistory(); - }); - - after(() => { - exportStub.restore(); - heartbeat.shutdown(); - }); - - describe("#Metrics", () => { - it("should create instruments", () => { - assert.ok(heartbeat["_metricGauge"], "_metricGauge not available"); - }); - - it("should observe instruments during collection", async () => { - await new Promise((resolve) => setTimeout(resolve, 120)); - assert.ok(exportStub.called); - const resourceMetrics = exportStub.args[0][0]; - const scopeMetrics = resourceMetrics.scopeMetrics; - assert.strictEqual(scopeMetrics.length, 1, "scopeMetrics count"); - const metrics = scopeMetrics[0].metrics; - assert.strictEqual(metrics.length, 1, "metrics count"); - assert.equal(metrics[0].descriptor.name, "HeartbeatState"); - }); - - it("should not collect when shutdown", async () => { - heartbeat.shutdown(); - await new Promise((resolve) => setTimeout(resolve, 120)); - assert.ok(exportStub.notCalled); - }); - }); - - describe("#_getMachineProperties()", () => { - it("should read correct web app values from environment variables", () => { - const env1 = <{ [id: string]: string }>{}; - env1["WEBSITE_SITE_NAME"] = "site_name"; - env1["WEBSITE_HOME_STAMPNAME"] = "stamp_name"; - env1["WEBSITE_HOSTNAME"] = "host_name"; - env1["WEBSITE_OWNER_NAME"] = "owner_name"; - env1["WEBSITE_RESOURCE_GROUP"] = "resource_group"; - env1["WEBSITE_SLOT_NAME"] = "slot_name"; - process.env = env1; - - const properties = heartbeat["_getMachineProperties"](); - const keys = Object.keys(properties); - assert.equal( - keys.length, - 10, - "should have 10 kv pairs added when resource type is appSrv" - ); - assert.equal(keys[0], "sdkVersion", "sdk should be added as a key"); - assert.equal(keys[1], "osType", "osType should be added as a key"); - assert.equal(keys[2], "osVersion", "osVersion should be added as a key"); - assert.equal(keys[3], "processSessionId", "processSessionId should be added as a key"); - assert.equal( - keys[4], - "appSrv_SiteName", - "appSrv_SiteName should be added as a key" - ); - assert.equal( - keys[5], - "appSrv_wsStamp", - "appSrv_wsStamp should be added as a key" - ); - assert.equal( - keys[6], - "appSrv_wsHost", - "appSrv_wsHost should be added as a key" - ); - assert.equal( - keys[7], - "appSrv_wsOwner", - "appSrv_wsOwner should be added as a key" - ); - assert.equal( - keys[8], - "appSrv_ResourceGroup", - "appSrv_ResourceGroup should be added as a key" - ); - assert.equal( - keys[9], - "appSrv_SlotName", - "appSrv_SlotName should be added as a key" - ); - assert.equal( - properties["sdkVersion"], - config.resource.attributes[ - SemanticResourceAttributes.TELEMETRY_SDK_VERSION - ], - "sdk version should be read from Context" - ); - assert.equal( - properties["osType"], - os.type(), - "osType should be read from os library" - ); - assert.equal( - properties["osVersion"], - os.release(), - "osVersion should be read from os library" - ); - assert.equal( - properties["appSrv_SiteName"], - "site_name", - "appSrv_SiteName should be read from environment variable" - ); - assert.equal( - properties["appSrv_wsStamp"], - "stamp_name", - "appSrv_wsStamp should be read from environment variable" - ); - assert.equal( - properties["appSrv_wsHost"], - "host_name", - "appSrv_wsHost should be read from environment variable" - ); - assert.equal( - properties["appSrv_wsOwner"], - "owner_name", - "appSrv_wsOwner should be read from environment variable" - ); - assert.equal( - properties["appSrv_ResourceGroup"], - "resource_group", - "appSrv_ResourceGroup should be read from environment variable" - ); - assert.equal( - properties["appSrv_SlotName"], - "slot_name", - "appSrv_SlotName should be read from environment variable" - ); - }); - }); -}); diff --git a/test/unitTests/metrics/metricHandler.tests.ts b/test/unitTests/metrics/metricHandler.tests.ts index b6da6b7e..7f229f88 100644 --- a/test/unitTests/metrics/metricHandler.tests.ts +++ b/test/unitTests/metrics/metricHandler.tests.ts @@ -27,7 +27,7 @@ describe("Library/MetricHandler", () => { it("performance enablement during start", () => { _config.enableAutoCollectPerformance = true; handler = new MetricHandler(_config); - exportStub = sinon.stub(handler["_perfCounterMetricsHandler"]["_azureExporter"], "export").callsFake( + exportStub = sinon.stub(handler["_perfCounterMetricsHandler"]["_azureMonitorExporter"], "export").callsFake( (spans: any, resultCallback: any) => new Promise((resolve, reject) => { resultCallback({ @@ -42,7 +42,7 @@ describe("Library/MetricHandler", () => { it("preAggregated metrics enablement during start", () => { _config.enableAutoCollectStandardMetrics = true; handler = new MetricHandler(_config); - exportStub = sinon.stub(handler["_standardMetricsHandler"]["_azureExporter"], "export").callsFake( + exportStub = sinon.stub(handler["_standardMetricsHandler"]["_azureMonitorExporter"], "export").callsFake( (spans: any, resultCallback: any) => new Promise((resolve, reject) => { resultCallback({ @@ -53,20 +53,5 @@ describe("Library/MetricHandler", () => { ); assert.ok(handler["_standardMetricsHandler"], "preAggregated metrics not loaded"); }); - - it("heartbeat metrics enablement during start", () => { - _config.enableAutoCollectHeartbeat = true; - handler = new MetricHandler(_config); - exportStub = sinon.stub(handler["_heartbeatHandler"]["_azureExporter"], "export").callsFake( - (spans: any, resultCallback: any) => - new Promise((resolve, reject) => { - resultCallback({ - code: ExportResultCode.SUCCESS, - }); - resolve(); - }) - ); - assert.ok(handler["_heartbeatHandler"], "Heartbeat metrics not loaded"); - }); }); }); diff --git a/test/unitTests/metrics/performance.tests.ts b/test/unitTests/metrics/performance.tests.ts index 60b859ba..5b02282f 100644 --- a/test/unitTests/metrics/performance.tests.ts +++ b/test/unitTests/metrics/performance.tests.ts @@ -30,7 +30,7 @@ describe("PerformanceCounterMetricsHandler", () => { function createAutoCollect(customConfig?: ApplicationInsightsConfig) { autoCollect = new PerformanceCounterMetricsHandler(customConfig || config, { collectionInterval: 100 }); - exportStub = sinon.stub(autoCollect["_azureExporter"], "export").callsFake( + exportStub = sinon.stub(autoCollect["_azureMonitorExporter"], "export").callsFake( (spans: any, resultCallback: any) => new Promise((resolve, reject) => { resultCallback({ diff --git a/test/unitTests/metrics/standardMetrics.tests.ts b/test/unitTests/metrics/standardMetrics.tests.ts index 627ad241..36a1d0eb 100644 --- a/test/unitTests/metrics/standardMetrics.tests.ts +++ b/test/unitTests/metrics/standardMetrics.tests.ts @@ -11,13 +11,15 @@ import { ExportResultCode } from "@opentelemetry/core"; describe("#StandardMetricsHandler", () => { let exportStub: sinon.SinonStub; + let otlpExportStub: sinon.SinonStub; let autoCollect: StandardMetricsHandler; before(() => { const config = new ApplicationInsightsConfig(); config.azureMonitorExporterConfig.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; + config.otlpMetricExporterConfig.enabled = true; autoCollect = new StandardMetricsHandler(config, { collectionInterval: 100 }); - exportStub = sinon.stub(autoCollect["_azureExporter"], "export").callsFake( + exportStub = sinon.stub(autoCollect["_azureMonitorExporter"], "export").callsFake( (spans: any, resultCallback: any) => new Promise((resolve, reject) => { resultCallback({ @@ -26,14 +28,25 @@ describe("#StandardMetricsHandler", () => { resolve(); }) ); + otlpExportStub = sinon.stub(autoCollect["_otlpExporter"], "export").callsFake( + (spans: any, resultCallback: any) => + new Promise((resolve, reject) => { + resultCallback({ + code: ExportResultCode.SUCCESS, + }); + resolve(null); + }) + ); }); afterEach(() => { exportStub.resetHistory(); + otlpExportStub.resetHistory(); }); after(() => { exportStub.restore(); + otlpExportStub.restore(); autoCollect.shutdown(); }); @@ -106,6 +119,7 @@ describe("#StandardMetricsHandler", () => { } await new Promise((resolve) => setTimeout(resolve, 120)); + assert.ok(exportStub.called); const resourceMetrics = exportStub.args[0][0]; const scopeMetrics = resourceMetrics.scopeMetrics; @@ -224,11 +238,17 @@ describe("#StandardMetricsHandler", () => { metrics[3].dataPoints[1].attributes["cloudRoleName"], "testcloudRoleName2" ); + + // OTLP export + assert.ok(otlpExportStub.called); + assert.strictEqual(otlpExportStub.args[0][0].scopeMetrics.length, 1, "scopeMetrics count"); + assert.strictEqual(otlpExportStub.args[0][0].scopeMetrics[0].metrics.length, 4, "metrics count"); }); it("should not collect when disabled", async () => { autoCollect.shutdown(); await new Promise((resolve) => setTimeout(resolve, 120)); assert.ok(exportStub.notCalled); + assert.ok(otlpExportStub.notCalled); }); }); diff --git a/test/unitTests/shared/config.json b/test/unitTests/shared/config.json index 078e5866..7e94d6cb 100644 --- a/test/unitTests/shared/config.json +++ b/test/unitTests/shared/config.json @@ -1,12 +1,27 @@ { - "connectionString": "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/", + "azureMonitorExporterConfig": { + "connectionString": "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/", + "disableOfflineStorage": true, + "storageDirectory": "testPath" + }, + "otlpTraceExporterConfig": { + "enabled": true, + "baseConfig": { + "keepAlive": false, + "url": "someurlfortraces" + } + }, + "otlpMetricExporterConfig": { + "enabled": true, + "baseConfig": { + "keepAlive": true, + "url": "someurlformetrics" + } + }, "samplingRatio": 0.3, "enableAutoCollectExceptions": false, "enableAutoCollectPerformance": false, "enableAutoCollectStandardMetrics": false, - "enableAutoCollectHeartbeat": false, - "disableOfflineStorage": true, - "storageDirectory": "testPath", "extendedMetrics": { "heap": true, "gc": true, diff --git a/test/unitTests/shared/config.tests.ts b/test/unitTests/shared/config.tests.ts index 40b49c9d..d8fd4163 100644 --- a/test/unitTests/shared/config.tests.ts +++ b/test/unitTests/shared/config.tests.ts @@ -51,7 +51,7 @@ describe("Library/Config", () => { process.env = env; const config = new ApplicationInsightsConfig(); config.connectionString = "InstrumentationKey=cs.code"; - assert.deepEqual(config.connectionString, "InstrumentationKey=cs.code"); + assert.deepEqual(config.azureMonitorExporterConfig.connectionString, "InstrumentationKey=cs.code"); }); it("connection string set via environment variable", () => { @@ -88,6 +88,15 @@ describe("Library/Config", () => { assert.equal(config.samplingRatio, 0.3, "Wrong samplingRatio"); assert.equal(config.azureMonitorExporterConfig.disableOfflineStorage, true, "Wrong disableOfflineStorage"); assert.equal(config.azureMonitorExporterConfig.storageDirectory, "testPath", "Wrong storageDirectory"); + assert.equal(config.otlpTraceExporterConfig.enabled, true, "Wrong otlpTraceExporterConfig enabled"); + assert.equal(config.otlpTraceExporterConfig.baseConfig.keepAlive, false, "Wrong otlpTraceExporterConfig keepAlive"); + assert.equal(config.otlpTraceExporterConfig.baseConfig.url, "someurlfortraces", "Wrong otlpTraceExporterConfig url"); + + assert.equal(config.otlpMetricExporterConfig.enabled, true, "Wrong otlpMetricExporterConfig enabled"); + assert.equal(config.otlpMetricExporterConfig.baseConfig.keepAlive, true, "Wrong otlpMetricExporterConfig keepAlive"); + assert.equal(config.otlpMetricExporterConfig.baseConfig.url, "someurlformetrics", "Wrong otlpMetricExporterConfig url"); + + assert.equal( config.enableAutoCollectExceptions, false, @@ -103,11 +112,6 @@ describe("Library/Config", () => { false, "Wrong enableAutoCollectStandardMetrics" ); - assert.equal( - config.enableAutoCollectHeartbeat, - false, - "Wrong enableAutoCollectHeartbeat" - ); assert.equal(config.extendedMetrics.loop, true, "Wrong loop"); assert.equal(config.extendedMetrics.gc, true, "Wrong gc"); assert.equal(config.extendedMetrics.heap, true, "Wrong heap"); @@ -140,11 +144,6 @@ describe("Library/Config", () => { true, "Wrong enableAutoCollectStandardMetrics" ); - assert.equal( - config.enableAutoCollectHeartbeat, - true, - "Wrong enableAutoCollectHeartbeat" - ); assert.equal(config.extendedMetrics.loop, false, "Wrong loop"); assert.equal(config.extendedMetrics.gc, false, "Wrong gc"); assert.equal(config.extendedMetrics.heap, false, "Wrong heap"); @@ -155,11 +154,15 @@ describe("Library/Config", () => { assert.equal(config.instrumentations.redis.enabled, false, "Wrong redis"); assert.equal(config.instrumentations.redis4.enabled, false, "Wrong redis4"); assert.equal( - config.disableOfflineStorage, + config.azureMonitorExporterConfig.disableOfflineStorage, undefined, "Wrong disableOfflineStorage" ); assert.equal(config.azureMonitorExporterConfig.storageDirectory, undefined, "Wrong storageDirectory"); + assert.equal(config.otlpMetricExporterConfig.enabled, undefined, "Wrong otlpMetricExporterConfig.enabled"); + assert.equal(config.otlpMetricExporterConfig.baseConfig, undefined, "Wrong otlpMetricExporterConfig.baseConfig"); + assert.equal(config.otlpTraceExporterConfig.enabled, undefined, "Wrong otlpTraceExporterConfig.enabled"); + assert.equal(config.otlpTraceExporterConfig.baseConfig, undefined, "Wrong otlpTraceExporterConfig.baseConfig"); assert.equal(config.logInstrumentations.console.enabled, false, "Wrong console"); assert.equal(config.logInstrumentations.bunyan.enabled, false, "Wrong bunyan"); assert.equal(config.logInstrumentations.winston.enabled, false, "Wrong winston"); diff --git a/test/unitTests/shared/jsonConfig.tests.ts b/test/unitTests/shared/jsonConfig.tests.ts index 834b4dcc..718b4450 100644 --- a/test/unitTests/shared/jsonConfig.tests.ts +++ b/test/unitTests/shared/jsonConfig.tests.ts @@ -43,7 +43,7 @@ describe("Json Config", () => { process.env = env; const config = JsonConfig.getInstance(); assert.equal( - config.connectionString, + config.azureMonitorExporterConfig.connectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/" ); }); @@ -55,7 +55,7 @@ describe("Json Config", () => { process.env = env; const config = JsonConfig.getInstance(); assert.equal( - config.connectionString, + config.azureMonitorExporterConfig.connectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/" ); }); @@ -72,9 +72,17 @@ describe("Json Config", () => { process.env = env; const config = JsonConfig.getInstance(); assert.equal( - config.connectionString, + config.azureMonitorExporterConfig.connectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/" ); + assert.equal(config.azureMonitorExporterConfig.disableOfflineStorage, true); + assert.equal(config.azureMonitorExporterConfig.storageDirectory, "testPath"); + assert.equal(config.otlpTraceExporterConfig.enabled, true); + assert.equal(config.otlpTraceExporterConfig.baseConfig.keepAlive, false); + assert.equal(config.otlpTraceExporterConfig.baseConfig.url, "someurlfortraces"); + assert.equal(config.otlpMetricExporterConfig.enabled, true); + assert.equal(config.otlpMetricExporterConfig.baseConfig.keepAlive, true); + assert.equal(config.otlpMetricExporterConfig.baseConfig.url, "someurlformetrics"); assert.equal(config.samplingRatio, 0.3, "Wrong samplingRatio"); assert.equal( config.enableAutoCollectExceptions, @@ -91,11 +99,6 @@ describe("Json Config", () => { false, "Wrong enableAutoCollectStandardMetrics" ); - assert.equal( - config.enableAutoCollectHeartbeat, - false, - "Wrong enableAutoCollectHeartbeat" - ); assert.equal(config.extendedMetrics.loop, true, "Wrong loop"); assert.equal(config.extendedMetrics.gc, true, "Wrong gc"); assert.equal(config.extendedMetrics.heap, true, "Wrong heap"); @@ -121,7 +124,7 @@ describe("Json Config", () => { process.env = env; const config = JsonConfig.getInstance(); assert.equal( - config.connectionString, + config.azureMonitorExporterConfig.connectionString, "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/" ); }); diff --git a/test/unitTests/shim/telemetryClient.tests.ts b/test/unitTests/shim/telemetryClient.tests.ts index c64a597b..7d2f16e1 100644 --- a/test/unitTests/shim/telemetryClient.tests.ts +++ b/test/unitTests/shim/telemetryClient.tests.ts @@ -33,7 +33,7 @@ describe("shim/TelemetryClient", () => { "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333" ); const stub = sinon - .stub(client.client.getTraceHandler()["_exporter"], "export") + .stub(client.client.getTraceHandler()["_azureMonitorExporter"], "export") .callsFake( (spans: any, resultCallback: any) => new Promise((resolve, reject) => { @@ -78,7 +78,7 @@ describe("shim/TelemetryClient", () => { "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333" ); const stub = sinon - .stub(client.client.getTraceHandler()["_exporter"], "export") + .stub(client.client.getTraceHandler()["_azureMonitorExporter"], "export") .callsFake( (spans: any, resultCallback: any) => new Promise((resolve, reject) => { @@ -120,7 +120,7 @@ describe("shim/TelemetryClient", () => { "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333" ); const stub = sinon - .stub(client.client.getTraceHandler()["_exporter"], "export") + .stub(client.client.getTraceHandler()["_azureMonitorExporter"], "export") .callsFake( (spans: any, resultCallback: any) => new Promise((resolve, reject) => { diff --git a/test/unitTests/traces/traceHandler.tests.ts b/test/unitTests/traces/traceHandler.tests.ts index ea67f263..01b33f50 100644 --- a/test/unitTests/traces/traceHandler.tests.ts +++ b/test/unitTests/traces/traceHandler.tests.ts @@ -20,6 +20,7 @@ describe("Library/TraceHandler", () => { before(() => { _config = new ApplicationInsightsConfig(); _config.azureMonitorExporterConfig.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; + _config.otlpTraceExporterConfig.enabled = true; sandbox = sinon.createSandbox(); }); @@ -29,6 +30,7 @@ describe("Library/TraceHandler", () => { describe("#autoCollection of HTTP/HTTPS requests", () => { let exportStub: sinon.SinonStub; + let otlpExportStub: sinon.SinonStub; let handler: TraceHandler = null; let metricHandler: MetricHandler = null; let mockHttpServer: any; @@ -44,7 +46,7 @@ describe("Library/TraceHandler", () => { _config.instrumentations.http = httpConfig; metricHandler = new MetricHandler(_config); handler = new TraceHandler(_config, metricHandler); - exportStub = sinon.stub(handler["_exporter"], "export").callsFake( + exportStub = sinon.stub(handler["_azureMonitorExporter"], "export").callsFake( (spans: any, resultCallback: any) => new Promise((resolve, reject) => { resultCallback({ @@ -53,6 +55,15 @@ describe("Library/TraceHandler", () => { resolve(); }) ); + otlpExportStub = sinon.stub(handler["_otlpExporter"], "export").callsFake( + (spans: any, resultCallback: any) => + new Promise((resolve, reject) => { + resultCallback({ + code: ExportResultCode.SUCCESS, + }); + resolve(null); + }) + ); // Load Http modules, HTTP instrumentation hook will be created in OpenTelemetry http = require("http") as any; https = require("https") as any; @@ -65,10 +76,12 @@ describe("Library/TraceHandler", () => { afterEach(() => { exportStub.resetHistory(); + otlpExportStub.resetHistory(); }); after(() => { exportStub.restore(); + otlpExportStub.restore(); mockHttpServer.close(); mockHttpsServer.close(); metricHandler.shutdown(); @@ -331,6 +344,84 @@ describe("Library/TraceHandler", () => { }); }); + it("OTLP Export", (done) => { + handler["_initialize"](); + makeHttpRequest(false) + .then(() => { + handler + .flush() + .then(() => { + assert.ok(otlpExportStub.calledOnce, "Export called"); + const spans = otlpExportStub.args[0][0]; + assert.equal(spans.length, 2); + // Incoming request + assert.equal(spans[0].name, "HTTP GET"); + assert.equal( + spans[0].instrumentationLibrary.name, + "@opentelemetry/instrumentation-http" + ); + assert.equal(spans[0].kind, 1, "Span Kind"); + assert.equal(spans[0].status.code, 0, "Span Success"); // Success + assert.ok(spans[0].startTime); + assert.ok(spans[0].endTime); + assert.equal( + spans[0].attributes["http.host"], + `localhost:${mockHttpServerPort}` + ); + assert.equal(spans[0].attributes["http.method"], "GET"); + assert.equal(spans[0].attributes["http.status_code"], "200"); + assert.equal(spans[0].attributes["http.status_text"], "OK"); + assert.equal(spans[0].attributes["http.target"], "/test"); + assert.equal( + spans[0].attributes["http.url"], + `http://localhost:${mockHttpServerPort}/test` + ); + assert.equal(spans[0].attributes["net.host.name"], "localhost"); + assert.equal(spans[0].attributes["net.host.port"], mockHttpServerPort); + // Outgoing request + assert.equal(spans[1].name, "HTTP GET"); + assert.equal( + spans[1].instrumentationLibrary.name, + "@opentelemetry/instrumentation-http" + ); + assert.equal(spans[1].kind, 2, "Span Kind"); + assert.equal(spans[1].status.code, 0, "Span Success"); // Success + assert.ok(spans[1].startTime); + assert.ok(spans[1].endTime); + assert.equal( + spans[1].attributes["http.host"], + `localhost:${mockHttpServerPort}` + ); + assert.equal(spans[1].attributes["http.method"], "GET"); + assert.equal(spans[1].attributes["http.status_code"], "200"); + assert.equal(spans[1].attributes["http.status_text"], "OK"); + assert.equal(spans[1].attributes["http.target"], "/test"); + assert.equal( + spans[1].attributes["http.url"], + `http://localhost:${mockHttpServerPort}/test` + ); + assert.equal(spans[1].attributes["net.peer.name"], "localhost"); + assert.equal(spans[1].attributes["net.peer.port"], mockHttpServerPort); + + assert.equal( + spans[0]["_spanContext"]["traceId"], + spans[1]["_spanContext"]["traceId"] + ); + assert.notEqual( + spans[0]["_spanContext"]["spanId"], + spans[1]["_spanContext"]["spanId"] + ); + done(); + }) + .catch((error) => { + done(error); + }); + }) + .catch((error) => { + done(error); + }); + }); + it("Custom Span processors", (done) => { handler["_initialize"](); let customSpanProcessor: SpanProcessor = {