stagemonitor / stagemonitor-mailinglist

GitHub issues abused as a mailing list
3 stars 0 forks source link

Questions about Angular SPA application monitoring #65

Open gmtjlmartin opened 6 years ago

gmtjlmartin commented 6 years ago

I am very Interested in arming my team of Angular app developers with the ability to get instant feedback on key performance metrics during development. Stagemonitor appears to be one of the few APM tools that appears to target this exact use case. I am struggling with several issues here and I was hoping to get some clarification on capabilities and configuration matters.

Lets start with some basic configuration information on my project:

Our developers use Browsersync and deploy the REST service web app based on SPRING WEB-MVC on a local tomcat during development.

It was trivial to get stagemonitor version 0.87.2 wired into the REST service tier; however, getting the front end wired up is proving to be very tricky.

Question #1 I have tried to follow the pattern that was provided in: How to use stagemonitor on an AngularJS app with a Spring-Boot server ? #197 to no avail.

I have a single index.html web page that is loaded at application start up and I added the following to the end of the body section of that page. The stage monitor icon appears but it does nothing. I kinda suspected that this is not going to work because there is no: stagemonitor/static/rum/boomerang-56c823668fc.min.js in the version of stagemonitor (0-87.2) I am using. Has this dependency on boomerang been replaced with something else?


<script src="http://localhost:8080/epc-services/stagemonitor/static/rum/boomerang-56c823668fc.min.js"></script>
<script>
   BOOMR.init({
      log: null
   });
   BOOMR.addVar("requestId", "null");
   BOOMR.addVar("requestName", "testName");
   BOOMR.addVar("serverTime", 0);
</script>
<div id="stagemonitor-overlay">
    <iframe id="stagemonitor-modal"></iframe>
</div>

<script type="text/javascript">
    (function () {
        if (typeof(BOOMR) != 'undefined') {
            BOOMR.subscribe('before_beacon', function(data) {
                if (data.totalPageLoadTime) {
                    var stagemonitorWindow = document.getElementById("stagemonitor-modal").contentWindow;
                    if (stagemonitorWindow.stagemonitor) {
                        stagemonitorWindow.stagemonitor.renderPageLoadTime(data);
                    } else {
                        stagemonitorWindow.pageLoadTime = data;
                    }
                    var thresholdExceeded = stagemonitorWindow.stagemonitor.thresholdExceeded;
                    var clazz = thresholdExceeded ? "stagemonitor-threshold-exceeded" : "stagemonitor-threshold-ok";
                    document.getElementById("stagemonitor-overlay-show").className = clazz;
                }
            });
        }

        var stagemonitorWindow = document.getElementById("stagemonitor-modal").contentWindow;
        var contextPath = "";

        var xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function () {
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                var doc = stagemonitorWindow.document;
                doc.open();
                doc.write(xmlhttp.responseText
                        .split("href=\"").join("href=\"" + contextPath)
                        .split("src=\"").join("src=\"" + contextPath));
                doc.close();
            }
        };
        xmlhttp.open("GET", "http://localhost:8080/epc-services/stagemonitor/static/stagemonitor-modal.html", true);
        xmlhttp.send();

        window.StagemonitorLoaded = function () {

            function isDomLoaded() {
                return document.readyState === "complete";
            }
            if (isDomLoaded()) {
                init();
            } else {
                document.onreadystatechange = function () {
                    if (isDomLoaded()) {
                        init();
                    }
                };
            }
            function init() {
                var stagemonitorOverlayShow = document.getElementById("stagemonitor-overlay-show"),
                    stagemonitorOverlay = document.getElementById("stagemonitor-overlay"),
                    data = ,
                    configurationSources = {},
                    configurationOptions = {};

                stagemonitorWindow.stagemonitor.measurementSession = {"id":"db6b1c29-c2d5-4195-b1c1-b396ecc737de","applicationName":"testApplication","hostName":null,"instanceName":"testInstance","endTimestamp":null,"end":null,"start":"2016-09-12T12:26:51.697+0200","@timestamp":1473676011697};
                stagemonitorWindow.stagemonitor.pathsOfTabPlugins = [];
                var connectionId = generateUUID();
                addXMLRequestCallback( function( xhr ) {
                    xhr.setRequestHeader("x-stagemonitor-connection-id", connectionId);
                });

                stagemonitorWindow.stagemonitor.openOverlay = function () {
                    stagemonitorOverlay.style.display = "block";
                    stagemonitorWindow.stagemonitor.onOpen();
                    stagemonitorWindow.focus();
                };

                stagemonitorWindow.stagemonitor.closeOverlay = function () {
                    stagemonitorOverlay.style.display = "none";
                };

                stagemonitorWindow.stagemonitor.initialize(data, configurationSources, configurationOptions,
                        window.location.origin + "", contextPath,
                        false, connectionId, [], false);
                var thresholdExceeded = stagemonitorWindow.stagemonitor.thresholdExceeded;
                stagemonitorOverlayShow.className = thresholdExceeded ? "stagemonitor-threshold-exceeded" : "stagemonitor-threshold-ok";
                stagemonitorOverlayShow.addEventListener("click", stagemonitorWindow.stagemonitor.openOverlay);
            };

        };

        function generateUUID(){
            var d = new Date().getTime();
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                var r = (d + Math.random() * 16) % 16 | 0;
                d = Math.floor(d / 16);
                return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16);
            });
        }

        function addXMLRequestCallback(callback){
            var oldSend, i;
            if( XMLHttpRequest.callbacks ) {
                // we've already overridden send() so just add the callback
                XMLHttpRequest.callbacks.push( callback );
            } else {
                // create a callback queue
                XMLHttpRequest.callbacks = [callback];
                // store the native send()
                oldSend = XMLHttpRequest.prototype.send;
                // override the native send()
                XMLHttpRequest.prototype.send = function(){
                    // process the callback queue
                    // the xhr instance is passed into each callback but seems pretty useless
                    // you can't tell what its destination is or call abort() without an error
                    // so only really good for logging that a request has happened
                    // I could be wrong, I hope so...
                    // EDIT: I suppose you could override the onreadystatechange handler though
                    for( i = 0; i < XMLHttpRequest.callbacks.length; i++ ) {
                        XMLHttpRequest.callbacks[i]( this );
                    }
                    // call the native send()
                    oldSend.apply(this, arguments);
                }
            }
        }

    }());
</script>
<style>
    #stagemonitor-overlay {
        display: none;
        position: fixed;
        background-color: rgba(0, 0, 0, 0.5);
        width: 100%;
        height: 100%;
        z-index: 100000;
        top: 0;
        left: 0;
    }

    #stagemonitor-overlay-show {
        position: fixed;
        right: 3em;
        bottom: 3em;
        border-radius: 1em;
        padding: 0.5em;
        cursor: pointer;
    }

    #stagemonitor-overlay-show:hover {
        background-color: rgba(0, 0, 0, 0.6);
    }

    .stagemonitor-threshold-ok {
        background-color: rgba(0, 0, 0, 0.5);
    }

    .stagemonitor-threshold-exceeded {
        background-color: #bf3636;
        -webkit-animation: togglebackground 4s infinite;
        animation: togglebackground 4s infinite;
    }

    @-webkit-keyframes togglebackground {
        0%   {background-color: rgba(0, 0, 0, 0.5);}
        30%  {background-color: #bf3636}
        60%  {background-color: #bf3636}
        100% {background-color: rgba(0, 0, 0, 0.5);}
    }

    @keyframes togglebackground {
        0%   {background-color: rgba(0, 0, 0, 0.5);}
        30%  {background-color: #bf3636}
        60%  {background-color: #bf3636}
        100% {background-color: rgba(0, 0, 0, 0.5);}
    }

    #stagemonitor-modal {
        height: 100%;
        width: 100%;
    }
</style>

<div id="stagemonitor-overlay-show" class="stagemonitor-threshold-ok">
    <img src="http://localhost:8080/epc-services/stagemonitor/static/stagemonitor.png" />
</div>

What you should note is that my Browsersync is running on localhost port 5555 and I am linking up to the REST service deployment of stagemonitor on loalhost port 8080 to get at the stagemonitor artifacts that are needed to drive the front end stagemonitor functionality.

Can anyone provide some insight as to what I need to do to get the web page side of this working?

felixbarny commented 6 years ago

Hi,

this snipped is outdated. I've just committed a gradle task which you can use to export the skeleton. Clone the repo and execute ./gradlew exportWidget and the widget will be exported to stagemonitor-web-servlet/build/stagemonitor-widget.html.

gmtjlmartin commented 6 years ago

Thanks for the quick turnaround and guidance. I hope this works out as I hope it will.

gmtjlmartin commented 6 years ago

So to be clear here is my situation. We have an Angular SPA and back end services crafted using Spring Web MVC ( we simply don't have the V part as we ship mostly JSON data and images back to the SPA client). Our developers all work on their own boxes and the SPA is served from browsersync server on localhost:5555. The service is deployed to tomcat on their box at localhost:8080. This setup was initially a challenge because of CORS and getting this to work was somewhat tricky so I am used to that aspect.

So while I can inject the widget into my SPA index.html, I have had no real success getting it to work properly which has been a bummer today.

Here is a recap of what I did: -generated the widget -added the code into the SPA index.html -tweaked the following: copied eum.min.js to the SPA deployment used by Browsersync pointed ISOGRAM thingy to that location verified that the script loaded through browser side debugger -copied the stagemonitor static assets to a place reachable by Browsersync

At this point I now have the widget and the modal window functioning but with no content for call traces, requests, metrics etc... The browser debugger keeps looking to hit up Browsersync port for metrics when I know that it needs to hit the tomcat port of localhost to get to metrics.

I can also hit stagemonitor through the tomcat server (rest services, sitemonitor libs, bytebuddy agent) and see metrics being recorded as I excersise my app. That is not really all that interesting for me. I need this front end functionality to help guide developers to the places where things are iffy in the code.

Am I off my rocker here trying to use the tool in this manner? There has got to be a way to make this a configurable option. If I can get my SPA js to work with my tomcat backend then there is no reason I can think of that this could not work for stagemonitor as well.

Any ideas that you could float my way would be appreciated. I am going to start looking through the code to see if I can figure out a way to make this work.