Browser Logs


When you are investigating bugs in a website, it would be useful to know if the browser logged any javascript errors while the test was running. This is fairly straightforward for Chrome and Firefox. You can also request the full performance log showing the status and timing of every request. Be warned, though, the performance log can become enormous for all but the most trivial tests. Firefox is also quite chatty in its standard browser log, so I will only touch on using Chrome logs here. I will show how to parse the performance log for network request timings, but quite frankly, that's a job best left to webpagetest.

Each log has an associated level (of detail)

Enabling logging

You enable the browser's logs when you instantiate the browser:


    var chromeOptions = new wd.ChromeOptions();
    chromeOptions.addArguments("test-type");//to stop warning banner in Chrome 35+
    caps = new wd.DesiredCapabilities["chrome"]();
    caps.setCapability("chromeOptions", chromeOptions);

    //enable logging in Chrome
    var logPrefs = new wd.LoggingPreferences();
    //only log javascript errors
    logPrefs.enable("browser", wd.Level.SEVERE);
    //log all performance - warning (again) this can be a LOT
    logPrefs.enable("performance", wd.Level.INFO);
    caps.setCapability("loggingPrefs", logPrefs);
    driver = new wd["ChromeDriver"](caps);
                

Parsing the browser log

To get the browser log:


    var logErrors = driver.manage().logs().get("browser").getAll();
    logErrors.forEach(function (logError) {
        console.log(new Date(+logError.timestamp.longValue) + ": " + logError.message);
    });
                

You get an array of objects with timestamp and message properties (corresponding to what you would see if you had Console open in Chrome Developer Tools).

Parsing the performance log

I'm just going to say one last time that this can produce a LOT of data.

Not surprisingly, you request the log like this:


    var perfLog = driver.manage().logs().get("performance").getAll();
                

So what does the raw performance log look like?


    [{
        "_instance": {},
        "level": "INFO",
        "message": "{\"message\":{\"method\":\"Timeline.started\",\"params\":{\"consoleTimeline\":false}},\"webview\":\"3139A36F-B1F7-6B65-DAFB-C372A1636555\"}",
        "timestamp": 1414776397208
    }, {
        "_instance": {},
        "level": "INFO",
        "message": "{\"message\":{\"method\":\"Timeline.eventRecorded\",\"params\":{\"record\":{\"children\":[],\"data\":{},\"endTime\":66257151.351,\"startTime\":66257151.204,\"type\":\"Program\"}}},\"webview\":\"3139A36F-B1F7-6B65-DAFB-C372A1636555\"}",
        "timestamp": 1414776397244
    }, {
        "_instance": {},
        "level": "INFO",
        "message": "{\"message\":{\"method\":\"Page.frameAttached\",\"params\":{\"frameId\":\"9.2\",\"parentFrameId\":\"9.1\"}},\"webview\":\"3139A36F-B1F7-6B65-DAFB-C372A1636555\"}",
        "timestamp": 1414776397336
    }, {
        "_instance": {},
        "level": "INFO",
        "message": "{\"message\":{\"method\":\"Page.frameStartedLoading\",\"params\":{\"frameId\":\"9.2\"}},\"webview\":\"3139A36F-B1F7-6B65-DAFB-C372A1636555\"}",
        "timestamp": 1414776397336
    }, {
        "_instance": {},
        "level": "INFO",
        "message": "{\"message\":{\"method\":\"Page.frameNavigated\",\"params\":{\"frame\":{\"id\":\"9.2\",\"loaderId\":\"9.3\",\"mimeType\":\"text/html\",\"name\":\"\",\"parentId\":\"9.1\",\"securityOrigin\":\"://\",\"url\":\"about:blank\"}}},\"webview\":\"3139A36F-B1F7-6B65-DAFB-C372A1636555\"}",
        "timestamp": 1414776397336
    }, {
        "_instance": {},
        "level": "INFO",
        "message": "{\"message\":{\"method\":\"Page.frameStoppedLoading\",\"params\":{\"frameId\":\"9.2\"}},\"webview\":\"3139A36F-B1F7-6B65-DAFB-C372A1636555\"}",
        "timestamp": 1414776397336
    }...]
                

Say what?

Yes, it's quite low level (possibly the same information as the timeline in Chrome Developer Tools), but it is possible to glean details of network request timings with some effort.

I'm just going to leave this here for the sake of interest, but it is more sensible to use webpagetest to do this. I make no apologies for the terseness of the code as you would be mad to do this anyway.


            var logs = driver.manage().logs().get("performance").getAll();
            var requests = {};
            var firstResponder = 0;
            logs.forEach(function (plog) {
                var msg = JSON.parse(plog.message).message;
                var duration = 0;
                //network request starts
                if (msg.method == "Network.requestWillBeSent") {
                    if (msg.params.timestamp > timeMark) {
                        requests[msg.params.requestId] = requests[msg.params.requestId] || {
                            "url": msg.params.request.url,
                            "startTime": msg.params.timestamp,
                            "offsetTime": 0,
                            "ttfb": 0,
                            "ttlb": 0,
                            "transferTime": 0,
                            "redirected": false,
                            "failed": false,
                            "fromCache": false,
                            "bytes": 0
                        };
                        requests[msg.params.requestId]["redirected"] = 
                            (msg.params["redirectResponse"] !== undefined);
                        //base all offsetTimes on the first request
                        if (firstResponder) {
                            requests[msg.params.requestId]["offsetTime"] = 
                                requests[msg.params.requestId]["startTime"] - firstResponder;
                        } else {
                            firstResponder = requests[msg.params.requestId]["startTime"];
                        }
                    }
                }
                if (requests[msg.params.requestId]) {
                    //ttfb could come from dataReceived or responseReceived
                    if ((msg.method == "Network.dataReceived") 
                        && (!requests[msg.params.requestId]["ttfb"])) {
                        requests[msg.params.requestId]["ttfb"] = msg.params.timestamp - 
                            requests[msg.params.requestId]["startTime"];
                    }
                    if ((msg.method == "Network.responseReceived") 
                        && !requests[msg.params.requestId]["ttfb"]
                        && (msg.params["redirectResponse"] === undefined)) {
                        //could be the first or last response for this request, 
                        //so calculate ttfb, ttlb and transferTime
                        duration = msg.params.timestamp - 
                            requests[msg.params.requestId]["startTime"];
                        requests[msg.params.requestId]["ttfb"] = duration;
                        if (duration > requests[msg.params.requestId]["ttlb"]) {
                            requests[msg.params.requestId]["ttlb"] = duration;
                            requests[msg.params.requestId]["transferTime"] = 
                                requests[msg.params.requestId]["ttlb"] - 
                                requests[msg.params.requestId]["ttfb"];
                        }
                    }
                    //response could also finish here
                    if (msg.method == "Network.loadingFinished") {
                        duration = msg.params.timestamp - 
                            requests[msg.params.requestId]["startTime"];
                        if (duration > requests[msg.params.requestId]["ttlb"]) {
                            requests[msg.params.requestId]["ttlb"] = duration;
                            requests[msg.params.requestId]["transferTime"] = 
                                requests[msg.params.requestId]["ttlb"] - 
                                requests[msg.params.requestId]["ttfb"];
                        }
                        requests[msg.params.requestId]["bytes"] += 
                            +msg.params["encodedDataLength"];
                    }
                    //mark cached responses
                    if ((msg.method == "Network.responseReceived") 
                        && msg.params.response["fromDiskCache"]) {
                        requests[msg.params.requestId]["fromCache"] = true; 
                    }
                    if (msg.method == "Network.requestServedFromCache") {
                        requests[msg.params.requestId]["fromCache"] = true;
                    }
                    //mark failed responses
                    if (msg.method == "Network.loadingFailed") {
                        requests[msg.params.requestId]["failed"] = true;
                    }   
                }
            });
            fs.writeFileSync(testing.TEST_HOME + "perflog.json", JSON.stringify(requests));

            return requests;
        }
                

This translates the (numbered) network requests into something more readable with performance stats:


{
    "26.1": {
        "url": "http://foobar.com/auth/login?targetUri=%2Fexplore%2Findex",
        "startTime": 1414776397.43785,
        "offsetTime": 0,
        "ttfb": 0.5678300857543945,
        "ttlb": 0.5685598850250244,
        "transferTime": 0.0007297992706298828,
        "redirected": false,
        "failed": false,
        "fromCache": false,
        "bytes": 5210
    },
    "26.2": {
        "url": "http://foobar.com/0dc1d7a064e0/bundle-app-ie9_head.js",
        "startTime": 1414776398.06587,
        "offsetTime": 0.6280200481414795,
        "ttfb": 0.03729987144470215,
        "ttlb": 0.03937983512878418,
        "transferTime": 0.0020799636840820312,
        "redirected": false,
        "failed": false,
        "fromCache": false,
        "bytes": 379
    },
    "26.3": {
        "url": "http://foobar.com/0dc1d7a064e0/bundle-app-jquery_head.js",
        "startTime": 1414776398.06744,
        "offsetTime": 0.6295900344848633,
        "ttfb": 0.09619998931884766,
        "ttlb": 0.15544986724853516,
        "transferTime": 0.0592498779296875,
        "redirected": false,
        "failed": false,
        "fromCache": false,
        "bytes": 33128
    },
    "26.4": {
        "url": "http://foobar.com/0dc1d7a064e0/bundle-style_head.css",
        "startTime": 1414776398.06819,
        "offsetTime": 0.6303400993347168,
        "ttfb": 0.07647991180419922,
        "ttlb": 0.12150979042053223,
        "transferTime": 0.04502987861633301,
        "redirected": false,
        "failed": false,
        "fromCache": false,
        "bytes": 29134
    },
    //etc.
}
                
Next: Doing this properly with Webpagetest