UNPKG

phantomas

Version:

Headless Chromium-based web performance metrics collector and monitoring tool

255 lines (217 loc) 6.33 kB
/** * Log requests for build HAR output * * Depends on windowPerformance module! */ "use strict"; var fs = require("fs"); /** * Inspired by phantomHAR * @author: Christopher Van (@cvan) * @homepage: https://github.com/cvan/phantomHAR * @original: https://github.com/cvan/phantomHAR/blob/master/phantomhar.js */ function createHAR(page, creator) { // @see: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html var address = page.address; var title = page.title; var startTime = page.startTime; var resources = page.resources; var entries = []; resources.forEach(function (resource) { var request = resource.request; var response = resource.response; if (!request || !response) { return; } // Exclude data URIs from the HAR because they aren't // included in the spec. if (request.url.substring(0, 5).toLowerCase() === "data:") { return; } entries.push({ cache: {}, pageref: address, request: { // Accurate bodySize blocked on https://github.com/ariya/phantomjs/pull/11484 bodySize: -1, cookies: [], headers: request.headers, // Accurate headersSize blocked on https://github.com/ariya/phantomjs/pull/11484 headersSize: -1, httpVersion: "HTTP/1.1", method: request.method, queryString: [], url: request.url, }, response: { bodySize: response.bodySize, cookies: [], headers: response.headers, headersSize: response.headersSize, httpVersion: "HTTP/1.1", redirectURL: "", status: response.status, statusText: response.statusText, content: { mimeType: response.contentType || "", size: response.bodySize, // uncompressed text: "", }, }, startedDateTime: resource.startTime && resource.startTime.toISOString(), time: response.timeToLastByte, timings: { blocked: 0, dns: -1, connect: -1, send: 0, wait: 0, // response.timeToFirstByte || 0, receive: 0, // response.receiveTime, ssl: -1, }, }); }); return { log: { creator: creator, entries: entries, pages: [ { startedDateTime: startTime.toISOString(), id: address, title: title, pageTimings: { onLoad: page.onLoad || -1, onContentLoad: page.onContentLoad || -1, }, }, ], version: "1.2", }, }; } /** End **/ module.exports = function (phantomas) { var param = phantomas.getParam("har"), path = "", timeToLastByte; if (param === false) { return; } var page = { origin: undefined, resources: [], title: undefined, address: undefined, startTime: undefined, endTime: undefined, onDOMReadyTime: undefined, windowOnLoadTime: undefined, timeToLastByte: undefined, onLoad: undefined, onContentLoad: undefined, }; var creator = { name: "Phantomas - (using phantomHAR)", version: phantomas.getVersion(), }; if (typeof param === "undefined") { phantomas.log("HAR: no path specified, use --har <path>"); return; } // --har if (param === true) { // defaults to "phantomas_2013-12-07T20:15:01.521Z.har" path = "phantomas_" + new Date().toJSON() + ".har"; } // --har [file name] else { path = param; } phantomas.log("Will be stored in %s", path); phantomas.on("pageBeforeOpen", function (p) { page.origin = p; }); phantomas.on("loadFinished", function () { page.endTime = new Date(); }); phantomas.on("send", (entry) => { const resId = entry._requestId; page.resources[resId] = { id: resId, request: entry, response: null, startTime: new Date(), }; // a first request has been made? if (typeof page.startTime === "undefined") { page.startTime = new Date(); } }); phantomas.on("recv", (entry) => { const resId = entry.id; page.resources[resId].response = entry; timeToLastByte = entry.timeToLastByte; }); phantomas.on("metric", function (name, value) { switch (name) { case "domContentLoaded": page.onDOMReadyTime = value; break; case "domComplete": page.windowOnLoadTime = value; break; case "timeToLastByte": page.timeToLastByte = value; break; } }); phantomas.on("report", () => { // make resources list a real array page.resources = Object.values(page.resources); // Set endTime if page was not finished correctly if (!page.endTime) page.endTime = new Date(); // If metric 'windowOnLoadTime' hasn't been fired, compute it //if (!page.windowOnLoadTime) // page.windowOnLoadTime = page.endTime.getTime() - page.startTime.getTime(); // If metric 'timeToLastByte' hasn't been fired, use last entry if (!page.timeToLastByte) page.timeToLastByte = timeToLastByte; //page.address = page.origin.url; //page.title = page.origin.title; // Times (windowOnLoadTime, onDOMReadyTime) are relative to responseEnd entry // in NavigationTiming (represented by timeToLastByte metric) page.onLoad = page.timeToLastByte + page.windowOnLoadTime; page.onContentLoad = page.timeToLastByte + page.onDOMReadyTime; phantomas.log('Generating for <%s> ("%s")', page.address, page.title); phantomas.log("Page data: %j", page); var har, dump; try { har = createHAR(page, creator); } catch (e) { console.error(e); phantomas.log("Failed to build - %s", e); return; } phantomas.log("Result: %j", har); try { dump = JSON.stringify(har); } catch (e) { console.error(e); phantomas.log("Failed to stringify HAR to JSON - %s!", e); return; } phantomas.log("Saving to %s ...", path); try { // https://nodejs.org/api/fs.html#fs_fs_writefilesync_file_data_options fs.writeFileSync(path, dump); } catch (e) { console.error(e); phantomas.log("Failed to save HAR - %s!", e); return; } // let clients know that we save a HAR file phantomas.emit("har", path); phantomas.log("Done"); }); };