Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] ld: -1 does not block page load #1742

Closed
50Wliu opened this issue Jan 11, 2022 · 16 comments
Closed

[BUG] ld: -1 does not block page load #1742

50Wliu opened this issue Jan 11, 2022 · 16 comments
Labels

Comments

@50Wliu
Copy link
Member

50Wliu commented Jan 11, 2022

Description/Screenshot
Setting ld to -1 does not appear to block the page load, which is causing SDK load failures for us due to conflicts with a script file that should be loaded after App Insights.
ld: -1 does not block
Notice how loader.js is loaded before ai.2.min.js, even though loader.js is called in the body.

Steps to Reproduce

  1. Set ld: -1
  2. Add some scripts into the body
  3. Reload the page
  • OS/Browser: Edge Version 98.0.1108.15 (Official build) dev (64-bit)
  • SDK Version [e.g. 22]: Uh...how do I find this?
  • How you initialized the SDK: JavaScript snippet

Expected behavior
ai.2.min.js loads before any other scripts or styles.

Additional context
This is a hosted Blazor WASM app. Here is a full index.html, with the only extra package being BlazorMonaco:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>AITest</title>
    <base href="/" />
    <script type="text/javascript">
    !function(T,l,y){var S=T.location,k="script",D="instrumentationKey",C="ingestionendpoint",I="disableExceptionTracking",E="ai.device.",b="toLowerCase",w="crossOrigin",N="POST",e="appInsightsSDK",t=y.name||"appInsights";(y.name||T[e])&&(T[e]=t);var n=T[t]||function(d){var g=!1,f=!1,m={initialize:!0,queue:[],sv:"5",version:2,config:d};function v(e,t){var n={},a="Browser";return n[E+"id"]=a[b](),n[E+"type"]=a,n["ai.operation.name"]=S&&S.pathname||"_unknown_",n["ai.internal.sdkVersion"]="javascript:snippet_"+(m.sv||m.version),{time:function(){var e=new Date;function t(e){var t=""+e;return 1===t.length&&(t="0"+t),t}return e.getUTCFullYear()+"-"+t(1+e.getUTCMonth())+"-"+t(e.getUTCDate())+"T"+t(e.getUTCHours())+":"+t(e.getUTCMinutes())+":"+t(e.getUTCSeconds())+"."+((e.getUTCMilliseconds()/1e3).toFixed(3)+"").slice(2,5)+"Z"}(),iKey:e,name:"Microsoft.ApplicationInsights."+e.replace(/-/g,"")+"."+t,sampleRate:100,tags:n,data:{baseData:{ver:2}}}}var h=d.url||y.src;if(h){function a(e){var t,n,a,i,r,o,s,c,u,p,l;g=!0,m.queue=[],f||(f=!0,t=h,s=function(){var e={},t=d.connectionString;if(t)for(var n=t.split(";"),a=0;a<n.length;a++){var i=n[a].split("=");2===i.length&&(e[i[0][b]()]=i[1])}if(!e[C]){var r=e.endpointsuffix,o=r?e.location:null;e[C]="https://"+(o?o+".":"")+"dc."+(r||"services.visualstudio.com")}return e}(),c=s[D]||d[D]||"",u=s[C],p=u?u+"/v2/track":d.endpointUrl,(l=[]).push((n="SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details)",a=t,i=p,(o=(r=v(c,"Exception")).data).baseType="ExceptionData",o.baseData.exceptions=[{typeName:"SDKLoadFailed",message:n.replace(/\./g,"-"),hasFullStack:!1,stack:n+"\nSnippet failed to load ["+a+"] -- Telemetry is disabled\nHelp Link: https://go.microsoft.com/fwlink/?linkid=2128109\nHost: "+(S&&S.pathname||"_unknown_")+"\nEndpoint: "+i,parsedStack:[]}],r)),l.push(function(e,t,n,a){var i=v(c,"Message"),r=i.data;r.baseType="MessageData";var o=r.baseData;return o.message='AI (Internal): 99 message:"'+("SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details) ("+n+")").replace(/\"/g,"")+'"',o.properties={endpoint:a},i}(0,0,t,p)),function(e,t){if(JSON){var n=T.fetch;if(n&&!y.useXhr)n(t,{method:N,body:JSON.stringify(e),mode:"cors"});else if(XMLHttpRequest){var a=new XMLHttpRequest;a.open(N,t),a.setRequestHeader("Content-type","application/json"),a.send(JSON.stringify(e))}}}(l,p))}function i(e,t){f||setTimeout(function(){!t&&m.core||a()},500)}var e=function(){var n=l.createElement(k);n.src=h;var e=y[w];return!e&&""!==e||"undefined"==n[w]||(n[w]=e),n.onload=i,n.onerror=a,n.onreadystatechange=function(e,t){"loaded"!==n.readyState&&"complete"!==n.readyState||i(0,t)},n}();y.ld<0?l.getElementsByTagName("head")[0].appendChild(e):setTimeout(function(){l.getElementsByTagName(k)[0].parentNode.appendChild(e)},y.ld||0)}try{m.cookie=l.cookie}catch(p){}function t(e){for(;e.length;)!function(t){m[t]=function(){var e=arguments;g||m.queue.push(function(){m[t].apply(m,e)})}}(e.pop())}var n="track",r="TrackPage",o="TrackEvent";t([n+"Event",n+"PageView",n+"Exception",n+"Trace",n+"DependencyData",n+"Metric",n+"PageViewPerformance","start"+r,"stop"+r,"start"+o,"stop"+o,"addTelemetryInitializer","setAuthenticatedUserContext","clearAuthenticatedUserContext","flush"]),m.SeverityLevel={Verbose:0,Information:1,Warning:2,Error:3,Critical:4};var s=(d.extensionConfig||{}).ApplicationInsightsAnalytics||{};if(!0!==d[I]&&!0!==s[I]){var c="onerror";t(["_"+c]);var u=T[c];T[c]=function(e,t,n,a,i){var r=u&&u(e,t,n,a,i);return!0!==r&&m["_"+c]({message:e,url:t,lineNumber:n,columnNumber:a,error:i}),r},d.autoExceptionInstrumented=!0}return m}(y.cfg);function a(){y.onInit&&y.onInit(n)}(T[t]=n).queue&&0===n.queue.length?(n.queue.push(a),n.trackPageView({})):a()}(window,document,{
    src: "https://js.monitor.azure.com/scripts/b/ai.2.min.js", // The SDK URL Source
    // name: "appInsights", // Global SDK Instance name defaults to "appInsights" when not supplied
    ld: -1, // Defines the load delay (in ms) before attempting to load the sdk. -1 = block page load and add to head. (default) = 0ms load after timeout,
    // useXhr: 1, // Use XHR instead of fetch to report failures (if available),
    crossOrigin: "anonymous", // When supplied this will add the provided value as the cross origin attribute on the script tag
    // onInit: null, // Once the application insights instance has loaded and initialized this callback function will be called with 1 argument -- the sdk instance (DO NOT ADD anything to the sdk.queue -- As they won't get called)
    cfg: { // Application Insights Configuration
        instrumentationKey: "REDACTED"
    }});
    </script>
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="AITest.Client.styles.css" rel="stylesheet" />
    <link href="_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.css" rel="stylesheet" />
</head>

<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
    <script>require.config({ paths: { 'vs': '_content/BlazorMonaco/lib/monaco-editor/min/vs' } });</script>
    <script src="_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script>
    <script src="_content/BlazorMonaco/jsInterop.js"></script>

    <script src="_framework/blazor.webassembly.js"></script>
</body>

</html>
@MSNev
Copy link
Collaborator

MSNev commented Jan 11, 2022

This should be working, once the page is loaded where is the added <script /> tag (which is added by the snippet) -- it should be the last entry in the section (which means that it will be loaded and executed) before the page load event. Not including it (ld: -1) will should also cause the same.

https://github.com/microsoft/ApplicationInsights-JS/blob/master/AISKU/snippet/snippet.js#L208-L211

However, the location of the <script /> is not a 100% association as when it's not < 0, it will still get added to the same location ( in the case above) because of this which is executed from a timeout (which causes the script to be added after page load)
doc.getElementsByTagName(scriptText)[0].parentNode.appendChild(theScript);

So some additional debugging is needed, the links above show the location on the un-minified version of the snippet, so you may need to grab a copy of that and include it in your page to determine whether it's being added directly or from the timeout.

@50Wliu
Copy link
Member Author

50Wliu commented Jan 11, 2022

once the page is loaded where is the added <script /> tag

Directly after the snippet:
SDK script location

I'll add the unminified snippet to the page and report back.

@50Wliu
Copy link
Member Author

50Wliu commented Jan 11, 2022

The debugger hits the if (snipConfig.ld < 0) case but I still see loader.js loading first.
Can we work around this by including the actual script by ourselves, rather than relying on the snippet to do so?

@MSNev
Copy link
Collaborator

MSNev commented Jan 11, 2022

loader.js might still get loaded first, but it should not get executed before the SDK (browsers only guarantee execution order based on the script inclusion, but not load order)...

Which browser are you using?
Doh! Missed this above Edge Version 98.0.1108.15 (Official build) dev (64-bit)

And yes, technically it's possible but there are a few things that the SDK will "need" to be present to initialize properly, I've not done this in a while but it should be something like.

Define this before the script
https://github.com/microsoft/ApplicationInsights-JS/blob/master/AISKU/snippet/snippet.js#L26-L32

And then "after" the script (or you can add callbacks to the queue) to call
aiSdk.trackPageView({}) as it does here
https://github.com/microsoft/ApplicationInsights-JS/blob/master/AISKU/snippet/snippet.js#L313

Most of the snippet is about

  • Create the basic "stub"
  • Add a bunch of proxy functions (which creates callbacks and add to the queue)
  • The proxy functions just allow the app to call some of the SDK functions before the SDK is fully loaded / initialized.
  • And the Queue is effectively an array of functions that the SDK will "execute" when it's loaded.
  • Managed loading the main SDK and a bunch of error reporting for when the SDK can't be loaded.

@50Wliu
Copy link
Member Author

50Wliu commented Jan 12, 2022

loader.js might still get loaded first, but it should not get executed before the SDK

Hrmm. loader.js contains its own implementation of define, and what we're seeing is that App Insights is trying to use loader's (incompatible) implementation rather than its own on startup, which to my team indicates that loader.js is initializing before AI. The one we're using is https://github.com/microsoft/vscode-loader/blob/79a17afc23c122a519312f8fb4c7e666f464288a/src/loader.js.

@50Wliu
Copy link
Member Author

50Wliu commented Jan 12, 2022

Put another way, adding a script in the body that defines define breaks App Insights:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>AITest</title>
    <base href="/" />
    <script type="text/javascript">
    !function(T,l,y){var S=T.location,k="script",D="instrumentationKey",C="ingestionendpoint",I="disableExceptionTracking",E="ai.device.",b="toLowerCase",w="crossOrigin",N="POST",e="appInsightsSDK",t=y.name||"appInsights";(y.name||T[e])&&(T[e]=t);var n=T[t]||function(d){var g=!1,f=!1,m={initialize:!0,queue:[],sv:"5",version:2,config:d};function v(e,t){var n={},a="Browser";return n[E+"id"]=a[b](),n[E+"type"]=a,n["ai.operation.name"]=S&&S.pathname||"_unknown_",n["ai.internal.sdkVersion"]="javascript:snippet_"+(m.sv||m.version),{time:function(){var e=new Date;function t(e){var t=""+e;return 1===t.length&&(t="0"+t),t}return e.getUTCFullYear()+"-"+t(1+e.getUTCMonth())+"-"+t(e.getUTCDate())+"T"+t(e.getUTCHours())+":"+t(e.getUTCMinutes())+":"+t(e.getUTCSeconds())+"."+((e.getUTCMilliseconds()/1e3).toFixed(3)+"").slice(2,5)+"Z"}(),iKey:e,name:"Microsoft.ApplicationInsights."+e.replace(/-/g,"")+"."+t,sampleRate:100,tags:n,data:{baseData:{ver:2}}}}var h=d.url||y.src;if(h){function a(e){var t,n,a,i,r,o,s,c,u,p,l;g=!0,m.queue=[],f||(f=!0,t=h,s=function(){var e={},t=d.connectionString;if(t)for(var n=t.split(";"),a=0;a<n.length;a++){var i=n[a].split("=");2===i.length&&(e[i[0][b]()]=i[1])}if(!e[C]){var r=e.endpointsuffix,o=r?e.location:null;e[C]="https://"+(o?o+".":"")+"dc."+(r||"services.visualstudio.com")}return e}(),c=s[D]||d[D]||"",u=s[C],p=u?u+"/v2/track":d.endpointUrl,(l=[]).push((n="SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details)",a=t,i=p,(o=(r=v(c,"Exception")).data).baseType="ExceptionData",o.baseData.exceptions=[{typeName:"SDKLoadFailed",message:n.replace(/\./g,"-"),hasFullStack:!1,stack:n+"\nSnippet failed to load ["+a+"] -- Telemetry is disabled\nHelp Link: https://go.microsoft.com/fwlink/?linkid=2128109\nHost: "+(S&&S.pathname||"_unknown_")+"\nEndpoint: "+i,parsedStack:[]}],r)),l.push(function(e,t,n,a){var i=v(c,"Message"),r=i.data;r.baseType="MessageData";var o=r.baseData;return o.message='AI (Internal): 99 message:"'+("SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details) ("+n+")").replace(/\"/g,"")+'"',o.properties={endpoint:a},i}(0,0,t,p)),function(e,t){if(JSON){var n=T.fetch;if(n&&!y.useXhr)n(t,{method:N,body:JSON.stringify(e),mode:"cors"});else if(XMLHttpRequest){var a=new XMLHttpRequest;a.open(N,t),a.setRequestHeader("Content-type","application/json"),a.send(JSON.stringify(e))}}}(l,p))}function i(e,t){f||setTimeout(function(){!t&&m.core||a()},500)}var e=function(){var n=l.createElement(k);n.src=h;var e=y[w];return!e&&""!==e||"undefined"==n[w]||(n[w]=e),n.onload=i,n.onerror=a,n.onreadystatechange=function(e,t){"loaded"!==n.readyState&&"complete"!==n.readyState||i(0,t)},n}();y.ld<0?l.getElementsByTagName("head")[0].appendChild(e):setTimeout(function(){l.getElementsByTagName(k)[0].parentNode.appendChild(e)},y.ld||0)}try{m.cookie=l.cookie}catch(p){}function t(e){for(;e.length;)!function(t){m[t]=function(){var e=arguments;g||m.queue.push(function(){m[t].apply(m,e)})}}(e.pop())}var n="track",r="TrackPage",o="TrackEvent";t([n+"Event",n+"PageView",n+"Exception",n+"Trace",n+"DependencyData",n+"Metric",n+"PageViewPerformance","start"+r,"stop"+r,"start"+o,"stop"+o,"addTelemetryInitializer","setAuthenticatedUserContext","clearAuthenticatedUserContext","flush"]),m.SeverityLevel={Verbose:0,Information:1,Warning:2,Error:3,Critical:4};var s=(d.extensionConfig||{}).ApplicationInsightsAnalytics||{};if(!0!==d[I]&&!0!==s[I]){var c="onerror";t(["_"+c]);var u=T[c];T[c]=function(e,t,n,a,i){var r=u&&u(e,t,n,a,i);return!0!==r&&m["_"+c]({message:e,url:t,lineNumber:n,columnNumber:a,error:i}),r},d.autoExceptionInstrumented=!0}return m}(y.cfg);function a(){y.onInit&&y.onInit(n)}(T[t]=n).queue&&0===n.queue.length?(n.queue.push(a),n.trackPageView({})):a()}(window,document,{
    src: "https://js.monitor.azure.com/scripts/b/ai.2.min.js", // The SDK URL Source
    // name: "appInsights", // Global SDK Instance name defaults to "appInsights" when not supplied
    ld: -1, // Defines the load delay (in ms) before attempting to load the sdk. -1 = block page load and add to head. (default) = 0ms load after timeout,
    // useXhr: 1, // Use XHR instead of fetch to report failures (if available),
    crossOrigin: "anonymous", // When supplied this will add the provided value as the cross origin attribute on the script tag
    // onInit: null, // Once the application insights instance has loaded and initialized this callback function will be called with 1 argument -- the sdk instance (DO NOT ADD anything to the sdk.queue -- As they won't get called)
    cfg: { // Application Insights Configuration
        instrumentationKey: "REDACTED"
    }});
    </script>
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="AITest.Client.styles.css" rel="stylesheet" />
</head>

<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <!-- THIS RIGHT HERE -->
    <script>window.define = function() {}; define.amd = 'hello';</script>

    <script src="_framework/blazor.webassembly.js"></script>
</body>

</html>

Which shouldn't happen if AI is blocking page load to initialize itself.

@MSNev
Copy link
Collaborator

MSNev commented Jan 13, 2022

Just a clarification here, it's not AI that does the "blocking" it is the behavior of the browsers based on when the scripts are loaded / executed.... But the above is unexpected.

As a workaround you can change the URL used to fetch the script to use the alternative module format which doesn't look for and wait for any loaded require / depend instances (we have not documented this on the readme yet, but they are automatically generated and deployed).

Just change the URL to
https://js.monitor.azure.com/scripts/b/ai.2.gbl.min.js

The only difference is the automatic module wrapping, the gbl versions are effectively iife modules

@MSNev
Copy link
Collaborator

MSNev commented Jan 13, 2022

I've now published a PR which documents the deployed versions, with the relevant section here

@50Wliu
Copy link
Member Author

50Wliu commented Jan 14, 2022

As a workaround you can change the URL used to fetch the script to use the alternative module format

Thank you, this worked perfectly!

Just a clarification here, it's not AI that does the "blocking" it is the behavior of the browsers

Thanks for the clarification. Should I also report this to Edge for them to investigate?

@MSNev
Copy link
Collaborator

MSNev commented Jan 14, 2022

Thanks for the clarification. Should I also report this to Edge for them to investigate?

It wouldn't hurt, but I'll also try and carve out some time to investigate further as this is unexpected. I also need to go back and re-read the spec about this as I vaguely remember something about using document.write() which we can't do anymore because we are listening to whether the script can be loaded successfully.

@MSNev
Copy link
Collaborator

MSNev commented Jan 14, 2022

OK, just did a bunch of testing (with the latest Edge) and the -1 is blocking the "onload" event but it's not changing the script execution order and even attempting to use the workaround defined here (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#compatibility_notes) to add an 'async=false' doesn't work as this seems to never get written to the DOM (when it's false).

So it looks like the only solution is to use the *.gbl* script version.

@github-actions
Copy link

This Issue will be closed in 30 days. Please remove the "Stale" label or comment to avoid closure with no action.

@github-actions github-actions bot added the stale label Nov 11, 2022
@50Wliu
Copy link
Member Author

50Wliu commented Nov 11, 2022

Not stale.

@MSNev
Copy link
Collaborator

MSNev commented Nov 11, 2022

Based on what I investigated and reported in Jan, the -1 is working as expected by blocking the "onload" to be delayed.
For the case of modern browsers still loading and executing other scripts before the SDK is loaded, I don't believe that there is any way for the SDK to cause the browser to block the execution of those other scripts.... As such I don't have any work items scheduled or otherwise to try to "fix" this....

So this means that because of the requirejs / define implementation the solution is to change the src script to the "gbl" version https://js.monitor.azure.com/scripts/b/ai.2.gbl.min.js which just executes the SDK as soon as it's loaded (ie. it doesn't have the prefix support for working with requireJs and define)

@github-actions github-actions bot removed the stale label Nov 12, 2022
@github-actions
Copy link

github-actions bot commented Sep 8, 2023

This Issue will be closed in 30 days. Please remove the "Stale" label or comment to avoid closure with no action.

@github-actions github-actions bot added the stale label Sep 8, 2023
@50Wliu
Copy link
Member Author

50Wliu commented Sep 21, 2023

Closing as I no longer work on the project that experienced this, there's a workaround, and

I don't believe that there is any way for the SDK to cause the browser to block the execution of those other scripts

@50Wliu 50Wliu closed this as not planned Won't fix, can't repro, duplicate, stale Sep 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants