Describe the bug
In a production build produced by Vite/Rollup, the Ajax (XHR) auto-instrumentation performed by @microsoft/applicationinsights-dependencies-js (AjaxPlugin / AjaxMonitor) never attaches to XMLHttpRequest.prototype. As a result:
XMLHttpRequest.prototype.open / .send remain the browser's native functions after SDK initialization (verified via XMLHttpRequest.prototype.open.toString() → "function open() { [native code] }").
- No
_ajaxData marker is ever attached to XHR instances.
- No
traceparent (W3C distributed tracing) header, nor any dependency telemetry, is ever generated for requests made via XMLHttpRequest (e.g. through axios's default xhr adapter).
- Fetch instrumentation works correctly in the exact same build/runtime:
window.fetch is wrapped (not native), traceparent is correctly injected, and dependency telemetry is correctly generated for fetch()-based calls (e.g. @azure/msal-browser, which uses fetch internally).
This means, in this build, disableAjaxTracking: false is effectively a no-op for XHR: automatic Ajax dependency tracking and distributed-tracing header injection silently never happen for any XHR-based HTTP client, while disableFetchTracking: false behaves as expected.
To Reproduce
- Vue 3 + TypeScript SPA, built with Vite 5.4.10 (Rollup under the hood), served via
vite preview (i.e. testing the real production bundle, not the dev server).
@microsoft/applicationinsights-web 3.4.3, with the applicationinsights-dependencies-js 3.4.3 Ajax extension included (default), configured with:
{
distributedTracingMode: DistributedTracingModes.W3C,
enableCorsCorrelation: true,
maxAjaxCallsPerView: -1,
isBeaconApiDisabled: false,
disableAjaxTracking: false,
disableFetchTracking: false
}
- Application performs API calls via
axios (default xhr adapter) to a same-origin/CORS-enabled backend, and separately performs fetch() calls (via MSAL).
- Load the production build in a real browser (Chromium), open DevTools/network capture, and observe:
- Requests made via
axios/XHR never carry a traceparent header.
- Requests made via
fetch (MSAL) correctly carry a traceparent header.
- Confirm directly in the console:
XMLHttpRequest.prototype.open.toString().includes('[native code]') // → true (should be false once instrumented)
XMLHttpRequest.prototype.send.toString().includes('[native code]') // → true (should be false once instrumented)
window.fetch.toString().includes('[native code]') // → false (correctly wrapped)
Expected behavior
With disableAjaxTracking: false, XMLHttpRequest.prototype.open/.send should be patched by the SDK (via InstrumentProto) the same way window.fetch is patched (via InstrumentFunc), and XHR requests should carry the traceparent header and produce dependency telemetry, consistently with fetch-based requests.
Investigation done so far
We spent significant effort ruling out application-level causes before concluding this looks like an SDK/bundler-interaction issue:
- Not Azure-infrastructure-specific (reproduces identically against a locally-served production build with
vite preview).
- Not caused by any custom app-level network patch (fully disabled/removed, bug persists).
- Not caused by duplicate SDK instances.
- Not a Rollup tree-shaking /
dynamicProto side-effect elimination issue: we forced treeshake: { moduleSideEffects: true } in the Vite/Rollup config, rebuilt, and compared the output bundle — the emitted JS was byte-identical with and without this setting, proving nothing relevant was being tree-shaken to begin with (consistent with the fact that none of the @microsoft/* packages declare "sideEffects": false).
XMLHttpRequest.prototype is not frozen/non-extensible/non-writable in the affected runtime (verified directly: Object.getOwnPropertyDescriptor(XMLHttpRequest.prototype, 'open') → { writable: true, enumerable: true, configurable: true }, and manually reassigning XMLHttpRequest.prototype.open = XMLHttpRequest.prototype.open does not throw), so the SDK's own internal _supportsAjaxMonitoring() write-capability probe (in ajax.js) should not fail either.
- Traced the source in
applicationinsights-dependencies-js/dist-es5/ajax.js: initialize() calls _populateDefaults(config); _instrumentXhr(); _instrumentFetch(); _populateContext(); in sequence — since _instrumentFetch() demonstrably runs and succeeds (fetch is instrumented), _instrumentXhr() must also execute (no way for the second statement to be skipped without the first running), yet it fails to leave any observable trace on XMLHttpRequest.prototype, and does not log any AI-internal critical/warning message even with loggingLevelConsole: 2 set.
_instrumentXhr()'s only external difference vs _instrumentFetch() is that it instruments via InstrumentProto(XMLHttpRequest, "open"/"send", ...) (i.e. InstrumentFunc(target.prototype, ...)), while fetch is instrumented via InstrumentFunc(globalObject, "fetch", ...) directly on the global object rather than through a .prototype indirection — this appears to be the key structural difference between the two code paths, though we were not able to pin down the exact failing line inside the minified/bundled InstrumentFunc/InstrumentProto implementation (applicationinsights-core-js/dist-es5/core/InstrumentHooks.js) in this specific Rollup output.
Versions
@microsoft/applicationinsights-web: 3.4.3
@microsoft/applicationinsights-dependencies-js: 3.4.3 (bundled via applicationinsights-web)
@microsoft/applicationinsights-core-js: (bundled dependency of the above)
- Build tool: Vite 5.4.10 / Rollup (via
vite build)
- Browser tested: Chromium (Playwright-driven), also reproduced in a real user session
axios: 1.16.0 (default xhr adapter)
Additional context
We are aware axios 1.7+ supports an adapter: 'fetch' option, and switching to it works around the issue entirely (since fetch instrumentation is unaffected) — but we wanted to report this because it seems like a genuine compatibility issue between the SDK's Ajax/XHR auto-instrumentation and a Vite/Rollup-bundled production build, which could affect any consumer using XHR-based HTTP clients (axios, jQuery.ajax, raw XHR, etc.) under similar bundler conditions. Happy to provide a minimal reproduction repository if useful.
Describe the bug
In a production build produced by Vite/Rollup, the Ajax (XHR) auto-instrumentation performed by
@microsoft/applicationinsights-dependencies-js(AjaxPlugin/AjaxMonitor) never attaches toXMLHttpRequest.prototype. As a result:XMLHttpRequest.prototype.open/.sendremain the browser's native functions after SDK initialization (verified viaXMLHttpRequest.prototype.open.toString()→"function open() { [native code] }")._ajaxDatamarker is ever attached to XHR instances.traceparent(W3C distributed tracing) header, nor any dependency telemetry, is ever generated for requests made viaXMLHttpRequest(e.g. throughaxios's defaultxhradapter).window.fetchis wrapped (not native),traceparentis correctly injected, and dependency telemetry is correctly generated forfetch()-based calls (e.g.@azure/msal-browser, which usesfetchinternally).This means, in this build,
disableAjaxTracking: falseis effectively a no-op for XHR: automatic Ajax dependency tracking and distributed-tracing header injection silently never happen for any XHR-based HTTP client, whiledisableFetchTracking: falsebehaves as expected.To Reproduce
vite preview(i.e. testing the real production bundle, not the dev server).@microsoft/applicationinsights-web3.4.3, with theapplicationinsights-dependencies-js3.4.3 Ajax extension included (default), configured with:axios(defaultxhradapter) to a same-origin/CORS-enabled backend, and separately performsfetch()calls (via MSAL).axios/XHR never carry atraceparentheader.fetch(MSAL) correctly carry atraceparentheader.Expected behavior
With
disableAjaxTracking: false,XMLHttpRequest.prototype.open/.sendshould be patched by the SDK (viaInstrumentProto) the same waywindow.fetchis patched (viaInstrumentFunc), and XHR requests should carry thetraceparentheader and produce dependency telemetry, consistently with fetch-based requests.Investigation done so far
We spent significant effort ruling out application-level causes before concluding this looks like an SDK/bundler-interaction issue:
vite preview).dynamicProtoside-effect elimination issue: we forcedtreeshake: { moduleSideEffects: true }in the Vite/Rollup config, rebuilt, and compared the output bundle — the emitted JS was byte-identical with and without this setting, proving nothing relevant was being tree-shaken to begin with (consistent with the fact that none of the@microsoft/*packages declare"sideEffects": false).XMLHttpRequest.prototypeis not frozen/non-extensible/non-writable in the affected runtime (verified directly:Object.getOwnPropertyDescriptor(XMLHttpRequest.prototype, 'open')→{ writable: true, enumerable: true, configurable: true }, and manually reassigningXMLHttpRequest.prototype.open = XMLHttpRequest.prototype.opendoes not throw), so the SDK's own internal_supportsAjaxMonitoring()write-capability probe (inajax.js) should not fail either.applicationinsights-dependencies-js/dist-es5/ajax.js:initialize()calls_populateDefaults(config); _instrumentXhr(); _instrumentFetch(); _populateContext();in sequence — since_instrumentFetch()demonstrably runs and succeeds (fetch is instrumented),_instrumentXhr()must also execute (no way for the second statement to be skipped without the first running), yet it fails to leave any observable trace onXMLHttpRequest.prototype, and does not log any AI-internal critical/warning message even withloggingLevelConsole: 2set._instrumentXhr()'s only external difference vs_instrumentFetch()is that it instruments viaInstrumentProto(XMLHttpRequest, "open"/"send", ...)(i.e.InstrumentFunc(target.prototype, ...)), while fetch is instrumented viaInstrumentFunc(globalObject, "fetch", ...)directly on the global object rather than through a.prototypeindirection — this appears to be the key structural difference between the two code paths, though we were not able to pin down the exact failing line inside the minified/bundledInstrumentFunc/InstrumentProtoimplementation (applicationinsights-core-js/dist-es5/core/InstrumentHooks.js) in this specific Rollup output.Versions
@microsoft/applicationinsights-web: 3.4.3@microsoft/applicationinsights-dependencies-js: 3.4.3 (bundled viaapplicationinsights-web)@microsoft/applicationinsights-core-js: (bundled dependency of the above)vite build)axios: 1.16.0 (defaultxhradapter)Additional context
We are aware
axios1.7+ supports anadapter: 'fetch'option, and switching to it works around the issue entirely (since fetch instrumentation is unaffected) — but we wanted to report this because it seems like a genuine compatibility issue between the SDK's Ajax/XHR auto-instrumentation and a Vite/Rollup-bundled production build, which could affect any consumer using XHR-based HTTP clients (axios, jQuery.ajax, raw XHR, etc.) under similar bundler conditions. Happy to provide a minimal reproduction repository if useful.