diff --git a/API-reference.md b/API-reference.md index 1e158ab4..4176ec5e 100644 --- a/API-reference.md +++ b/API-reference.md @@ -315,8 +315,9 @@ interface IConfig { // Default: false isStorageUseDisabled: boolean; - // Default true. If false, the SDK will add two headers ('x-ms-request-root-id' and 'x-ms-request-id) - // to all dependency requests (within the same domain) to correlate them with corresponding requests on the server side. + // If false, the SDK will add two headers ('Request-Id' and 'Request-Context') to all + // dependency requests to correlate them with corresponding requests on the server side. + // Default false. disableCorrelationHeaders: boolean; // If true, the SDK will send all telemetry using [Beacon API](https://www.w3.org/TR/beacon/) @@ -332,6 +333,11 @@ interface IConfig { // If true, the SDK will track all [Browser Link](https://docs.microsoft.com/en-us/aspnet/core/client-side/using-browserlink) requests. // Default: false isBrowserLinkTrackingEnabled: boolean; + + // AppId is used for the correlation between AJAX dependencies happening on the client-side with the server-side requests. + // When Beacon API is enabled it can not be used automatically, but can be set manually in the configuration. + // Default: null + appId: string; } ``` diff --git a/JavaScript/JavaScriptSDK.Interfaces/IConfig.ts b/JavaScript/JavaScriptSDK.Interfaces/IConfig.ts index b6c89f5a..aba0b81b 100644 --- a/JavaScript/JavaScriptSDK.Interfaces/IConfig.ts +++ b/JavaScript/JavaScriptSDK.Interfaces/IConfig.ts @@ -34,5 +34,6 @@ isBeaconApiDisabled?: boolean; sdkExtension?: string; isBrowserLinkTrackingEnabled?: boolean; + appId?: string; } } \ No newline at end of file diff --git a/JavaScript/JavaScriptSDK.Tests/CheckinTests/Context/Session.tests.ts b/JavaScript/JavaScriptSDK.Tests/CheckinTests/Context/Session.tests.ts index 605f5b5c..78d6aa0d 100644 --- a/JavaScript/JavaScriptSDK.Tests/CheckinTests/Context/Session.tests.ts +++ b/JavaScript/JavaScriptSDK.Tests/CheckinTests/Context/Session.tests.ts @@ -516,7 +516,8 @@ class SessionContextTests extends TestClass { isRetryDisabled: () => null, isBeaconApiDisabled: () => null, sdkExtension: () => null, - isBrowserLinkTrackingEnabled: () => null + isBrowserLinkTrackingEnabled: () => null, + appId: () => null, }; } } diff --git a/JavaScript/JavaScriptSDK.Tests/CheckinTests/Context/User.tests.ts b/JavaScript/JavaScriptSDK.Tests/CheckinTests/Context/User.tests.ts index 575d86cf..0f05d02b 100644 --- a/JavaScript/JavaScriptSDK.Tests/CheckinTests/Context/User.tests.ts +++ b/JavaScript/JavaScriptSDK.Tests/CheckinTests/Context/User.tests.ts @@ -413,7 +413,8 @@ class UserContextTests extends TestClass { isRetryDisabled: () => null, isBeaconApiDisabled: () => null, sdkExtension: () => null, - isBrowserLinkTrackingEnabled: () => null + isBrowserLinkTrackingEnabled: () => null, + appId: () => null, }; } } diff --git a/JavaScript/JavaScriptSDK.Tests/CheckinTests/Sender.tests.ts b/JavaScript/JavaScriptSDK.Tests/CheckinTests/Sender.tests.ts index 634c59ba..c6ab50ce 100644 --- a/JavaScript/JavaScriptSDK.Tests/CheckinTests/Sender.tests.ts +++ b/JavaScript/JavaScriptSDK.Tests/CheckinTests/Sender.tests.ts @@ -162,7 +162,7 @@ class SenderTests extends TestClass { // verify this.requestAsserts(); - this.fakeServer.requests.pop().respond(200, { "Content-Type": "application/json" }, '{"test":"success"}'); + this.fakeServer.requests.pop().respond(200, { "Content-Type": "application/json" }, '{"itemsReceived": 1, "itemsAccepted": 1, "errors": []}'); this.successAsserts(sender); this.logAsserts(0); sender.successSpy.reset(); @@ -175,9 +175,9 @@ class SenderTests extends TestClass { // verify this.requestAsserts(); - this.fakeServer.requests.pop().respond(404, { "Content-Type": "application/json" }, 'some_error'); + this.fakeServer.requests[0].respond(404, { "Content-Type": "application/json" }, '{"itemsReceived": 1, "itemsAccepted": 0, "errors": [{ "index": 0, "statusCode": 404, "message": "Not found" }]}'); this.errorAsserts(sender); - this.logAsserts(1, "message:XMLHttpRequest,Status:404,Response:some_error"); + this.logAsserts(1, "message:XMLHttpRequest,Status:404"); sender.successSpy.reset(); sender.errorSpy.reset(); } @@ -228,9 +228,9 @@ class SenderTests extends TestClass { // verify this.requestAsserts(); - this.fakeServer.requests[0].respond(404, { "Content-Type": "application/json" }, '400'); + this.fakeServer.requests[0].respond(404, { "Content-Type": "application/json" }, '{ "itemsReceived": 1, "itemsAccepted": 0, "errors": [{ "index": 0, "statusCode": 404, "message": "Not found" }]}'); this.errorAsserts(sender); - this.logAsserts(1, "message:XDomainRequest,Response:400"); + this.logAsserts(1, "message:partial success 0 of 1"); sender.successSpy.reset(); sender.errorSpy.reset(); } @@ -725,6 +725,84 @@ class SenderTests extends TestClass { } }); + this.testCase({ + name: "SenderTests: XMLHttpRequest sender successfully parses appId from the response.", + test: () => { + // setup + XMLHttpRequest = (() => { + var xhr = new this.xhr; + xhr.withCredentials = false; + return xhr; + }); + + var sender = this.getSender(); + Assert.ok(sender, "sender was constructed. Testing response code: 200"); + + this.fakeServer.requests.pop(); + sender.send(this.testTelemetry); + + Assert.equal(1, sender._buffer.count(), "Buffer has one item"); + + // trigger send + this.clock.tick(sender._config.maxBatchInterval()); + + this.requestAsserts(); + this.fakeServer.requests.pop().respond( + 200, + { "Content-Type": "application/json" }, + '{ "itemsReceived": 1, "itemsAccepted": 1, "errors": [], "appId": "C16FBA4D-ECE9-472E-8125-4FF5BEFAF8C1" }' + ); + + // verify + Assert.ok(sender.successSpy.called, "success was invoked"); + Assert.ok(sender.errorSpy.notCalled, "error was not invoked"); + + Assert.equal("C16FBA4D-ECE9-472E-8125-4FF5BEFAF8C1", sender._appId, "App Id was parsed."); + + // clean up + this.testCleanup(); + } + }); + + this.testCase({ + name: "SenderTests: XMLHttpRequest sender does not store appId from the response if it's not returned.", + test: () => { + // setup + XMLHttpRequest = (() => { + var xhr = new this.xhr; + xhr.withCredentials = false; + return xhr; + }); + + var sender = this.getSender(); + Assert.ok(sender, "sender was constructed. Testing response code: 200"); + + this.fakeServer.requests.pop(); + sender.send(this.testTelemetry); + + Assert.equal(1, sender._buffer.count(), "Buffer has one item"); + + // trigger send + this.clock.tick(sender._config.maxBatchInterval()); + + this.requestAsserts(); + this.fakeServer.requests.pop().respond( + 200, + { "Content-Type": "application/json" }, + '{ "itemsReceived": 1, "itemsAccepted": 1, "errors": [] }' + ); + + // verify + Assert.ok(sender.successSpy.called, "success was invoked"); + Assert.ok(sender.errorSpy.notCalled, "error was not invoked"); + + Assert.equal(null, sender._appId, "App Id was not parsed."); + + // clean up + this.testCleanup(); + } + }); + this.testCase({ name: "SenderTests: XMLHttpRequest sender does NOT retry on non-retriable response code from the backend.", test: () => { diff --git a/JavaScript/JavaScriptSDK.Tests/CheckinTests/TelemetryContext.tests.ts b/JavaScript/JavaScriptSDK.Tests/CheckinTests/TelemetryContext.tests.ts index ec00091a..a643e696 100644 --- a/JavaScript/JavaScriptSDK.Tests/CheckinTests/TelemetryContext.tests.ts +++ b/JavaScript/JavaScriptSDK.Tests/CheckinTests/TelemetryContext.tests.ts @@ -26,7 +26,8 @@ class TelemetryContextTests extends TestClass { isRetryDisabled: () => false, isBeaconApiDisabled: () => true, sdkExtension: () => null, - isBrowserLinkTrackingEnabled: () => false + isBrowserLinkTrackingEnabled: () => false, + appId: () => undefined, } this._telemetryContext = new Microsoft.ApplicationInsights.TelemetryContext(this._config); diff --git a/JavaScript/JavaScriptSDK.Tests/CheckinTests/ajax.tests.ts b/JavaScript/JavaScriptSDK.Tests/CheckinTests/ajax.tests.ts index fd568630..786bb789 100644 --- a/JavaScript/JavaScriptSDK.Tests/CheckinTests/ajax.tests.ts +++ b/JavaScript/JavaScriptSDK.Tests/CheckinTests/ajax.tests.ts @@ -11,7 +11,8 @@ class AjaxTests extends TestClass { context: { operation: { id: "asdf" - } + }, + appId: () => "someid" }, config: { disableCorrelationHeaders: false diff --git a/JavaScript/JavaScriptSDK.Tests/CheckinTests/appInsights.tests.ts b/JavaScript/JavaScriptSDK.Tests/CheckinTests/appInsights.tests.ts index b1c2edae..ee91380e 100644 --- a/JavaScript/JavaScriptSDK.Tests/CheckinTests/appInsights.tests.ts +++ b/JavaScript/JavaScriptSDK.Tests/CheckinTests/appInsights.tests.ts @@ -31,7 +31,8 @@ class AppInsightsTests extends TestClass { isCookieUseDisabled: false, isRetryDisabled: false, isStorageUseDisabled: false, - isBeaconApiDisabled: true + isBeaconApiDisabled: true, + appId: undefined }; // set default values @@ -175,6 +176,28 @@ class AppInsightsTests extends TestClass { } }); + this.testCase({ + name: "AppInsightsTests: appId is propagated from the config", + test: () => { + var expectedAppId = "BDC8736D-D8E8-4B69-B19B-B0CE6B66A456"; + + // setup + var config = this.getAppInsightsSnippet(); + config.appId = expectedAppId; + var appInsights = new Microsoft.ApplicationInsights.AppInsights(config); + var trackStub = this.sandbox.stub(appInsights.context._sender, "send"); + this.clock.tick(60000); + + Assert.equal(expectedAppId, appInsights.context._sender._appId); + + // act + appInsights.trackEvent("testEvent"); + + // verify + Assert.equal(expectedAppId, appInsights.context._sender._appId); + } + }); + this.testCase({ name: "AppInsightsTests: application context is applied", test: () => { @@ -1710,13 +1733,14 @@ class AppInsightsTests extends TestClass { }); this.testCase({ - name: "Ajax - Request-Id is set and passed correctly", + name: "Ajax - Request-Context is not set if appId was not set", test: () => { var snippet = this.getAppInsightsSnippet(); snippet.disableAjaxTracking = false; snippet.disableCorrelationHeaders = false; snippet.maxBatchInterval = 0; var appInsights = new Microsoft.ApplicationInsights.AppInsights(snippet); + var trackStub = this.sandbox.spy(appInsights, "trackDependencyData"); var expectedRootId = appInsights.context.operation.id; Assert.ok(expectedRootId.length > 0, "root id was initialized to non empty string"); @@ -1733,7 +1757,40 @@ class AppInsightsTests extends TestClass { (xhr).respond("200", {}, ""); // Assert - Assert.equal(expectedAjaxId, (xhr).requestHeaders['Request-Id'], "Request-Id id set correctly"); + Assert.equal(expectedAjaxId, (xhr).requestHeaders['Request-Id'], "Request-Id is set correctly"); + Assert.equal(null, (xhr).requestHeaders['Request-Context'], "Request-Context is not set"); + Assert.equal(expectedAjaxId, trackStub.args[0][0].id, "ajax id passed to trackAjax correctly"); + } + }); + + this.testCase({ + name: "Ajax - Request-Id and Request-Context are set and passed correctly", + test: () => { + var snippet = this.getAppInsightsSnippet(); + snippet.disableAjaxTracking = false; + snippet.disableCorrelationHeaders = false; + snippet.maxBatchInterval = 0; + var appInsights = new Microsoft.ApplicationInsights.AppInsights(snippet); + appInsights.context.appId = () => "C16FBA4D-ECE9-472E-8125-4FF5BEFAF8C1"; + + var trackStub = this.sandbox.spy(appInsights, "trackDependencyData"); + var expectedRootId = appInsights.context.operation.id; + Assert.ok(expectedRootId.length > 0, "root id was initialized to non empty string"); + + // Act + var xhr = new XMLHttpRequest(); + xhr.open("GET", "/bla"); + xhr.send(); + + var expectedAjaxId = (xhr).ajaxData.id; + Assert.ok(expectedAjaxId.length > 0, "ajax id was initialized"); + + // Emulate response + (xhr).respond("200", {}, ""); + + // Assert + Assert.equal(expectedAjaxId, (xhr).requestHeaders['Request-Id'], "Request-Id is set correctly"); + Assert.equal("appId=cid-v1:C16FBA4D-ECE9-472E-8125-4FF5BEFAF8C1", (xhr).requestHeaders['Request-Context'], "Request-Context is set correctly"); Assert.equal(expectedAjaxId, trackStub.args[0][0].id, "ajax id passed to trackAjax correctly"); } }); @@ -1772,13 +1829,16 @@ class AppInsightsTests extends TestClass { }); this.testCase({ - name: "Ajax - disableCorrelationHeaders disables Request-Id header", + name: "Ajax - disableCorrelationHeaders disables Request-Id and Request-Context headers", test: () => { var snippet = this.getAppInsightsSnippet(); snippet.disableAjaxTracking = false; snippet.disableCorrelationHeaders = true; snippet.maxBatchInterval = 0; + var appInsights = new Microsoft.ApplicationInsights.AppInsights(snippet); + appInsights.context.appId = () => "C16FBA4D-ECE9-472E-8125-4FF5BEFAF8C1"; + var trackStub = this.sandbox.spy(appInsights, "trackDependencyData"); var expectedRootId = appInsights.context.operation.id; Assert.ok(expectedRootId.length > 0, "root id was initialized to non empty string"); @@ -1793,11 +1853,12 @@ class AppInsightsTests extends TestClass { // Assert Assert.equal(null, (xhr).requestHeaders['Request-Id'], "Request-Id header is not set."); + Assert.equal(null, (xhr).requestHeaders['Request-Context'], "Request-Context header is not set."); } }); this.testCase({ - name: "Ajax - Request-Id header is disabled for excluded domain", + name: "Ajax - Request-Id and Request-Context headers are disabled for excluded domain", test: () => { var snippet = this.getAppInsightsSnippet(); snippet.disableAjaxTracking = false; @@ -1819,6 +1880,7 @@ class AppInsightsTests extends TestClass { // Assert Assert.equal(null, (xhr).requestHeaders['Request-Id'], "Request-Id header is not set."); + Assert.equal(null, (xhr).requestHeaders['Request-Context'], "Request-Context header is not set."); } }); } diff --git a/JavaScript/JavaScriptSDK.Tests/Selenium/ai.tests.d.ts b/JavaScript/JavaScriptSDK.Tests/Selenium/ai.tests.d.ts index cbe6b305..c9b1309e 100644 --- a/JavaScript/JavaScriptSDK.Tests/Selenium/ai.tests.d.ts +++ b/JavaScript/JavaScriptSDK.Tests/Selenium/ai.tests.d.ts @@ -1272,10 +1272,23 @@ declare module Microsoft.ApplicationInsights { * calling application when processing incoming responses. */ static requestContextTargetKey: string; + /** + * Request-Context appId format + */ + static requestContextAppIdFormat: string; /** * Request-Id header */ static requestIdHeader: string; + /** + * Sdk-Context header + * If this header passed with appId in content then appId will be returned back by the backend. + */ + static sdkContextHeader: string; + /** + * String to pass in header for requesting appId back from the backend. + */ + static sdkContextHeaderAppIdRequest: string; } } declare module Microsoft.Telemetry { @@ -1613,6 +1626,10 @@ declare module Microsoft.ApplicationInsights { * List of errors for items which were not accepted */ errors: IResponseError[]; + /** + * App id returned by the backend - not necessary returned, but we don't need it with each response. + */ + appId?: string; } class Sender { /** @@ -1639,6 +1656,10 @@ declare module Microsoft.ApplicationInsights { * The configuration for this sender instance */ _config: ISenderConfig; + /** + * AppId of this component parsed from some backend response. + */ + _appId: string; /** * A method which will cause data to be send to the url */ @@ -1708,12 +1729,16 @@ declare module Microsoft.ApplicationInsights { * * Note: XDomainRequest does not support sync requests. This 'isAsync' parameter is added * to maintain consistency with the xhrSender's contract + * Note: XDomainRequest does not support custom headers and we are not able to get + * appId from the backend for the correct correlation. */ private _xdrSender(payload, isAsync); /** * Send Beacon API request * @param payload {string} - The data payload to be sent. * @param isAsync {boolean} - not used + * Note: Beacon API does not support custom headers and we are not able to get + * appId from the backend for the correct correlation. */ private _beaconSender(payload, isAsync); /** @@ -2223,6 +2248,7 @@ declare module Microsoft.ApplicationInsights { cookieDomain: () => string; sdkExtension: () => string; isBrowserLinkTrackingEnabled: () => boolean; + appId: () => string; } class TelemetryContext implements ITelemetryContext { /** @@ -2259,6 +2285,10 @@ declare module Microsoft.ApplicationInsights { * The object describing a session tracked by this object. */ session: Context.Session; + /** + * AppId of this component if returned by the backend. + */ + appId: () => string; /** * The array of telemetry initializers to call before sending each telemetry item. */ @@ -2426,6 +2456,7 @@ declare module Microsoft.ApplicationInsights { isBeaconApiDisabled?: boolean; sdkExtension?: string; isBrowserLinkTrackingEnabled?: boolean; + appId?: string; } } declare module Microsoft.ApplicationInsights { diff --git a/JavaScript/JavaScriptSDK/AppInsights.ts b/JavaScript/JavaScriptSDK/AppInsights.ts index 9f8a9e13..152edbea 100644 --- a/JavaScript/JavaScriptSDK/AppInsights.ts +++ b/JavaScript/JavaScriptSDK/AppInsights.ts @@ -85,7 +85,8 @@ module Microsoft.ApplicationInsights { isRetryDisabled: () => this.config.isRetryDisabled, isBeaconApiDisabled: () => this.config.isBeaconApiDisabled, sdkExtension: () => this.config.sdkExtension, - isBrowserLinkTrackingEnabled: () => this.config.isBrowserLinkTrackingEnabled + isBrowserLinkTrackingEnabled: () => this.config.isBrowserLinkTrackingEnabled, + appId: () => this.config.appId, } if (this.config.isCookieUseDisabled) { diff --git a/JavaScript/JavaScriptSDK/RequestResponseHeaders.ts b/JavaScript/JavaScriptSDK/RequestResponseHeaders.ts index 3ffd1f79..1bd11a3d 100644 --- a/JavaScript/JavaScriptSDK/RequestResponseHeaders.ts +++ b/JavaScript/JavaScriptSDK/RequestResponseHeaders.ts @@ -5,7 +5,7 @@ module Microsoft.ApplicationInsights { /** * Request-Context header */ - public static requestContextHeader = "request-context"; + public static requestContextHeader = "Request-Context"; /** * Target instrumentation header that is added to the response and retrieved by the @@ -13,9 +13,25 @@ module Microsoft.ApplicationInsights { */ public static requestContextTargetKey = "appId"; + /** + * Request-Context appId format + */ + public static requestContextAppIdFormat = "appId=cid-v1:"; + /** * Request-Id header */ public static requestIdHeader = "Request-Id"; + + /** + * Sdk-Context header + * If this header passed with appId in content then appId will be returned back by the backend. + */ + public static sdkContextHeader = "Sdk-Context"; + + /** + * String to pass in header for requesting appId back from the backend. + */ + public static sdkContextHeaderAppIdRequest = "appId"; } } diff --git a/JavaScript/JavaScriptSDK/Sender.ts b/JavaScript/JavaScriptSDK/Sender.ts index d918a841..9c18791f 100644 --- a/JavaScript/JavaScriptSDK/Sender.ts +++ b/JavaScript/JavaScriptSDK/Sender.ts @@ -89,6 +89,11 @@ module Microsoft.ApplicationInsights { * List of errors for items which were not accepted */ errors: IResponseError[]; + + /** + * App id returned by the backend - not necessary returned, but we don't need it with each response. + */ + appId?: string; } export class Sender { @@ -122,6 +127,11 @@ module Microsoft.ApplicationInsights { */ public _config: ISenderConfig; + /** + * AppId of this component parsed from some backend response. + */ + public _appId: string; + /** * A method which will cause data to be send to the url */ @@ -388,6 +398,7 @@ module Microsoft.ApplicationInsights { xhr[AjaxMonitor.DisabledPropertyName] = true; xhr.open("POST", this._config.endpointUrl(), isAsync); xhr.setRequestHeader("Content-type", "application/json"); + xhr.setRequestHeader(RequestHeaders.sdkContextHeader, RequestHeaders.sdkContextHeaderAppIdRequest); xhr.onreadystatechange = () => this._xhrReadyStateChange(xhr, payload, payload.length); xhr.onerror = (event: ErrorEvent) => this._onError(payload, this._formatErrorMessageXhr(xhr), event); @@ -405,6 +416,8 @@ module Microsoft.ApplicationInsights { * * Note: XDomainRequest does not support sync requests. This 'isAsync' parameter is added * to maintain consistency with the xhrSender's contract + * Note: XDomainRequest does not support custom headers and we are not able to get + * appId from the backend for the correct correlation. */ private _xdrSender(payload: string[], isAsync: boolean) { var xdr = new XDomainRequest(); @@ -438,6 +451,8 @@ module Microsoft.ApplicationInsights { * Send Beacon API request * @param payload {string} - The data payload to be sent. * @param isAsync {boolean} - not used + * Note: Beacon API does not support custom headers and we are not able to get + * appId from the backend for the correct correlation. */ private _beaconSender(payload: string[], isAsync: boolean) { var url = this._config.endpointUrl(); @@ -462,6 +477,14 @@ module Microsoft.ApplicationInsights { */ public _xhrReadyStateChange(xhr: XMLHttpRequest, payload: string[], countOfItemsInPayload: number) { if (xhr.readyState === 4) { + var response: IBackendResponse = null; + if (!this._appId) { + response = this._parseResponse(xhr.responseText || xhr.response); + if (response && response.appId) { + this._appId = response.appId; + } + } + if ((xhr.status < 200 || xhr.status >= 300) && xhr.status !== 0) { if (!this._config.isRetryDisabled() && this._isRetriable(xhr.status)) { this._resendPayload(payload); @@ -475,7 +498,9 @@ module Microsoft.ApplicationInsights { } } else { if (xhr.status === 206) { - var response = this._parseResponse(xhr.responseText || xhr.response); + if (!response) { + response = this._parseResponse(xhr.responseText || xhr.response); + } if (response && !this._config.isRetryDisabled()) { this._onPartialSuccess(payload, response); diff --git a/JavaScript/JavaScriptSDK/TelemetryContext.ts b/JavaScript/JavaScriptSDK/TelemetryContext.ts index 4d188890..eb545a4f 100644 --- a/JavaScript/JavaScriptSDK/TelemetryContext.ts +++ b/JavaScript/JavaScriptSDK/TelemetryContext.ts @@ -20,6 +20,7 @@ module Microsoft.ApplicationInsights { cookieDomain: () => string; sdkExtension: () => string; isBrowserLinkTrackingEnabled: () => boolean; + appId: () => string; } export class TelemetryContext implements ITelemetryContext { @@ -67,6 +68,11 @@ module Microsoft.ApplicationInsights { */ public session: Context.Session; + /** + * AppId of this component if returned by the backend. + */ + public appId: () => string; + /** * The array of telemetry initializers to call before sending each telemetry item. */ @@ -80,6 +86,13 @@ module Microsoft.ApplicationInsights { constructor(config: ITelemetryConfig) { this._config = config; this._sender = new Sender(config); + this.appId = () => this._sender._appId; + + // use appId set in config instead of getting it from the backend + if (config.appId()) { + this._sender._appId = config.appId(); + } + this.telemetryInitializers = []; // window will be undefined in node.js where we do not want to initialize contexts diff --git a/JavaScript/JavaScriptSDK/ajax/ajax.ts b/JavaScript/JavaScriptSDK/ajax/ajax.ts index dcf6770d..7fce505c 100644 --- a/JavaScript/JavaScriptSDK/ajax/ajax.ts +++ b/JavaScript/JavaScriptSDK/ajax/ajax.ts @@ -159,6 +159,12 @@ module Microsoft.ApplicationInsights { if (CorrelationIdHelper.canIncludeCorrelationHeader(this.appInsights.config, xhr.ajaxData.getAbsoluteUrl())) { xhr.setRequestHeader(RequestHeaders.requestIdHeader, xhr.ajaxData.id); + if (this.appInsights.context) { + var appId = this.appInsights.context.appId(); + if (appId) { + xhr.setRequestHeader(RequestHeaders.requestContextHeader, RequestHeaders.requestContextAppIdFormat + appId); + } + } } xhr.ajaxData.xhrMonitoringState.sendDone = true; }