See the sections below to learn how to troubleshoot issues with monitoring SPAs.

Problems With Dynamically Loading the JavaScript Agent 

Because Angular allows you to dynamically load modules with AppdInitServlet, you may be tempted to dynamically load adrum.js. The problem with dynamically loading adrum.js, however, is that this could cause a race condition between the time adrum.js is evaluated and the time angular.js is bootstrapped. This race condition can result in JavaScript Agent missing virtual pages or XHR events.

You can avoid this race condition by doing the following:

Load Angular and the JavaScript Agent

The key here is simply to make sure that you load angular.js first and then immediately load adrum.js as shown below.

<script src="angular.js"></script>
<script src="adrum.js"></script>

Bootstrap Angular

Manual (Recommended)

After you have loaded adrum.js, you can then bootstrap Angular through manual initialization. You should load both angular.js and adrum.js as soon as possible, so that the JavaScript Agent can get more accurate metrics about the page load and the HTML DOM Ready /Build time. The examples below follow the recommended procedure of first loading angular.js, then adrum.js, and then bootstrapping angular.js

AppDynamics CDN

<!doctype html>
<html>
    <head>
        <script src="http://code.angularjs.org/snapshot/angular.js"></script> 
        <script charset='UTF-8'>
            window['adrum-start-time'] = new Date().getTime();
            (function(config){
                config.appKey = '<EUM_APP_KEY>';
                config.adrumExtUrlHttp = 'http://cdn.appdynamics.com';
                config.adrumExtUrlHttps = 'https://cdn.appdynamics.com';
                config.beaconUrlHttp = 'http://col.eum-appdynamics.com ';
                config.beaconUrlHttps = 'https://col.eum-appdynamics.com';
                config.xd = {enable : false};
            })(window['adrum-config'] || (window['adrum-config'] = {}));
            
        </script>
        <script src='//cdn.appdynamics.com/adrum/adrum-latest.js' type='text/javascript' charset='UTF-8'></script>
    </head>
    <body>
        <div ng-controller="TestController">
        It's {{currentTime}}!
        </div>
        <script>
            angular.module('angularApp', [])
            .controller('TestController', ['$scope', function ($scope) {
                $scope.currentTime = new Date().toLocaleString();
            }]);
            angular.element(document).ready(function() {
                angular.bootstrap(document, ['angularApp']);
            });
        </script>
    </body>
</html>

Self-Hosting

<!doctype html>
<html>
    <head>
        <script src="http://code.angularjs.org/snapshot/angular.js"></script> 
        <script charset='UTF-8'>
            window['adrum-start-time'] = new Date().getTime();
            (function(config){
                config.appKey = '<EUM_APP_KEY>';
                config.adrumExtUrlHttp = 'http://<your-cdn.com>';
                config.adrumExtUrlHttps = 'https://<your-cdn.com>';
                config.beaconUrlHttp = 'http://col.eum-appdynamics.com';
                config.beaconUrlHttps = 'https://col.eum-appdynamics.com';
                config.xd = {enable : false};
            })(window['adrum-config'] || (window['adrum-config'] = {}));
        </script>
        <script src='//<your-cdn.com>/adrum/adrum.js' type='text/javascript' charset='UTF-8'></script>
    </head>
    <body>
        <div ng-controller="TestController">
        It's {{currentTime}}!
        </div>
        <script>
            angular.module('angularApp', [])
            .controller('TestController', ['$scope', function ($scope) {
                $scope.currentTime = new Date().toLocaleString();
            }]);
            angular.element(document).ready(function() {
                angular.bootstrap(document, ['angularApp']);
            });
        </script>
    </body>
</html>

Shared Hosting

<!doctype html>
<html>
    <head>
        <script src="http://code.angularjs.org/snapshot/angular.js"></script> 
        <script>
            // Assumes that you downloaded the JS Agent and copied it to the 'js' directory of your app root.
            (function(config){
                config.appKey = '<EUM_APP_KEY>';
                config.adrumExtUrlHttp = 'http://cdn.appdynamics.com';
                config.adrumExtUrlHttps = 'https://cdn.appdynamics.com';
                config.beaconUrlHttp = 'http://col.eum-appdynamics.com';
                config.beaconUrlHttps = 'https://col.eum-appdynamics.com';
                config.xd = {enable : false};
           })(window['adrum-config'] || (window['adrum-config'] = {}));
        </script>
        <script src='//<your-cdn.com>/adrum/adrum.js' type='text/javascript' charset='UTF-8'/>
    </head>
   <body>
       <div ng-controller="TestController">
       It's {{currentTime}}!
       </div>
       <script>
           angular.module('angularApp', [])
           .controller('TestController', ['$scope', function ($scope) {
               $scope.currentTime = new Date().toLocaleString();
           }]);
           angular.element(document).ready(function() {
               angular.bootstrap(document, ['angularApp']);
           });
       </script>
       <script src="js/adrum.js"></script>
    </body>
</html>

Deferred (Optional)

If manual bootstrap is not possible, you can use deferred bootstrap. What this means is that you pause the bootstrap until adrum.js is successfully loaded, at which point, you resume the bootstrapping with angular.resumeBootstrap() as shown in the example below.

<!doctype html>
<html>
    <head>
        <script src="http://code.angularjs.org/snapshot/angular.js"></script> 
        <script>
            // This defers the bootstrap.
            window.name = 'NG_DEFER_BOOTSTRAP!';
            angular.module('angularApp', [])
            .controller('TestController', ['$scope', function ($scope) {
                $scope.currentTime = new Date().toLocaleString();
            }]);
            angular.element(document).ready(function() {
                angular.bootstrap(document, ['angularApp']);
            });
        </script>
        // For simplicity, we're just showing the injection snippet using the AppDynamics CDN.
        <script charset='UTF-8'>
            window['adrum-start-time'] = new Date().getTime();
            (function(config){
                config.appKey = '<EUM_APP_KEY>';
                config.adrumExtUrlHttp = 'http://cdn.appdynamics.com';
                config.adrumExtUrlHttps = 'https://cdn.appdynamics.com';
                config.beaconUrlHttp = 'http://col.eum-appdynamics.com';
                config.beaconUrlHttps = 'https://col.eum-appdynamics.com';
                config.xd = {enable : false};
            })(window['adrum-config'] || (window['adrum-config'] = {}));
        </script>
        <script src='//cdn.appdynamics.com/adrum/adrum-latest.js' type='text/javascript' charset='UTF-8'></script>
  </head>
<body>
  <div ng-controller="TestController">
    It's {{currentTime}}!
  </div>
  <script>
    // Resume the bootstrapping.
    angular.resumeBootstrap()
  </script>
</body>
</html>

Manually Initializing the JavaScript Agent (Optional)

If adrum.js loads successfully, but does not initialize, you can manually initialize it by calling ADRUM.ng.ngMonitor.init() as shown below. This will force the JavaScript Agent to initialize even after the Angular bootstrap.

 <html>
     <head>
         <script src="http://code.angularjs.org/snapshot/angular.js"></script> 
         <script>
             // This defers the bootstrap.
             window.name = 'NG_DEFER_BOOTSTRAP!';
             angular.module('angularApp', [])
             .controller('TestController', ['$scope', function ($scope) {
                 $scope.currentTime = new Date().toLocaleString();
             }]);
             angular.element(document).ready(function() {
                 angular.bootstrap(document, ['angularApp']);
             });
         </script>
<!doctype html>
<html>
    <head>
        <script src="http://code.angularjs.org/snapshot/angular.js"></script> 
        <script>
            // This defers the bootstrap.
            window.name = 'NG_DEFER_BOOTSTRAP!';
            angular.module('angularApp', [])
            .controller('TestController', ['$scope', function ($scope) {
                $scope.currentTime = new Date().toLocaleString();
            }]);
            angular.element(document).ready(function() {
                angular.bootstrap(document, ['angularApp']);
            });
        </script>
        // For simplicity, we're just showing the injection snippet using the AppDynamics CDN.
        <script charset='UTF-8'>
            window['adrum-start-time'] = new Date().getTime();
            (function(config){
                config.appKey = '<EUM_APP_KEY>';
                config.adrumExtUrlHttp = 'http://cdn.appdynamics.com';
                config.adrumExtUrlHttps = 'https://cdn.appdynamics.com';
                config.beaconUrlHttp = 'http://col.eum-appdynamics.com';
                config.beaconUrlHttps = 'https://col.eum-appdynamics.com ';
                config.xd = {enable : false};
            })(window['adrum-config'] || (window['adrum-config'] = {}));
        </script>
        <script src='//cdn.appdynamics.com/adrum/adrum-latest.js' type='text/javascript' charset='UTF-8'></script>
    </head>
    <body>
        <div ng-controller="TestController">
        It's {{currentTime}}!
        </div>
        <script>
            // Resume the bootstrapping.
            angular.resumeBootstrap();
            ADRUM.ng.ngMonitor.init();
        </script>
    </body>
</html>

In some cases, you may be able to place ADRUM.ng.ngMonitor.init() before angular.resumeBootstrap().

  • No labels