From 6d8eaf9c64268da52b4f0c3665ad91ab8772be46 Mon Sep 17 00:00:00 2001 From: divyakarippath Date: Tue, 16 Jun 2020 10:11:31 -0700 Subject: [PATCH] add web SSR timing reporting events (#1672) --- packages/subapp-server/lib/fastify-plugin.js | 6 ++++ .../subapp-server/lib/setup-hapi-routes.js | 12 +++++++ packages/subapp-server/lib/utils.js | 3 +- packages/subapp-web/lib/init.js | 7 ++++ packages/subapp-web/lib/load.js | 3 ++ packages/subapp-web/lib/start.js | 35 ++++++++++++++++++- packages/subapp-web/lib/util.js | 19 ++++++++++ 7 files changed, 83 insertions(+), 2 deletions(-) diff --git a/packages/subapp-server/lib/fastify-plugin.js b/packages/subapp-server/lib/fastify-plugin.js index b32de1519..81826d507 100644 --- a/packages/subapp-server/lib/fastify-plugin.js +++ b/packages/subapp-server/lib/fastify-plugin.js @@ -22,6 +22,12 @@ module.exports = { const { routes, topOpts } = searchRoutesFromFile(srcDir, pluginOpts); + const reporting = _.get(topOpts, "reporting", {}); + if (!reporting.enable || !reporting.reporter) { + // eslint-disable-next-line + console.warn(`Warning: Metric reporting for ssr not enabled or no reporter specified.`); + } + const subApps = await subAppUtil.scanSubAppsFromDir(srcDir); const subAppsByPath = subAppUtil.getSubAppByPathMap(subApps); diff --git a/packages/subapp-server/lib/setup-hapi-routes.js b/packages/subapp-server/lib/setup-hapi-routes.js index 54deb0070..3916eb3af 100644 --- a/packages/subapp-server/lib/setup-hapi-routes.js +++ b/packages/subapp-server/lib/setup-hapi-routes.js @@ -246,6 +246,12 @@ function searchRoutesFromFile(srcDir, pluginOpts) { async function setupRoutesFromFile(srcDir, server, pluginOpts) { const { routes, topOpts } = searchRoutesFromFile(srcDir, pluginOpts); + const reporting = _.get(topOpts, "reporting", {}); + if (!reporting.enable || !reporting.reporter) { + // eslint-disable-next-line + console.warn(`Warning: Metric reporting for ssr not enabled or no reporter specified.`); + } + await handleFavIcon(server, topOpts); // invoke setup callback @@ -268,6 +274,12 @@ async function setupRoutesFromDir(server, pluginOpts, fromDir) { const topOpts = _.merge(getDefaultRouteOptions(), fromDir.options, pluginOpts); + const reporting = _.get(topOpts, "reporting", {}); + if (!reporting.enable || !reporting.reporter) { + // eslint-disable-next-line + console.warn(`Warning: Metric reporting for ssr not enabled or no reporter specified.`); + } + topOpts.routes = _.merge({}, routes, topOpts.routes); updateFullTemplate(fromDir.dir, topOpts); diff --git a/packages/subapp-server/lib/utils.js b/packages/subapp-server/lib/utils.js index d6b8a339e..ed9f274f7 100644 --- a/packages/subapp-server/lib/utils.js +++ b/packages/subapp-server/lib/utils.js @@ -83,7 +83,8 @@ function getDefaultRouteOptions() { devBundleBase: "/js", cspNonceValue: undefined, templateFile: Path.join(__dirname, "..", "resources", "index-page"), - cdn: {} + cdn: {}, + reporting: { enable: false } }; } diff --git a/packages/subapp-web/lib/init.js b/packages/subapp-web/lib/init.js index 3da6105f5..9d60ab2e8 100644 --- a/packages/subapp-web/lib/init.js +++ b/packages/subapp-web/lib/init.js @@ -6,6 +6,7 @@ const Fs = require("fs"); const Path = require("path"); const util = require("./util"); const subappUtil = require("subapp-util"); +const _ = require("lodash"); module.exports = function setup(setupContext) { const distDir = process.env.NODE_ENV === "production" ? "../dist/min" : "../dist/dev"; @@ -16,6 +17,8 @@ module.exports = function setup(setupContext) { // TODO: in webpack dev mode, we need to reload stats after there's a change // + const metricReport = _.get(setupContext, "routeOptions.reporting", {}); + const { assets } = util.loadAssetsFromStats(setupContext.routeOptions.stats); setupContext.routeOptions.__internals.assets = assets; @@ -68,6 +71,10 @@ ${inlineRuntimeJS} runtimeEntryPoints.forEach(ep => { context.user.includedBundles[ep] = true; }); + + if (metricReport.enable && metricReport.reporter) { + context.user.xarcSSREmitter = util.getEventEmiiter(metricReport.reporter); + } // invoke the initialize method of subapp's server code if (subAppServers.length > 0) { for (const server of subAppServers) { diff --git a/packages/subapp-web/lib/load.js b/packages/subapp-web/lib/load.js index 312bfdaf8..555816514 100644 --- a/packages/subapp-web/lib/load.js +++ b/packages/subapp-web/lib/load.js @@ -360,6 +360,9 @@ ${stack}`, } if (props.serverSideRendering) { + if (!context.user[`prepare-grp-${props.group}`]) { + context.user[`prepare-grp-${props.group}`] = Date.now(); + } const lib = (ssrInfo.lib = util.getFramework(ref)); ssrInfo.awaitData = lib.handlePrepare(); diff --git a/packages/subapp-web/lib/start.js b/packages/subapp-web/lib/start.js index 4f763059a..9701f236f 100644 --- a/packages/subapp-web/lib/start.js +++ b/packages/subapp-web/lib/start.js @@ -10,7 +10,7 @@ const xaa = require("xaa"); module.exports = function setup() { return { process: (context, { props: { concurrency } }) => { - const { xarcSubappSSR } = context.user; + const { xarcSubappSSR, xarcSSREmitter } = context.user; const startMsg = ` @@ -63,6 +63,7 @@ module.exports = function setup() { // default group _ subapps should all run independently if (xarcSubappSSR._) { + const startTime = Date.now(); xaa .map( xarcSubappSSR._.queue, @@ -76,6 +77,12 @@ module.exports = function setup() { context.voidStop(err); xaa.map(xarcSubappSSR._.queue, async info => info.done(), { concurrency }); }); + if (xarcSSREmitter) { + xarcSSREmitter.emit("web_ssr", { + action: `subapps-ssr`, + duration: Date.now() - startTime + }); + } } xaa @@ -85,6 +92,8 @@ module.exports = function setup() { if (group !== "_") { mapCtx.assertNoFailure(); + const loadStartTime = Date.now(); + // first ensure everyone in the queue finish preparing await xaa.map( queue, @@ -95,10 +104,19 @@ module.exports = function setup() { }, { concurrency } ); + if (xarcSSREmitter) { + xarcSSREmitter.emit("web_ssr", { + action: `prepare-group`, + labels: ["group"], + group, + duration: Date.now() - context.user[`prepare-grp-${group}`] + }); + } mapCtx.assertNoFailure(); // and then kick off rendering for every subapp in the group + const renderStartTime = Date.now(); await xaa.map( queue, async (v, ix2, ctx2) => { @@ -108,6 +126,21 @@ module.exports = function setup() { }, { concurrency } ); + if (xarcSSREmitter) { + const now = Date.now(); + xarcSSREmitter.emit("web_ssr", { + action: `render-group`, + group, + labels: ["group"], + duration: now - renderStartTime + }); + xarcSSREmitter.emit("web_ssr", { + action: `group-ssr-total`, + group, + labels: ["group"], + duration: now - loadStartTime + }); + } } }, { concurrency } diff --git a/packages/subapp-web/lib/util.js b/packages/subapp-web/lib/util.js index 3f1296433..273f979e5 100644 --- a/packages/subapp-web/lib/util.js +++ b/packages/subapp-web/lib/util.js @@ -7,6 +7,7 @@ const assert = require("assert"); const Path = require("path"); const _ = require("lodash"); const { tryThrowOriginalSubappRegisterError } = require("subapp-util"); +const EventEmitter = require("events"); let CDN_ASSETS; let CDN_JS_BUNDLES; @@ -341,6 +342,24 @@ ${ignoreMsg}` assets.chunks = stats.chunks; return { assets, stats }; + }, + getEventEmiiter(reporter) { + const emitter = new EventEmitter(); + const groupEvents = {}; + + emitter.on("web_ssr", (data = {}) => { + const group = data.group || "_"; + groupEvents[group] = groupEvents[group] || []; + groupEvents[group].push(data); + + if (data.action === "group-ssr-total" || data.action === "subapps-ssr") { + const events = groupEvents[group]; + events.forEach((event) => reporter(event)); + delete groupEvents[group]; + } + }); + + return emitter; } };