From f4e1bc485ae32d94fccb7de73e3e2daf22621773 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Mon, 8 Nov 2021 08:56:54 -0800 Subject: [PATCH] Handle JSON stringify errors (#867) * Handle JSON stringify errors * Statsbeat test reliability * Update * Add error handling to get more accurate error in tests * Update * Using promises instead of callbacks Remove interval check as it makes test unreliable --- AutoCollection/Statsbeat.ts | 152 +++++----- .../diagnostic-channel/SpanParser.ts | 3 +- Library/QuickPulseSender.ts | 2 +- Library/Util.ts | 8 + Tests/AutoCollection/Statsbeat.tests.ts | 278 ++++++++---------- Tests/AutoCollection/bunyan.tests.ts | 5 +- 6 files changed, 219 insertions(+), 229 deletions(-) diff --git a/AutoCollection/Statsbeat.ts b/AutoCollection/Statsbeat.ts index b4caf7dd2..35f25d3e4 100644 --- a/AutoCollection/Statsbeat.ts +++ b/AutoCollection/Statsbeat.ts @@ -164,47 +164,45 @@ class Statsbeat { } public async trackShortIntervalStatsbeats() { - this._getResourceProvider(async () => { - let networkProperties = { - "os": this._os, - "rp": this._resourceProvider, - "cikey": this._cikey, - "runtimeVersion": this._runtimeVersion, - "language": this._language, - "version": this._sdkVersion, - "attach": this._attach, - } - this._trackRequestDuration(networkProperties); - this._trackRequestsCount(networkProperties); - await this._sendStatsbeats(); - }); + await this._getResourceProvider(); + let networkProperties = { + "os": this._os, + "rp": this._resourceProvider, + "cikey": this._cikey, + "runtimeVersion": this._runtimeVersion, + "language": this._language, + "version": this._sdkVersion, + "attach": this._attach, + } + this._trackRequestDuration(networkProperties); + this._trackRequestsCount(networkProperties); + await this._sendStatsbeats(); } public async trackLongIntervalStatsbeats() { - this._getResourceProvider(async () => { - let commonProperties = { - "os": this._os, - "rp": this._resourceProvider, - "cikey": this._cikey, - "runtimeVersion": this._runtimeVersion, - "language": this._language, - "version": this._sdkVersion, - "attach": this._attach, - }; - let attachProperties = Object.assign({ - "rpId": this._resourceIdentifier, - }, commonProperties); - this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.ATTACH, value: 1, properties: attachProperties }); - if (this._instrumentation != Constants.StatsbeatInstrumentation.NONE) {// Only send if there are some instrumentations enabled - let instrumentationProperties = Object.assign({ "feature": this._instrumentation, "type": Constants.StatsbeatFeatureType.Instrumentation }, commonProperties); - this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.FEATURE, value: 1, properties: instrumentationProperties }); - } - if (this._feature != Constants.StatsbeatFeature.NONE) {// Only send if there are some features enabled - let featureProperties = Object.assign({ "feature": this._feature, "type": Constants.StatsbeatFeatureType.Feature }, commonProperties); - this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.FEATURE, value: 1, properties: featureProperties }); - } - await this._sendStatsbeats(); - }); + await this._getResourceProvider(); + let commonProperties = { + "os": this._os, + "rp": this._resourceProvider, + "cikey": this._cikey, + "runtimeVersion": this._runtimeVersion, + "language": this._language, + "version": this._sdkVersion, + "attach": this._attach, + }; + let attachProperties = Object.assign({ + "rpId": this._resourceIdentifier, + }, commonProperties); + this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.ATTACH, value: 1, properties: attachProperties }); + if (this._instrumentation != Constants.StatsbeatInstrumentation.NONE) {// Only send if there are some instrumentations enabled + let instrumentationProperties = Object.assign({ "feature": this._instrumentation, "type": Constants.StatsbeatFeatureType.Instrumentation }, commonProperties); + this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.FEATURE, value: 1, properties: instrumentationProperties }); + } + if (this._feature != Constants.StatsbeatFeature.NONE) {// Only send if there are some features enabled + let featureProperties = Object.assign({ "feature": this._feature, "type": Constants.StatsbeatFeatureType.Feature }, commonProperties); + this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.FEATURE, value: 1, properties: featureProperties }); + } + await this._sendStatsbeats(); } private _getNetworkStatsbeatCounter(endpoint: number, host: string): Network.NetworkStatsbeat { @@ -227,10 +225,9 @@ class Statsbeat { var currentCounter = this._networkStatsbeatCollection[i]; currentCounter.time = +new Date; var intervalRequests = (currentCounter.totalRequestCount - currentCounter.lastRequestCount) || 0; - var elapsedMs = currentCounter.time - currentCounter.lastTime; var averageRequestExecutionTime = ((currentCounter.intervalRequestExecutionTime - currentCounter.lastIntervalRequestExecutionTime) / intervalRequests) || 0; currentCounter.lastIntervalRequestExecutionTime = currentCounter.intervalRequestExecutionTime; // reset - if (elapsedMs > 0 && intervalRequests > 0) { + if (intervalRequests > 0) { // Add extra properties let properties = Object.assign({ "endpoint": this._networkStatsbeatCollection[i].endpoint, "host": this._networkStatsbeatCollection[i].host }, commonProperties); this._statbeatMetrics.push({ name: Constants.StatsbeatCounter.REQUEST_DURATION, value: averageRequestExecutionTime, properties: properties }); @@ -292,45 +289,46 @@ class Statsbeat { this._runtimeVersion = process.version; } - private _getResourceProvider(callback: () => void) { - // Check resource provider - let waiting: boolean = false; - this._resourceProvider = Constants.StatsbeatResourceProvider.unknown; - this._resourceIdentifier = Constants.StatsbeatResourceProvider.unknown; - - if (process.env.WEBSITE_SITE_NAME) { // Web apps - this._resourceProvider = Constants.StatsbeatResourceProvider.appsvc; - this._resourceIdentifier = process.env.WEBSITE_SITE_NAME; - if (process.env.WEBSITE_HOME_STAMPNAME) { - this._resourceIdentifier += "/" + process.env.WEBSITE_HOME_STAMPNAME; - } - } else if (process.env.FUNCTIONS_WORKER_RUNTIME) { // Function apps - this._resourceProvider = Constants.StatsbeatResourceProvider.functions; - if (process.env.WEBSITE_HOSTNAME) { - this._resourceIdentifier = process.env.WEBSITE_HOSTNAME; - } - } else if (this._config) { - if (this._isVM === undefined || this._isVM == true) { - waiting = true; - Vm.AzureVirtualMachine.getAzureComputeMetadata(this._config, (vmInfo) => { - this._isVM = vmInfo.isVM; - if (this._isVM) { - this._resourceProvider = Constants.StatsbeatResourceProvider.vm; - this._resourceIdentifier = vmInfo.id + "/" + vmInfo.subscriptionId; - // Override OS as VM info have higher precedence - if (vmInfo.osType) { - this._os = vmInfo.osType; + private _getResourceProvider(): Promise { + return new Promise((resolve, reject) => { + // Check resource provider + let waiting: boolean = false; + this._resourceProvider = Constants.StatsbeatResourceProvider.unknown; + this._resourceIdentifier = Constants.StatsbeatResourceProvider.unknown; + if (process.env.WEBSITE_SITE_NAME) { // Web apps + this._resourceProvider = Constants.StatsbeatResourceProvider.appsvc; + this._resourceIdentifier = process.env.WEBSITE_SITE_NAME; + if (process.env.WEBSITE_HOME_STAMPNAME) { + this._resourceIdentifier += "/" + process.env.WEBSITE_HOME_STAMPNAME; + } + } else if (process.env.FUNCTIONS_WORKER_RUNTIME) { // Function apps + this._resourceProvider = Constants.StatsbeatResourceProvider.functions; + if (process.env.WEBSITE_HOSTNAME) { + this._resourceIdentifier = process.env.WEBSITE_HOSTNAME; + } + } else if (this._config) { + if (this._isVM === undefined || this._isVM == true) { + waiting = true; + Vm.AzureVirtualMachine.getAzureComputeMetadata(this._config, (vmInfo) => { + this._isVM = vmInfo.isVM; + if (this._isVM) { + this._resourceProvider = Constants.StatsbeatResourceProvider.vm; + this._resourceIdentifier = vmInfo.id + "/" + vmInfo.subscriptionId; + // Override OS as VM info have higher precedence + if (vmInfo.osType) { + this._os = vmInfo.osType; + } } - } - callback(); - }); - } else { - this._resourceProvider = Constants.StatsbeatResourceProvider.unknown; + resolve(); + }); + } else { + this._resourceProvider = Constants.StatsbeatResourceProvider.unknown; + } } - } - if (!waiting) { - callback(); - } + if (!waiting) { + resolve(); + } + }); } } diff --git a/AutoCollection/diagnostic-channel/SpanParser.ts b/AutoCollection/diagnostic-channel/SpanParser.ts index 19d64fe29..e7583bcb3 100644 --- a/AutoCollection/diagnostic-channel/SpanParser.ts +++ b/AutoCollection/diagnostic-channel/SpanParser.ts @@ -9,6 +9,7 @@ import * as Contracts from "../../Declarations/Contracts"; import * as Constants from "../../Declarations/Constants"; import { parseEventHubSpan } from "./Azure/EventHub"; import { DependencyTelemetry } from "../../Declarations/Contracts"; +import Util = require ("../../Library/Util"); function createPropertiesFromSpan(span: ReadableSpan): { [key: string]: any; } { const properties: { [key: string]: any; } = {}; @@ -30,7 +31,7 @@ function createPropertiesFromSpan(span: ReadableSpan): { [key: string]: any; } { id: link.context.spanId })); if (links.length > 0) { - properties["_MS.links"] = JSON.stringify(links); + properties["_MS.links"] = Util.stringify(links); } return properties; } diff --git a/Library/QuickPulseSender.ts b/Library/QuickPulseSender.ts index daa78812e..1412b4be4 100644 --- a/Library/QuickPulseSender.ts +++ b/Library/QuickPulseSender.ts @@ -70,7 +70,7 @@ class QuickPulseSender { additionalHeaders?: { name: string, value: string }[] ): Promise { - const payload = JSON.stringify(envelope); + const payload = Util.stringify(envelope); var options = { [AutoCollectHttpDependencies.disableCollectionRequestOption]: true, host: (redirectedHostEndpoint && redirectedHostEndpoint.length > 0) ? redirectedHostEndpoint : this._config.quickPulseHost, diff --git a/Library/Util.ts b/Library/Util.ts index 969dd590d..481b3d863 100644 --- a/Library/Util.ts +++ b/Library/Util.ts @@ -412,6 +412,14 @@ class Util { return objectTypeDump + propertyValueDump; } + public static stringify(payload: any) { + try { + return JSON.stringify(payload); + } catch (error) { + Logging.warn("Failed to serialize payload", error, payload); + } + } + private static addCorrelationIdHeaderFromString(client: TelemetryClient, response: http.ClientRequest | http.ServerResponse, correlationHeader: string) { const components = correlationHeader.split(","); const key = `${RequestResponseHeaders.requestContextSourceKey}=`; diff --git a/Tests/AutoCollection/Statsbeat.tests.ts b/Tests/AutoCollection/Statsbeat.tests.ts index 2cf73a5a2..c161ee8fb 100644 --- a/Tests/AutoCollection/Statsbeat.tests.ts +++ b/Tests/AutoCollection/Statsbeat.tests.ts @@ -5,7 +5,6 @@ import nock = require("nock"); import AppInsights = require("../../applicationinsights"); import Statsbeat = require("../../AutoCollection/Statsbeat"); import Constants = require("../../Declarations/Constants"); -import Contracts = require("../../Declarations/Contracts"); import TelemetryClient = require("../../Library/TelemetryClient"); import Config = require("../../Library/Config"); @@ -13,14 +12,18 @@ describe("AutoCollection/Statsbeat", () => { var sandbox: sinon.SinonSandbox; const config = new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); Statsbeat.CONNECTION_STRING = "InstrumentationKey=2aa22222-bbbb-1ccc-8ddd-eeeeffff3333;" + let statsBeat: Statsbeat = null; beforeEach(() => { sandbox = sinon.sandbox.create(); + statsBeat = new Statsbeat(config); }); afterEach(() => { sandbox.restore(); AppInsights.dispose(); + statsBeat.enable(false); + statsBeat = null; }); after(() => { @@ -40,48 +43,43 @@ describe("AutoCollection/Statsbeat", () => { describe("#Resource provider property", () => { it("unknown resource provider", (done) => { - const statsBeat: Statsbeat = new Statsbeat(config); - statsBeat["_getResourceProvider"](() => { + statsBeat["_getResourceProvider"]().then(() => { assert.equal(statsBeat["_resourceProvider"], "unknown"); assert.equal(statsBeat["_resourceIdentifier"], "unknown"); done(); - }); - + }).catch((error) => { done(error); }); }); it("app service", (done) => { - const statsBeat: Statsbeat = new Statsbeat(config); var newEnv = <{ [id: string]: string }>{}; newEnv["WEBSITE_SITE_NAME"] = "Test Website"; newEnv["WEBSITE_HOME_STAMPNAME"] = "test_home"; var originalEnv = process.env; process.env = newEnv; - statsBeat["_getResourceProvider"](() => { + statsBeat["_getResourceProvider"]().then(() => { process.env = originalEnv; assert.equal(statsBeat["_resourceProvider"], "appsvc"); assert.equal(statsBeat["_resourceIdentifier"], "Test Website/test_home"); done(); - }); - + }).catch((error) => { done(error); }); }); it("Azure Function", (done) => { - const statsBeat: Statsbeat = new Statsbeat(config); var newEnv = <{ [id: string]: string }>{}; newEnv["FUNCTIONS_WORKER_RUNTIME"] = "test"; newEnv["WEBSITE_HOSTNAME"] = "test_host"; var originalEnv = process.env; process.env = newEnv; - statsBeat["_getResourceProvider"](() => { + statsBeat["_getResourceProvider"]().then(() => { process.env = originalEnv; assert.equal(statsBeat["_resourceProvider"], "functions"); assert.equal(statsBeat["_resourceIdentifier"], "test_host"); done(); - }); + }).catch((error) => { done(error); }); + }); it("Azure VM", (done) => { - const statsBeat: Statsbeat = new Statsbeat(config); var newEnv = <{ [id: string]: string }>{}; var originalEnv = process.env; process.env = newEnv; @@ -94,65 +92,65 @@ describe("AutoCollection/Statsbeat", () => { "subscriptionId": "testsubscriptionId", "osType": "testOsType" }); - statsBeat["_getResourceProvider"](() => { + statsBeat["_getResourceProvider"]().then(() => { process.env = originalEnv; assert.equal(statsBeat["_resourceProvider"], "vm"); assert.equal(statsBeat["_resourceIdentifier"], "testId/testsubscriptionId"); assert.equal(statsBeat["_os"], "testOsType"); done(); - }); + }).catch((error) => { done(error); }); }); }); describe("#trackStatbeats", () => { + beforeEach(() => { + // Prevent handles to be initialized + statsBeat["_longHandle"] = setInterval(() => { }, 0); + statsBeat["_handle"] = setInterval(() => { }, 0); + }); + it("It adds correct network properties to custom metric", (done) => { - const statsBeat: Statsbeat = new Statsbeat(config); statsBeat.enable(true); - const spy = sandbox.spy(statsBeat["_sender"], "send"); - statsBeat.countRequest(123, "testEndpointHost", 123, true); + const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); + statsBeat.countRequest(1, "testEndpointHost", 123, true); statsBeat.setCodelessAttach(); statsBeat.trackShortIntervalStatsbeats().then(() => { - assert.equal(spy.callCount, 2, "should call sender"); - let envelope = spy.args[1][0][0]; - assert.equal(envelope.name, "Statsbeat"); - assert.equal(envelope.iKey, "2aa22222-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.equal(envelope.data.baseType, "MetricData"); - let baseData: Contracts.MetricData = envelope.data.baseData; - assert.equal(baseData.properties["attach"], "codeless"); - assert.equal(baseData.properties["cikey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.equal(baseData.properties["language"], "node"); - assert.equal(baseData.properties["rp"], "unknown"); - assert.equal(baseData.properties["endpoint"], 123); - assert.equal(baseData.properties["host"], "testEndpointHost"); - assert.ok(baseData.properties["os"]); - assert.ok(baseData.properties["runtimeVersion"]); - assert.ok(baseData.properties["version"]); - statsBeat.enable(false); + assert.ok(sendStub.called, "should call _sendStatsbeats"); + let metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Request Duration")[0]; + assert.ok(metric, "Statsbeat Request not found"); + assert.equal(metric.value, 123); + assert.equal(((metric.properties))["attach"], "codeless"); + assert.equal(((metric.properties))["cikey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); + assert.equal(((metric.properties))["language"], "node"); + assert.equal(((metric.properties))["rp"], "unknown"); + assert.equal(((metric.properties))["endpoint"], 1); + assert.equal(((metric.properties))["host"], "testEndpointHost"); + assert.ok(((metric.properties))["os"]); + assert.ok(((metric.properties))["runtimeVersion"]); + assert.ok(((metric.properties))["version"]); + done(); - }); + }).catch((error) => { done(error); }); }); it("Track duration", (done) => { - const statsBeat: Statsbeat = new Statsbeat(config); statsBeat.enable(true); - const spy = sandbox.spy(statsBeat["_sender"], "send"); + const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); statsBeat.countRequest(0, "test", 1000, true); statsBeat.countRequest(0, "test", 500, false); statsBeat.trackShortIntervalStatsbeats().then((error) => { - assert.equal(spy.callCount, 2, "should call sender"); - let envelope = spy.args[1][0][0]; - let baseData: Contracts.MetricData = envelope.data.baseData; - assert.equal(baseData.metrics[0].name, "Request Duration"); - assert.equal(baseData.metrics[0].value, 750); - statsBeat.enable(false); + assert.ok(sendStub.called, "should call _sendStatsbeats"); + assert.equal(statsBeat["_statbeatMetrics"].length, 3); + let metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Request Duration")[0]; + assert.ok(metric, "Request Duration metric not found"); + assert.equal(metric.value, 750); done(); - }); + }).catch((error) => { done(error); }); }); it("Track counts", (done) => { - const statsBeat: Statsbeat = new Statsbeat(config); statsBeat.enable(true); - const spy = sandbox.spy(statsBeat["_sender"], "send"); + const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); statsBeat.countRequest(0, "test", 1, true); statsBeat.countRequest(0, "test", 1, true); statsBeat.countRequest(0, "test", 1, true); @@ -165,104 +163,94 @@ describe("AutoCollection/Statsbeat", () => { statsBeat.countThrottle(0, "test"); statsBeat.countException(0, "test"); statsBeat.trackShortIntervalStatsbeats().then(() => { - assert.equal(spy.callCount, 2, "should call sender"); - let envelope = spy.args[1][0][1]; - let baseData: Contracts.MetricData = envelope.data.baseData; - assert.equal(baseData.metrics[0].name, "Request Success Count"); - assert.equal(baseData.metrics[0].value, 4); - envelope = spy.args[1][0][2]; - baseData = envelope.data.baseData; - assert.equal(baseData.metrics[0].name, "Requests Failure Count"); - assert.equal(baseData.metrics[0].value, 3); - envelope = spy.args[1][0][3]; - baseData = envelope.data.baseData; - assert.equal(baseData.metrics[0].name, "Retry Count"); - assert.equal(baseData.metrics[0].value, 2); - envelope = spy.args[1][0][4]; - baseData = envelope.data.baseData; - assert.equal(baseData.metrics[0].name, "Throttle Count"); - assert.equal(baseData.metrics[0].value, 1); - envelope = spy.args[1][0][5]; - baseData = envelope.data.baseData; - assert.equal(baseData.metrics[0].name, "Exception Count"); - assert.equal(baseData.metrics[0].value, 1); - statsBeat.enable(false); + assert.ok(sendStub.called, "should call _sendStatsbeats"); + assert.equal(statsBeat["_statbeatMetrics"].length, 6); + let metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Request Success Count")[0]; + assert.ok(metric, "Request Success Count metric not found"); + assert.equal(metric.value, 4); + metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Requests Failure Count")[0]; + assert.ok(metric, "Requests Failure Count metric not found"); + assert.equal(metric.value, 3); + metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Retry Count")[0]; + assert.ok(metric, "Retry Count metric not found"); + assert.equal(metric.value, 2); + metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Throttle Count")[0]; + assert.ok(metric, "Throttle Count metric not found"); + assert.equal(metric.value, 1); + metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Exception Count")[0]; + assert.ok(metric, "Exception Count metric not found"); + assert.equal(metric.value, 1); done(); - }); + }).catch((error) => { done(error); }); }); it("Track attach Statbeat", (done) => { - const statsBeat: Statsbeat = new Statsbeat(config); statsBeat.enable(true); - const spy = sandbox.spy(statsBeat["_sender"], "send"); - setImmediate(() => { - let envelope = spy.args[0][0][0]; - let baseData: Contracts.MetricData = envelope.data.baseData; - assert.equal(baseData.metrics[0].name, "Attach"); - assert.equal(baseData.metrics[0].value, 1); - assert.equal(baseData.properties["cikey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.equal(baseData.properties["language"], "node"); - assert.equal(baseData.properties["rp"], "unknown"); - assert.equal(baseData.properties["rpId"], "unknown"); - assert.equal(baseData.properties["attach"], "sdk"); - assert.ok(baseData.properties["os"]); - assert.ok(baseData.properties["runtimeVersion"]); - assert.ok(baseData.properties["version"]); - statsBeat.enable(false); + const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); + statsBeat.trackLongIntervalStatsbeats().then(() => { + assert.ok(sendStub.called, "should call _sendStatsbeats"); + let metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Attach")[0]; + assert.ok(metric, "attach metric not found"); + assert.equal(metric.value, 1); + assert.equal(((metric.properties))["cikey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); + assert.equal(((metric.properties))["language"], "node"); + assert.equal(((metric.properties))["rp"], "unknown"); + assert.equal(((metric.properties))["rpId"], "unknown"); + assert.equal(((metric.properties))["attach"], "sdk"); + assert.ok(((metric.properties))["os"]); + assert.ok(((metric.properties))["runtimeVersion"]); + assert.ok(((metric.properties))["version"]); done(); - }) + }).catch((error) => { done(error); }); }); it("Track feature Statbeat", (done) => { - const statsBeat: Statsbeat = new Statsbeat(config); statsBeat.enable(true); statsBeat.addFeature(Constants.StatsbeatFeature.DISK_RETRY); - const spy = sandbox.spy(statsBeat["_sender"], "send"); - setImmediate(() => { - let envelope = spy.args[0][0][1]; - let baseData: Contracts.MetricData = envelope.data.baseData; - assert.equal(baseData.metrics[0].name, "Feature"); - assert.equal(baseData.metrics[0].value, 1); - assert.equal(baseData.properties["type"], 0); - assert.equal(baseData.properties["cikey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.equal(baseData.properties["language"], "node"); - assert.equal(baseData.properties["rp"], "unknown"); - assert.equal(baseData.properties["attach"], "sdk"); - assert.equal(baseData.properties["feature"], 1); - assert.ok(baseData.properties["os"]); - assert.ok(baseData.properties["runtimeVersion"]); - assert.ok(baseData.properties["version"]); - statsBeat.enable(false); + const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); + statsBeat.trackLongIntervalStatsbeats().then(() => { + assert.ok(sendStub.called, "should call _sendStatsbeats"); + let metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Feature")[0]; + assert.ok(metric, "feature metric not found"); + assert.equal(metric.name, "Feature"); + assert.equal(metric.value, 1); + assert.equal(((metric.properties))["type"], 0); + assert.equal(((metric.properties))["cikey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); + assert.equal(((metric.properties))["language"], "node"); + assert.equal(((metric.properties))["rp"], "unknown"); + assert.equal(((metric.properties))["attach"], "sdk"); + assert.equal(((metric.properties))["feature"], 1); + assert.ok(((metric.properties))["os"]); + assert.ok(((metric.properties))["runtimeVersion"]); + assert.ok(((metric.properties))["version"]); done(); - }) + }).catch((error) => { done(error); }); }); it("Track instrumentation Statbeat", (done) => { - const statsBeat: Statsbeat = new Statsbeat(config); statsBeat.enable(true); statsBeat.addInstrumentation(Constants.StatsbeatInstrumentation.AZURE_CORE_TRACING); - const spy = sandbox.spy(statsBeat["_sender"], "send"); - setImmediate(() => { - let envelope = spy.args[0][0][1]; - let baseData: Contracts.MetricData = envelope.data.baseData; - assert.equal(baseData.metrics[0].name, "Feature"); - assert.equal(baseData.metrics[0].value, 1); - assert.equal(baseData.properties["type"], 1); - assert.equal(baseData.properties["cikey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); - assert.equal(baseData.properties["language"], "node"); - assert.equal(baseData.properties["rp"], "unknown"); - assert.equal(baseData.properties["attach"], "sdk"); - assert.equal(baseData.properties["feature"], 1); - assert.ok(baseData.properties["os"]); - assert.ok(baseData.properties["runtimeVersion"]); - assert.ok(baseData.properties["version"]); - statsBeat.enable(false); + const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); + statsBeat.trackLongIntervalStatsbeats().then(() => { + assert.ok(sendStub.called, "should call _sendStatsbeats"); + let metric = statsBeat["_statbeatMetrics"].filter(f => f.name === "Feature")[0]; + assert.ok(metric, "instrumentation metric not found"); + assert.equal(metric.name, "Feature"); + assert.equal(metric.value, 1); + assert.equal(((metric.properties))["type"], 1); + assert.equal(((metric.properties))["cikey"], "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333"); + assert.equal(((metric.properties))["language"], "node"); + assert.equal(((metric.properties))["rp"], "unknown"); + assert.equal(((metric.properties))["attach"], "sdk"); + assert.equal(((metric.properties))["feature"], 1); + assert.ok(((metric.properties))["os"]); + assert.ok(((metric.properties))["runtimeVersion"]); + assert.ok(((metric.properties))["version"]); done(); - }) + }).catch((error) => { done(error); }); }); it("Instrumentations", () => { - const statsBeat: Statsbeat = new Statsbeat(config); statsBeat.addInstrumentation(Constants.StatsbeatInstrumentation.AZURE_CORE_TRACING); assert.equal(statsBeat["_instrumentation"], 1); statsBeat.addInstrumentation(Constants.StatsbeatInstrumentation.MONGODB); @@ -276,7 +264,6 @@ describe("AutoCollection/Statsbeat", () => { }); it("Features", () => { - const statsBeat: Statsbeat = new Statsbeat(config); statsBeat.addFeature(Constants.StatsbeatFeature.DISK_RETRY); assert.equal(statsBeat["_feature"], 1); statsBeat.addFeature(Constants.StatsbeatFeature.AAD_HANDLING); @@ -286,35 +273,30 @@ describe("AutoCollection/Statsbeat", () => { }); it("Multiple network categories and endpoints", (done) => { - const statsBeat: Statsbeat = new Statsbeat(config); statsBeat.enable(true); - const spy = sandbox.spy(statsBeat["_sender"], "send"); + const sendStub = sandbox.stub(statsBeat, "_sendStatsbeats"); statsBeat.countRequest(0, "breezeFirstEndpoint", 100, true); statsBeat.countRequest(1, "quickpulseEndpoint", 200, true); statsBeat.countRequest(0, "breezeSecondEndpoint", 400, true); statsBeat.trackShortIntervalStatsbeats().then(() => { - assert.equal(spy.callCount, 2, "should call sender"); - let envelope = spy.args[1][0][0]; - let baseData: Contracts.MetricData = envelope.data.baseData; - assert.equal(baseData.metrics[0].name, "Request Duration"); - assert.equal(baseData.metrics[0].value, 100); - assert.equal(baseData.properties["endpoint"], 0); - assert.equal(baseData.properties["host"], "breezeFirstEndpoint"); - envelope = spy.args[1][0][1]; - baseData = envelope.data.baseData; - assert.equal(baseData.metrics[0].name, "Request Duration"); - assert.equal(baseData.metrics[0].value, 200); - assert.equal(baseData.properties["endpoint"], 1); - assert.equal(baseData.properties["host"], "quickpulseEndpoint"); - envelope = spy.args[1][0][2]; - baseData = envelope.data.baseData; - assert.equal(baseData.metrics[0].name, "Request Duration"); - assert.equal(baseData.metrics[0].value, 400); - assert.equal(baseData.properties["endpoint"], 0); - assert.equal(baseData.properties["host"], "breezeSecondEndpoint"); - statsBeat.enable(false); + assert.ok(sendStub.called, "should call _sendStatsbeats"); + let metric: any = statsBeat["_statbeatMetrics"].find(f => f.name === "Request Duration" + && f.value === 100); + assert.ok(metric, "breezeFirstEndpoint metric not found"); + assert.equal(((metric.properties))["endpoint"], 0); + assert.equal(((metric.properties))["host"], "breezeFirstEndpoint"); + metric = statsBeat["_statbeatMetrics"].find(f => f.name === "Request Duration" + && f.value === 200); + assert.ok(metric, "quickpulseEndpoint metric not found"); + assert.equal(((metric.properties))["endpoint"], 1); + assert.equal(((metric.properties))["host"], "quickpulseEndpoint"); + metric = statsBeat["_statbeatMetrics"].find(f => f.name === "Request Duration" + && f.value === 400); + assert.ok(metric, "breezeSecondEndpoint metric not found"); + assert.equal(((metric.properties))["endpoint"], 0); + assert.equal(((metric.properties))["host"], "breezeSecondEndpoint"); done(); - }); - }).timeout(5000); + }).catch((error) => { done(error); }); + }); }); }); \ No newline at end of file diff --git a/Tests/AutoCollection/bunyan.tests.ts b/Tests/AutoCollection/bunyan.tests.ts index c48a1e2b7..7232c4a44 100644 --- a/Tests/AutoCollection/bunyan.tests.ts +++ b/Tests/AutoCollection/bunyan.tests.ts @@ -1,9 +1,10 @@ import assert = require("assert"); import sinon = require("sinon"); import AppInsights = require("../../applicationinsights"); -import { channel, IStandardEvent } from "diagnostic-channel"; +import { channel } from "diagnostic-channel"; import { enable, dispose as disable } from "../../AutoCollection/diagnostic-channel/bunyan.sub"; import { bunyan } from "diagnostic-channel-publishers"; +import Util = require("../../Library/Util"); describe("diagnostic-channel/bunyan", () => { afterEach(() => { @@ -25,7 +26,7 @@ describe("diagnostic-channel/bunyan", () => { }; const dummyError = { stack: "Test error" }; - const bunyanJson = JSON.stringify({ err: dummyError }); + const bunyanJson = Util.stringify({ err: dummyError }); const errorEvent: bunyan.IBunyanData = { result: bunyanJson, level: 10, // Verbose should still log as ExceptionData