From ababc00b43a6fff79c0365537bb926d167024908 Mon Sep 17 00:00:00 2001 From: Xiao Li Date: Fri, 16 Apr 2021 14:19:13 -0700 Subject: [PATCH] [BUG] address issues #1517 add unload listener and #1524 config from snippet (#1532) * bug fix - add unload listener and initialize missing config items * remove unnecessary initializations and pass in config to getDefault method * add correlationHeaderExcludePatterns default value * fix test --- AISKU/src/Initialization.ts | 1 + .../Tests/ApplicationInsights.tests.ts | 4 +- .../src/JavaScriptSDK/ApplicationInsights.ts | 10 +- .../Tests/Selenium/ajax.tests.ts | 125 +++++++++++++++++- .../src/ajax.ts | 1 + 5 files changed, 135 insertions(+), 6 deletions(-) diff --git a/AISKU/src/Initialization.ts b/AISKU/src/Initialization.ts index 05270e01..486e67af 100644 --- a/AISKU/src/Initialization.ts +++ b/AISKU/src/Initialization.ts @@ -485,6 +485,7 @@ export class Initialization implements IApplicationInsights { // Hook the unload event for the document, window and body to ensure that the client events are flushed to the server // As just hooking the window does not always fire (on chrome) for page navigations. let added = addEventHandler('beforeunload', performHousekeeping); + added = addEventHandler('unload', performHousekeeping) || added; added = addEventHandler('pagehide', performHousekeeping) || added; // A reactNative app may not have a window and therefore the beforeunload/pagehide events -- so don't diff --git a/extensions/applicationinsights-analytics-js/Tests/ApplicationInsights.tests.ts b/extensions/applicationinsights-analytics-js/Tests/ApplicationInsights.tests.ts index c420f238..1811cdfe 100644 --- a/extensions/applicationinsights-analytics-js/Tests/ApplicationInsights.tests.ts +++ b/extensions/applicationinsights-analytics-js/Tests/ApplicationInsights.tests.ts @@ -253,7 +253,7 @@ export class ApplicationInsightsTests extends AITestClass { Assert.equal(12, appInsights.config.samplingPercentage); Assert.notEqual('aaa', appInsights.config.accountId); Assert.equal('def', appInsights.config.accountId); - Assert.equal(undefined, (appInsights['config'] as IConfiguration).instrumentationKey); + Assert.equal('instrumentation_key', (appInsights['config'] as IConfiguration).instrumentationKey); Assert.equal(30 * 60 * 1000, appInsights.config.sessionRenewalMs); Assert.equal(24 * 60 * 60 * 1000, appInsights.config.sessionExpirationMs); @@ -262,7 +262,7 @@ export class ApplicationInsightsTests extends AITestClass { Assert.equal(12, extConfig.samplingPercentage); Assert.notEqual('aaa', extConfig.accountId); Assert.equal('def', extConfig.accountId); - Assert.equal(undefined, (extConfig as any).instrumentationKey); + Assert.equal('instrumentation_key', (extConfig as any).instrumentationKey); Assert.equal(30 * 60 * 1000, extConfig.sessionRenewalMs); Assert.equal(24 * 60 * 60 * 1000, extConfig.sessionExpirationMs); } diff --git a/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/ApplicationInsights.ts b/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/ApplicationInsights.ts index b85201a4..5c3026a1 100644 --- a/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/ApplicationInsights.ts +++ b/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/ApplicationInsights.ts @@ -76,6 +76,10 @@ export class ApplicationInsights extends BaseTelemetryPlugin implements IAppInsi config.enableAutoRouteTracking = stringToBoolOrDefault(config.enableAutoRouteTracking); config.namePrefix = config.namePrefix || ""; + config.enableDebug = stringToBoolOrDefault(config.enableDebug); + config.disableFlushOnBeforeUnload = stringToBoolOrDefault(config.disableFlushOnBeforeUnload); + config.disableFlushOnUnload = stringToBoolOrDefault(config.disableFlushOnUnload, config.disableFlushOnBeforeUnload); + return config; } @@ -498,7 +502,7 @@ export class ApplicationInsights extends BaseTelemetryPlugin implements IAppInsi _self.config = ctx.getExtCfg(identifier); // load default values if specified - const defaults: IConfig = ApplicationInsights.getDefaultConfig(); + const defaults: IConfig = ApplicationInsights.getDefaultConfig(config); if (defaults !== undefined) { objForEachKey(defaults, (field, value) => { // for each unspecified field, set the default value @@ -915,7 +919,7 @@ class Timing { if (typeof _events[name] !== "undefined") { logger.throwInternal( LoggingSeverity.WARNING, _InternalMessageId.StartCalledMoreThanOnce, "start was called more than once for this event without calling stop.", - { name: name, key: name }, true); + { name, key: name }, true); } _events[name] = +new Date; @@ -926,7 +930,7 @@ class Timing { if (isNaN(start)) { logger.throwInternal( LoggingSeverity.WARNING, _InternalMessageId.StopCalledWithoutStart, "stop was called without a corresponding start.", - { name: name, key: name }, true); + { name, key: name }, true); } else { const end = +new Date; const duration = dateTimeUtilsDuration(start, end); diff --git a/extensions/applicationinsights-dependencies-js/Tests/Selenium/ajax.tests.ts b/extensions/applicationinsights-dependencies-js/Tests/Selenium/ajax.tests.ts index 58b08cf7..c7ea7660 100644 --- a/extensions/applicationinsights-dependencies-js/Tests/Selenium/ajax.tests.ts +++ b/extensions/applicationinsights-dependencies-js/Tests/Selenium/ajax.tests.ts @@ -1491,7 +1491,46 @@ export class AjaxTests extends TestClass { Assert.equal("|", id[0]); Assert.equal(".", id[id.length - 1]); } - }) + }); + + this.testCase({ + name: "Ajax: should not create and pass a traceparent header if correlationHeaderExcludePatterns set to exclude all", + test: () => { + this._ajax = new AjaxMonitor(); + let appInsightsCore = new AppInsightsCore(); + let coreConfig = { + instrumentationKey: "instrumentationKey", + correlationHeaderExcludePatterns: [/.*/], + extensionConfig: { + "AjaxDependencyPlugin": { + appId: "appId", + distributedTracingMode: DistributedTracingModes.AI_AND_W3C + } + } + }; + appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); + var trackStub = this.sandbox.stub(appInsightsCore, "track"); + + // Use test hook to simulate the correct url location + this._ajax["_currentWindowHost"] = "www.example.com"; + + // Act + var xhr = new XMLHttpRequest(); + var stub = this.sandbox.stub(xhr, "setRequestHeader"); + xhr.open("GET", "http://www.example.com"); + xhr.send(); + + // Assert that both headers are not sent + Assert.equal(false, stub.calledWith(RequestHeaders.requestIdHeader)); // AI + Assert.equal(false, stub.calledWith(RequestHeaders.traceParentHeader)); // W3C + + // Emulate response so perf monitoring is cleaned up + (xhr).respond(200, {"Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*"}, ""); + var id = trackStub.args[0][0].baseData.id; + Assert.equal("|", id[0]); + Assert.equal(".", id[id.length - 1]); + } + }); this.testCase({ name: "Ajax: should create and only pass a traceparent header if w3c is enabled", @@ -2096,6 +2135,90 @@ export class AjaxPerfTests extends TestClass { return false; }, 'response received', 600, 1000) as any) }); + + this.testCaseAsync({ + name: "Fetch: should not create and pass correlation header if correlationHeaderExcludePatterns set to exclude all.", + stepDelay: 10, + timeOut: 10000, + steps: [ (done) => { + let fetchCalls = hookFetch((resolve) => { + TestClass.orgSetTimeout(function() { + resolve({ + headers: new Headers(), + ok: true, + body: null, + bodyUsed: false, + redirected: false, + status: 200, + statusText: "Hello", + trailer: null, + type: "basic", + url: "https://httpbin.org/status/200" + }); + }, 0); + }); + + this._ajax = new AjaxMonitor(); + let appInsightsCore = new AppInsightsCore(); + let coreConfig = { + instrumentationKey: "instrumentationKey", + disableFetchTracking: false, + disableAjaxTracking: false, + correlationHeaderExcludePatterns: [/.*/], + extensionConfig: { + "AjaxDependencyPlugin": { + appId: "appId", + distributedTracingMode: DistributedTracingModes.AI_AND_W3C + } + } + }; + appInsightsCore.initialize(coreConfig, [this._ajax, new TestChannelPlugin()]); + let trackSpy = this.sandbox.spy(appInsightsCore, "track") + this._context["trackStub"] = trackSpy; + + // Use test hook to simulate the correct url location + this._ajax["_currentWindowHost"] = "httpbin.org"; + + // Setup + let headers = new Headers(); + headers.append('My-Header', 'Header field'); + let init = { + method: 'get', + headers + }; + const url = 'https://httpbin.org/status/200'; + + // Act + Assert.ok(trackSpy.notCalled, "No fetch called yet"); + fetch(url, init).then(() => { + // Assert + Assert.ok(trackSpy.called, "The request was not tracked"); + Assert.equal(1, fetchCalls.length); + Assert.notEqual(undefined, fetchCalls[0].init, "Has init param"); + let headers:Headers = fetchCalls[0].init.headers as Headers; + Assert.equal(true, headers.has("My-Header"), "My-Header should be present"); + Assert.equal(false, headers.has(RequestHeaders.requestIdHeader), "Correlation header - AI header should be excluded"); // AI + Assert.equal(false, headers.has(RequestHeaders.traceParentHeader), "Correlation header - W3c header should be excluded"); // W3C + }, () => { + Assert.ok(false, "fetch failed!"); + done(); + }); + }] + .concat(PollingAssert.createPollingAssert(() => { + let trackStub = this._context["trackStub"] as SinonStub; + if (trackStub.called) { + Assert.ok(trackStub.calledOnce, "track is called"); + let data = trackStub.args[0][0].baseData; + Assert.equal("Fetch", data.type, "request is Fatch type"); + var id = data.id; + Assert.equal("|", id[0]); + Assert.equal(".", id[id.length - 1]); + return true; + } + + return false; + }, 'response received', 60, 1000) as any) + }) } } diff --git a/extensions/applicationinsights-dependencies-js/src/ajax.ts b/extensions/applicationinsights-dependencies-js/src/ajax.ts index d2f9ab55..dc4f1359 100644 --- a/extensions/applicationinsights-dependencies-js/src/ajax.ts +++ b/extensions/applicationinsights-dependencies-js/src/ajax.ts @@ -163,6 +163,7 @@ export class AjaxMonitor extends BaseTelemetryPlugin implements IDependenciesPlu "*.blob.core.cloudapi.de", "*.blob.core.usgovcloudapi.net"], correlationHeaderDomains: undefined, + correlationHeaderExcludePatterns: undefined, appId: undefined, enableCorsCorrelation: false, enableRequestHeaderTracking: false,