From ae42bd38bf4cc1bb5cf1aa693bf3c8ec4c090b1a Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 5 May 2023 17:15:18 -0700 Subject: [PATCH] [Beta] Align with SDK versions used by all telemetry (#1137) * Align with SDK versions used by all telemetry * Add test stubs * test * Update * Test * Update --- src/logs/logHandler.ts | 17 ++- .../collection/nativePerformanceMetrics.ts | 62 ++++---- src/metrics/handlers/heartBeatHandler.ts | 1 + .../performanceCounterMetricsHandler.ts | 2 +- .../handlers/standardMetricsHandler.ts | 1 - src/metrics/metricHandler.ts | 3 + test/unitTests/logs/logHandler.tests.ts | 133 +++++++----------- .../metrics/customMetricsHandler.tests.ts | 27 ++-- test/unitTests/metrics/heartbeat.tests.ts | 31 ++-- test/unitTests/metrics/metricHandler.tests.ts | 49 +++++-- test/unitTests/metrics/performance.tests.ts | 44 ++++-- .../metrics/standardMetrics.tests.ts | 27 ++-- test/unitTests/traces/traceHandler.tests.ts | 2 + 13 files changed, 230 insertions(+), 169 deletions(-) diff --git a/src/logs/logHandler.ts b/src/logs/logHandler.ts index f666df37..1dfdf16d 100644 --- a/src/logs/logHandler.ts +++ b/src/logs/logHandler.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import { ApplicationInsightsSampler } from "@azure/monitor-opentelemetry-exporter"; import { context, trace } from "@opentelemetry/api"; +import { SDK_INFO } from "@opentelemetry/core"; import { IdGenerator, RandomIdGenerator, SamplingDecision, SamplingResult } from "@opentelemetry/sdk-trace-base"; import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; @@ -36,7 +38,7 @@ import { import { Logger } from "../shared/logging"; import { IStandardMetricBaseDimensions, IMetricTraceDimensions } from "../metrics/types"; import { MetricHandler } from "../metrics/metricHandler"; -import { ApplicationInsightsSampler } from "@azure/monitor-opentelemetry-exporter"; +import { AZURE_MONITOR_DISTRO_VERSION } from "../declarations/constants"; export class LogHandler { // Statsbeat is instantiated here such that it can be accessed by the diagnostic-channel. @@ -50,6 +52,7 @@ export class LogHandler { private _metricHandler: MetricHandler; private _aiSampler: ApplicationInsightsSampler; private _instrumentationKey: string; + private _aiInternalSdkVersion: string; constructor(config: ApplicationInsightsConfig, metricHandler?: MetricHandler, statsbeat?: Statsbeat) { this._config = config; @@ -69,6 +72,14 @@ export class LogHandler { const parsedConnectionString = parser.parse(this._config.azureMonitorExporterConfig.connectionString); this._instrumentationKey = parsedConnectionString.instrumentationkey; this._console.enable(this._config.logInstrumentations); + + const { node } = process.versions; + let nodeVersion = node.split("."); + let opentelemetryVersion = SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_VERSION]; + let prefix = process.env["AZURE_MONITOR_AGENT_PREFIX"] + ? process.env["AZURE_MONITOR_AGENT_PREFIX"] + : ""; + this._aiInternalSdkVersion = `${prefix}node${nodeVersion}:otel${opentelemetryVersion}:dst${AZURE_MONITOR_DISTRO_VERSION}`; } /** @@ -349,9 +360,7 @@ export class LogHandler { } const serviceInstanceId = attributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID]; tags[KnownContextTagKeys.AiCloudRoleInstance] = String(serviceInstanceId); - tags[KnownContextTagKeys.AiInternalSdkVersion] = String( - attributes[SemanticResourceAttributes.TELEMETRY_SDK_VERSION] - ); + tags[KnownContextTagKeys.AiInternalSdkVersion] = this._aiInternalSdkVersion; // Add Correlation headers const spanContext = trace.getSpanContext(context.active()); diff --git a/src/metrics/collection/nativePerformanceMetrics.ts b/src/metrics/collection/nativePerformanceMetrics.ts index 681ae6e7..6ea84c63 100644 --- a/src/metrics/collection/nativePerformanceMetrics.ts +++ b/src/metrics/collection/nativePerformanceMetrics.ts @@ -37,38 +37,38 @@ export class NativePerformanceMetrics { NativeMetricsCounter.MEMORY_USAGE_NON_HEAP ); - // Try to require in the native-metrics library. If it's found initialize it, else do nothing and never try again. - try { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const NativeMetricsEmitter = require("applicationinsights-native-metrics"); - this._emitter = new NativeMetricsEmitter(); - Logger.getInstance().info("Native metrics module successfully loaded!"); - } catch (err) { - // Package not available. - return; - } - // Enable the emitter if we were able to construct one - if (this._emitter) { - try { - // enable self - this._emitter.enable(true, this._collectionInterval); - } catch (err) { - Logger.getInstance().error("Native metrics enable failed", err); - } + // // Try to require in the native-metrics library. If it's found initialize it, else do nothing and never try again. + // try { + // // eslint-disable-next-line @typescript-eslint/no-var-requires + // const NativeMetricsEmitter = require("applicationinsights-native-metrics"); + // this._emitter = new NativeMetricsEmitter(); + // Logger.getInstance().info("Native metrics module successfully loaded!"); + // } catch (err) { + // // Package not available. + // return; + // } + // // Enable the emitter if we were able to construct one + // if (this._emitter) { + // try { + // // enable self + // this._emitter.enable(true, this._collectionInterval); + // } catch (err) { + // Logger.getInstance().error("Native metrics enable failed", err); + // } - // Add histogram data collection - if (!this._handle) { - this._handle = setInterval( - () => this._collectHistogramData(), - this._collectionInterval - ); - this._handle.unref(); - } - // Add observable callbacks - this._heapMemoryTotalGauge.addCallback(this._getHeapTotal.bind(this)); - this._heapMemoryUsageGauge.addCallback(this._getHeapUsage.bind(this)); - this._memoryUsageNonHeapGauge.addCallback(this._getNonHeapUsage.bind(this)); - } + // // Add histogram data collection + // if (!this._handle) { + // this._handle = setInterval( + // () => this._collectHistogramData(), + // this._collectionInterval + // ); + // this._handle.unref(); + // } + // // Add observable callbacks + // this._heapMemoryTotalGauge.addCallback(this._getHeapTotal.bind(this)); + // this._heapMemoryUsageGauge.addCallback(this._getHeapUsage.bind(this)); + // this._memoryUsageNonHeapGauge.addCallback(this._getNonHeapUsage.bind(this)); + // } } /** diff --git a/src/metrics/handlers/heartBeatHandler.ts b/src/metrics/handlers/heartBeatHandler.ts index d8fe04a7..2a9a6114 100644 --- a/src/metrics/handlers/heartBeatHandler.ts +++ b/src/metrics/handlers/heartBeatHandler.ts @@ -60,6 +60,7 @@ export class HeartBeatHandler { } public async shutdown(): Promise { + this._metricGauge.removeCallback(this._metricGaugeCallback); await this._meterProvider.shutdown(); } diff --git a/src/metrics/handlers/performanceCounterMetricsHandler.ts b/src/metrics/handlers/performanceCounterMetricsHandler.ts index 83537d6f..f8bb3e8e 100644 --- a/src/metrics/handlers/performanceCounterMetricsHandler.ts +++ b/src/metrics/handlers/performanceCounterMetricsHandler.ts @@ -59,9 +59,9 @@ export class PerformanceCounterMetricsHandler { } public shutdown() { - this._meterProvider.shutdown(); this._processMetrics.shutdown(); this._requestMetrics.shutdown(); + this._meterProvider.shutdown(); } public recordSpan(span: ReadableSpan): void { diff --git a/src/metrics/handlers/standardMetricsHandler.ts b/src/metrics/handlers/standardMetricsHandler.ts index 6248eff6..57adb8dd 100644 --- a/src/metrics/handlers/standardMetricsHandler.ts +++ b/src/metrics/handlers/standardMetricsHandler.ts @@ -62,7 +62,6 @@ export class StandardMetricsHandler { } public shutdown() { - this._meterProvider.shutdown(); this._dependencyMetrics.shutdown(); this._exceptionMetrics.shutdown(); this._traceMetrics.shutdown(); diff --git a/src/metrics/metricHandler.ts b/src/metrics/metricHandler.ts index b1dcb0a4..9a9761fe 100644 --- a/src/metrics/metricHandler.ts +++ b/src/metrics/metricHandler.ts @@ -54,6 +54,9 @@ export class MetricHandler { await this._perfCounterMetricsHandler?.flush(); } + /** + * @deprecated This should not be used + */ public getConfig(): ApplicationInsightsConfig { return this._config; } diff --git a/test/unitTests/logs/logHandler.tests.ts b/test/unitTests/logs/logHandler.tests.ts index 223d05c0..7f8f77cd 100644 --- a/test/unitTests/logs/logHandler.tests.ts +++ b/test/unitTests/logs/logHandler.tests.ts @@ -21,6 +21,10 @@ import { ApplicationInsightsClient } from "../../../src"; describe("Library/LogHandler", () => { let sandbox: sinon.SinonSandbox; + let handler: LogHandler; + let traceHandler: TraceHandler; + let stub: sinon.SinonStub; + let metricHandler: MetricHandler; const _config = new ApplicationInsightsConfig(); _config.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://westus.in.applicationinsights.azure.com/;LiveEndpoint=https://west.live.monitor.azure.com/"; @@ -30,19 +34,39 @@ describe("Library/LogHandler", () => { afterEach(() => { sandbox.restore(); + handler.shutdown(); + if (traceHandler) { + traceHandler.shutdown(); + } + if (metricHandler) { + metricHandler.shutdown(); + } }); + function createLogHandler(config: ApplicationInsightsConfig, metricHandler?: MetricHandler) { + handler = new LogHandler(config, metricHandler); + stub = sinon.stub(handler["_exporter"], "export").callsFake( + (envelopes: any, resultCallback: any) => + new Promise((resolve, reject) => { + resultCallback({ + code: ExportResultCode.SUCCESS, + }); + resolve(); + }) + ); + } + describe("#autoCollect", () => { it("exception enablement during start", () => { _config.enableAutoCollectExceptions = true; - const handler = new LogHandler(_config); + createLogHandler(_config); assert.ok(handler["_exceptions"], "Exceptions not enabled"); }); }); describe("#manual track APIs", () => { it("_logToEnvelope", () => { - const handler = new LogHandler(_config); + createLogHandler(_config); const telemetry: Telemetry = {}; const data: MonitorDomain = {}; const envelope = handler["_logToEnvelope"]( @@ -64,16 +88,24 @@ describe("Library/LogHandler", () => { assert.equal(envelope.tags["ai.cloud.role"], "Web"); assert.equal(envelope.tags["ai.cloud.roleInstance"], os.hostname()); assert.ok( - envelope.tags["ai.internal.sdkVersion"].indexOf("node") == 0, + envelope.tags["ai.internal.sdkVersion"].indexOf("node") > 0, + "Incorrect SDK version" + ); + assert.ok( + envelope.tags["ai.internal.sdkVersion"].indexOf(":otel") > 0, + "Incorrect SDK version" + ); + assert.ok( + envelope.tags["ai.internal.sdkVersion"].indexOf(":dst") > 0, "Incorrect SDK version" ); }); it("tracing", () => { - const logHandler = new LogHandler(_config); - const traceHandler = new TraceHandler(_config); + createLogHandler(_config); + traceHandler = new TraceHandler(_config); traceHandler["_tracer"].startActiveSpan("test", () => { - const envelope = logHandler["_logToEnvelope"]({}, "", {}); + const envelope = handler["_logToEnvelope"]({}, "", {}); const spanContext = trace.getSpanContext(context.active()); assert.ok(isValidTraceId(envelope.tags["ai.operation.id"]), "Valid operation Id"); assert.ok( @@ -89,8 +121,8 @@ describe("Library/LogHandler", () => { let otherConfig = new ApplicationInsightsConfig(); otherConfig.connectionString = _config.connectionString; otherConfig.samplingRatio = 0; - const logHandler = new LogHandler(otherConfig); - const stub = sinon.stub(logHandler["_batchProcessor"], "send"); + createLogHandler(otherConfig); + const stub = sinon.stub(handler["_batchProcessor"], "send"); const telemetry: AvailabilityTelemetry = { name: "TestName", duration: 2000, //2 seconds @@ -99,22 +131,13 @@ describe("Library/LogHandler", () => { message: "testMessage", success: false, }; - logHandler.trackAvailability(telemetry); + handler.trackAvailability(telemetry); assert.ok(stub.notCalled); }); it("trackAvailability", (done) => { - const handler = new LogHandler(_config); - const stub = sinon.stub(handler["_exporter"], "export").callsFake( - (envelopes: any, resultCallback: any) => - new Promise((resolve, reject) => { - resultCallback({ - code: ExportResultCode.SUCCESS, - }); - resolve(); - }) - ); + createLogHandler(_config); const telemetry: AvailabilityTelemetry = { name: "TestName", duration: 2000, //2 seconds @@ -156,16 +179,7 @@ describe("Library/LogHandler", () => { }); it("trackPageView", (done) => { - const handler = new LogHandler(_config); - const stub = sinon.stub(handler["_exporter"], "export").callsFake( - (envelopes: any, resultCallback: any) => - new Promise((resolve, reject) => { - resultCallback({ - code: ExportResultCode.SUCCESS, - }); - resolve(); - }) - ); + createLogHandler(_config); const telemetry: PageViewTelemetry = { name: "TestName", duration: 2000, //2 seconds @@ -205,16 +219,7 @@ describe("Library/LogHandler", () => { }); it("trackTrace", (done) => { - const handler = new LogHandler(_config); - const stub = sinon.stub(handler["_exporter"], "export").callsFake( - (envelopes: any, resultCallback: any) => - new Promise((resolve, reject) => { - resultCallback({ - code: ExportResultCode.SUCCESS, - }); - resolve(); - }) - ); + createLogHandler(_config); const telemetry: TraceTelemetry = { message: "testMessage", severity: "Information", @@ -249,16 +254,7 @@ describe("Library/LogHandler", () => { }); it("trackException", (done) => { - const handler = new LogHandler(_config); - const stub = sinon.stub(handler["_exporter"], "export").callsFake( - (envelopes: any, resultCallback: any) => - new Promise((resolve, reject) => { - resultCallback({ - code: ExportResultCode.SUCCESS, - }); - resolve(); - }) - ); + createLogHandler(_config); const measurements: { [key: string]: number } = {}; measurements["test"] = 123; const telemetry: ExceptionTelemetry = { @@ -304,16 +300,7 @@ describe("Library/LogHandler", () => { }); it("trackEvent", (done) => { - const handler = new LogHandler(_config); - const stub = sinon.stub(handler["_exporter"], "export").callsFake( - (envelopes: any, resultCallback: any) => - new Promise((resolve, reject) => { - resultCallback({ - code: ExportResultCode.SUCCESS, - }); - resolve(); - }) - ); + createLogHandler(_config); const measurements: { [key: string]: number } = {}; measurements["test"] = 123; const telemetry: EventTelemetry = { @@ -351,17 +338,9 @@ describe("Library/LogHandler", () => { it("Exception standard metrics processed", (done) => { _config.enableAutoCollectStandardMetrics = true; - const metricHandler = new MetricHandler(_config); - const handler = new LogHandler(_config, metricHandler); - const stub = sinon.stub(handler["_exporter"], "export").callsFake( - (envelopes: any, resultCallback: any) => - new Promise((resolve, reject) => { - resultCallback({ - code: ExportResultCode.SUCCESS, - }); - resolve(); - }) - ); + metricHandler = new MetricHandler(_config); + createLogHandler(_config, metricHandler); + const telemetry: ExceptionTelemetry = { exception: new Error("TestError"), severity: "Critical", @@ -385,17 +364,8 @@ describe("Library/LogHandler", () => { it("Trace standard metrics processed", (done) => { _config.enableAutoCollectStandardMetrics = true; - const metricHandler = new MetricHandler(_config); - const handler = new LogHandler(_config, metricHandler); - const stub = sinon.stub(handler["_exporter"], "export").callsFake( - (envelopes: any, resultCallback: any) => - new Promise((resolve, reject) => { - resultCallback({ - code: ExportResultCode.SUCCESS, - }); - resolve(); - }) - ); + metricHandler = new MetricHandler(_config); + createLogHandler(_config, metricHandler); const telemetry: TraceTelemetry = { message: "testMessage", severity: "Information", @@ -424,6 +394,7 @@ describe("Library/LogHandler", () => { const logsStatsbeatCollection = appInsights["_logHandler"]["_exporter"]["_statsbeatMetrics"]["_networkStatsbeatCollection"]; assert.strictEqual(logsStatsbeatCollection[0].totalSuccesfulRequestCount, 1); assert.strictEqual(logsStatsbeatCollection[0].intervalRequestExecutionTime, 100); + appInsights.shutdown(); }); }); }); diff --git a/test/unitTests/metrics/customMetricsHandler.tests.ts b/test/unitTests/metrics/customMetricsHandler.tests.ts index 8eff37e4..1995d1cc 100644 --- a/test/unitTests/metrics/customMetricsHandler.tests.ts +++ b/test/unitTests/metrics/customMetricsHandler.tests.ts @@ -3,25 +3,34 @@ import * as sinon from "sinon"; import { CustomMetricsHandler } from "../../../src/metrics/handlers/customMetricsHandler"; import { ApplicationInsightsConfig } from "../../../src/shared"; +import { ExportResultCode } from "@opentelemetry/core"; describe("#CustomMetricsHandler", () => { - let sandbox: sinon.SinonSandbox; let autoCollect: CustomMetricsHandler; + let exportStub: sinon.SinonStub; before(() => { - sandbox = sinon.createSandbox(); const config = new ApplicationInsightsConfig(); - config.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; + config.azureMonitorExporterConfig.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; autoCollect = new CustomMetricsHandler(config, { collectionInterval: 100 }); - sandbox.stub(autoCollect["_metricReader"]["_exporter"], "export"); + exportStub = sinon.stub(autoCollect["_azureExporter"], "export").callsFake( + (spans: any, resultCallback: any) => + new Promise((resolve, reject) => { + resultCallback({ + code: ExportResultCode.SUCCESS, + }); + resolve(); + }) + ); }); afterEach(() => { - sandbox.restore(); + exportStub.resetHistory(); }); after(() => { autoCollect.shutdown(); + exportStub.restore(); }); it("should create a meter", () => { @@ -29,11 +38,10 @@ describe("#CustomMetricsHandler", () => { }); it("should observe instruments during collection", async () => { - const mockExport = sandbox.stub(autoCollect["_azureExporter"], "export"); autoCollect.getMeter().createCounter("testCounter", { description: "testDescription" }); await new Promise((resolve) => setTimeout(resolve, 120)); - assert.ok(mockExport.called); - const resourceMetrics = mockExport.args[0][0]; + 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; @@ -43,10 +51,9 @@ describe("#CustomMetricsHandler", () => { }); it("should not collect when disabled", async () => { - const mockExport = sandbox.stub(autoCollect["_azureExporter"], "export"); autoCollect.getMeter().createCounter("testCounter", { description: "testDescription" }); autoCollect.shutdown(); await new Promise((resolve) => setTimeout(resolve, 120)); - assert.ok(mockExport.notCalled); + assert.ok(exportStub.notCalled); }); }); diff --git a/test/unitTests/metrics/heartbeat.tests.ts b/test/unitTests/metrics/heartbeat.tests.ts index 5d3f8d78..24d9b3e1 100644 --- a/test/unitTests/metrics/heartbeat.tests.ts +++ b/test/unitTests/metrics/heartbeat.tests.ts @@ -5,19 +5,27 @@ 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 sandbox: sinon.SinonSandbox; let originalEnv: NodeJS.ProcessEnv; let config: ApplicationInsightsConfig; let heartbeat: HeartBeatHandler; + let exportStub: sinon.SinonStub; before(() => { - sandbox = sinon.createSandbox(); config = new ApplicationInsightsConfig(); - config.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; + config.azureMonitorExporterConfig.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; heartbeat = new HeartBeatHandler(config, { collectionInterval: 100 }); - sandbox.stub(heartbeat["_metricReader"]["_exporter"], "export"); + exportStub = sinon.stub(heartbeat["_azureExporter"], "export").callsFake( + (spans: any, resultCallback: any) => + new Promise((resolve, reject) => { + resultCallback({ + code: ExportResultCode.SUCCESS, + }); + resolve(); + }) + ); }); beforeEach(() => { @@ -26,7 +34,12 @@ describe("AutoCollection/HeartBeat", () => { afterEach(() => { process.env = originalEnv; - sandbox.restore(); + exportStub.resetHistory(); + }); + + after(() => { + exportStub.restore(); + heartbeat.shutdown(); }); describe("#Metrics", () => { @@ -35,10 +48,9 @@ describe("AutoCollection/HeartBeat", () => { }); it("should observe instruments during collection", async () => { - const mockExport = sandbox.stub(heartbeat["_azureExporter"], "export"); await new Promise((resolve) => setTimeout(resolve, 120)); - assert.ok(mockExport.called); - const resourceMetrics = mockExport.args[0][0]; + 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; @@ -47,10 +59,9 @@ describe("AutoCollection/HeartBeat", () => { }); it("should not collect when shutdown", async () => { - const mockExport = sandbox.stub(heartbeat["_azureExporter"], "export"); heartbeat.shutdown(); await new Promise((resolve) => setTimeout(resolve, 120)); - assert.ok(mockExport.notCalled); + assert.ok(exportStub.notCalled); }); }); diff --git a/test/unitTests/metrics/metricHandler.tests.ts b/test/unitTests/metrics/metricHandler.tests.ts index a25fe484..b6da6b7e 100644 --- a/test/unitTests/metrics/metricHandler.tests.ts +++ b/test/unitTests/metrics/metricHandler.tests.ts @@ -2,38 +2,71 @@ import * as assert from "assert"; import * as sinon from "sinon"; import { MetricHandler } from "../../../src/metrics"; import { ApplicationInsightsConfig } from "../../../src/shared"; +import { ExportResultCode } from "@opentelemetry/core"; describe("Library/MetricHandler", () => { - let sandbox: sinon.SinonSandbox; + let exportStub: sinon.SinonStub; let _config: ApplicationInsightsConfig; + let handler: MetricHandler; before(() => { _config = new ApplicationInsightsConfig(); - _config.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; - sandbox = sinon.createSandbox(); + _config.azureMonitorExporterConfig.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; }); afterEach(() => { - sandbox.restore(); + exportStub.resetHistory(); + handler.shutdown(); + }); + + after(() => { + exportStub.restore(); }); describe("#autoCollect", () => { it("performance enablement during start", () => { _config.enableAutoCollectPerformance = true; - const handler = new MetricHandler(_config); + handler = new MetricHandler(_config); + exportStub = sinon.stub(handler["_perfCounterMetricsHandler"]["_azureExporter"], "export").callsFake( + (spans: any, resultCallback: any) => + new Promise((resolve, reject) => { + resultCallback({ + code: ExportResultCode.SUCCESS, + }); + resolve(); + }) + ); assert.ok(handler["_perfCounterMetricsHandler"], "Performance counters not loaded"); }); it("preAggregated metrics enablement during start", () => { _config.enableAutoCollectStandardMetrics = true; - const handler = new MetricHandler(_config); + handler = new MetricHandler(_config); + exportStub = sinon.stub(handler["_standardMetricsHandler"]["_azureExporter"], "export").callsFake( + (spans: any, resultCallback: any) => + new Promise((resolve, reject) => { + resultCallback({ + code: ExportResultCode.SUCCESS, + }); + resolve(); + }) + ); assert.ok(handler["_standardMetricsHandler"], "preAggregated metrics not loaded"); }); it("heartbeat metrics enablement during start", () => { _config.enableAutoCollectHeartbeat = true; - const handler = new MetricHandler(_config); - assert.ok(handler["_standardMetricsHandler"], "Heartbeat metrics not loaded"); + 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 7bf24edc..60b859ba 100644 --- a/test/unitTests/metrics/performance.tests.ts +++ b/test/unitTests/metrics/performance.tests.ts @@ -4,28 +4,46 @@ import * as sinon from "sinon"; import { PerformanceCounterMetricsHandler } from "../../../src/metrics/handlers"; import { NativeMetricsCounter, PerformanceCounter } from "../../../src/metrics/types"; import { ApplicationInsightsConfig } from "../../../src/shared"; +import { ExportResultCode } from "@opentelemetry/core"; describe("PerformanceCounterMetricsHandler", () => { - let sandbox: sinon.SinonSandbox; let autoCollect: PerformanceCounterMetricsHandler; + let config: ApplicationInsightsConfig; + let exportStub: sinon.SinonStub; before(() => { - sandbox = sinon.createSandbox(); - const config = new ApplicationInsightsConfig(); - config.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; + config = new ApplicationInsightsConfig(); + config.azureMonitorExporterConfig.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; config.extendedMetrics.heap = true; config.extendedMetrics.loop = true; config.extendedMetrics.gc = true; - autoCollect = new PerformanceCounterMetricsHandler(config, { collectionInterval: 100 }); - sandbox.stub(autoCollect["_metricReader"]["_exporter"], "export"); }); afterEach(() => { - sandbox.restore(); + exportStub.resetHistory(); + autoCollect.shutdown(); }); + after(() => { + exportStub.restore(); + }); + + function createAutoCollect(customConfig?: ApplicationInsightsConfig) { + autoCollect = new PerformanceCounterMetricsHandler(customConfig || config, { collectionInterval: 100 }); + exportStub = sinon.stub(autoCollect["_azureExporter"], "export").callsFake( + (spans: any, resultCallback: any) => + new Promise((resolve, reject) => { + resultCallback({ + code: ExportResultCode.SUCCESS, + }); + resolve(); + }) + ); + } + describe("#Metrics", () => { it("should create instruments", () => { + createAutoCollect(); assert.ok( autoCollect["_processMetrics"]["_memoryPrivateBytesGauge"], "_memoryPrivateBytesGauge not available" @@ -49,10 +67,10 @@ describe("PerformanceCounterMetricsHandler", () => { }); it("should observe instruments during collection", async () => { - const mockExport = sandbox.stub(autoCollect["_azureExporter"], "export"); + createAutoCollect(); await new Promise((resolve) => setTimeout(resolve, 120)); - assert.ok(mockExport.called); - const resourceMetrics = mockExport.args[0][0]; + assert.ok(exportStub.called); + const resourceMetrics = exportStub.args[0][0]; const scopeMetrics = resourceMetrics.scopeMetrics; assert.strictEqual(scopeMetrics.length, 1, "scopeMetrics count"); let metrics = scopeMetrics[0].metrics; @@ -66,10 +84,10 @@ describe("PerformanceCounterMetricsHandler", () => { }); it("should not collect when disabled", async () => { - const mockExport = sandbox.stub(autoCollect["_azureExporter"], "export"); + createAutoCollect(); autoCollect.shutdown(); await new Promise((resolve) => setTimeout(resolve, 120)); - assert.ok(mockExport.notCalled); + assert.ok(exportStub.notCalled); }); it("should add correct views", () => { @@ -78,7 +96,7 @@ describe("PerformanceCounterMetricsHandler", () => { config.extendedMetrics.heap = false; config.extendedMetrics.loop = false; config.extendedMetrics.gc = false; - const autoCollect = new PerformanceCounterMetricsHandler(config); + createAutoCollect(config); let views = autoCollect["_getViews"](); assert.equal(views.length, 11); }); diff --git a/test/unitTests/metrics/standardMetrics.tests.ts b/test/unitTests/metrics/standardMetrics.tests.ts index f69228b7..627ad241 100644 --- a/test/unitTests/metrics/standardMetrics.tests.ts +++ b/test/unitTests/metrics/standardMetrics.tests.ts @@ -7,24 +7,33 @@ import * as sinon from "sinon"; import { StandardMetricsHandler } from "../../../src/metrics/handlers/standardMetricsHandler"; import { IStandardMetricBaseDimensions, StandardMetric } from "../../../src/metrics/types"; import { ApplicationInsightsConfig } from "../../../src/shared"; +import { ExportResultCode } from "@opentelemetry/core"; describe("#StandardMetricsHandler", () => { - let sandbox: sinon.SinonSandbox; + let exportStub: sinon.SinonStub; let autoCollect: StandardMetricsHandler; before(() => { - sandbox = sinon.createSandbox(); const config = new ApplicationInsightsConfig(); config.azureMonitorExporterConfig.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;"; autoCollect = new StandardMetricsHandler(config, { collectionInterval: 100 }); - sandbox.stub(autoCollect["_metricReader"]["_exporter"], "export"); + exportStub = sinon.stub(autoCollect["_azureExporter"], "export").callsFake( + (spans: any, resultCallback: any) => + new Promise((resolve, reject) => { + resultCallback({ + code: ExportResultCode.SUCCESS, + }); + resolve(); + }) + ); }); afterEach(() => { - sandbox.restore(); + exportStub.resetHistory(); }); after(() => { + exportStub.restore(); autoCollect.shutdown(); }); @@ -40,7 +49,6 @@ describe("#StandardMetricsHandler", () => { }); it("should observe instruments during collection", async () => { - const mockExport = sandbox.stub(autoCollect["_azureExporter"], "export"); let resource = { attributes: {} as any }; @@ -98,8 +106,8 @@ describe("#StandardMetricsHandler", () => { } await new Promise((resolve) => setTimeout(resolve, 120)); - assert.ok(mockExport.called); - const resourceMetrics = mockExport.args[0][0]; + 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; @@ -110,7 +118,7 @@ describe("#StandardMetricsHandler", () => { assert.equal(metrics[3].descriptor.name, StandardMetric.TRACE_COUNT); // Requests - + assert.strictEqual(metrics[0].dataPoints.length, 3, "dataPoints count"); assert.strictEqual((metrics[0].dataPoints[0].value as Histogram).count, 1, "dataPoint count"); assert.strictEqual((metrics[0].dataPoints[0].value as Histogram).min, 654321, "dataPoint min"); @@ -219,9 +227,8 @@ describe("#StandardMetricsHandler", () => { }); it("should not collect when disabled", async () => { - const mockExport = sandbox.stub(autoCollect["_azureExporter"], "export"); autoCollect.shutdown(); await new Promise((resolve) => setTimeout(resolve, 120)); - assert.ok(mockExport.notCalled); + assert.ok(exportStub.notCalled); }); }); diff --git a/test/unitTests/traces/traceHandler.tests.ts b/test/unitTests/traces/traceHandler.tests.ts index 143225b2..ea67f263 100644 --- a/test/unitTests/traces/traceHandler.tests.ts +++ b/test/unitTests/traces/traceHandler.tests.ts @@ -71,6 +71,8 @@ describe("Library/TraceHandler", () => { exportStub.restore(); mockHttpServer.close(); mockHttpsServer.close(); + metricHandler.shutdown(); + handler.shutdown(); }); function createMockServers() {