sitespeed.io/lib/plugins/graphite/index.js

184 lines
6.2 KiB
JavaScript

'use strict';
const path = require('path');
const isEmpty = require('lodash.isempty');
const GraphiteSender = require('./graphite-sender');
const StatsDSender = require('./statsd-sender');
const merge = require('lodash.merge');
const get = require('lodash.get');
const log = require('intel').getLogger('sitespeedio.plugin.graphite');
const sendAnnotations = require('./send-annotation');
const DataGenerator = require('./data-generator');
const graphiteUtil = require('../../support/tsdbUtil');
const isStatsd = require('./helpers/is-statsd');
const throwIfMissing = require('../../support/util').throwIfMissing;
const cliUtil = require('../../cli/util');
module.exports = {
name() {
return path.basename(__dirname);
},
/**
* Define `yargs` options with their respective default values. When displayed by the CLI help message
* all options are namespaced by its plugin name.
*
* @return {Object<string, require('yargs').Options} an object mapping the name of the option and its yargs configuration
*/
get cliOptions() {
return require(path.resolve(__dirname, 'cli.js'));
},
open(context, options) {
throwIfMissing(options.graphite, ['host'], 'graphite');
const opts = merge({}, this.config, options.graphite);
this.options = options;
this.perIteration = get(opts, 'perIteration', false);
const SenderConstructor = isStatsd(opts) ? StatsDSender : GraphiteSender;
this.filterRegistry = context.filterRegistry;
this.sender = new SenderConstructor(opts.host, opts.port, opts.bulkSize);
this.dataGenerator = new DataGenerator(
opts.namespace,
opts.includeQueryParams,
options
);
log.debug(
'Setting up Graphite %s:%s for namespace %s',
opts.host,
opts.port,
opts.namespace
);
this.timestamp = context.timestamp;
this.resultUrls = context.resultUrls;
this.messageTypesToFireAnnotations = [];
this.receivedTypesThatFireAnnotations = {};
this.make = context.messageMaker('graphite').make;
this.sendAnnotation = true;
this.alias = {};
this.wptExtras = {};
this.usingBrowsertime = false;
},
processMessage(message, queue) {
// First catch if we are running Browsertime and/or WebPageTest
if (message.type === 'browsertime.setup') {
this.messageTypesToFireAnnotations.push('browsertime.pageSummary');
this.usingBrowsertime = true;
} else if (message.type === 'webpagetest.setup') {
this.messageTypesToFireAnnotations.push('webpagetest.pageSummary');
} else if (message.type === 'browsertime.config') {
if (message.data.screenshot) {
this.useScreenshots = message.data.screenshot;
this.screenshotType = message.data.screenshotType;
}
} else if (message.type === 'sitespeedio.setup') {
// Let other plugins know that the Graphite plugin is alive
queue.postMessage(this.make('graphite.setup'));
} else if (message.type === 'grafana.setup') {
this.sendAnnotation = false;
} else if (message.type === 'browsertime.browser') {
this.browser = message.data.browser;
} else if (
message.type === 'webpagetest.browser' &&
!this.usingBrowsertime
) {
// We are only interested in WebPageTest browser if we run it standalone
this.browser = message.data.browser;
}
if (message.type === 'browsertime.alias') {
this.alias[message.url] = message.data;
}
const filterRegistry = this.filterRegistry;
if (
!(
message.type.endsWith('.summary') ||
message.type.endsWith('.pageSummary') ||
(this.perIteration && message.type.endsWith('.run'))
)
) {
return;
}
if (this.messageTypesToFireAnnotations.includes(message.type)) {
this.receivedTypesThatFireAnnotations[message.url]
? this.receivedTypesThatFireAnnotations[message.url]++
: (this.receivedTypesThatFireAnnotations[message.url] = 1);
}
if (message.type === 'webpagetest.pageSummary') {
this.wptExtras[message.url] = {};
this.wptExtras[message.url].webPageTestResultURL =
message.data.data.summary;
this.wptExtras[message.url].connectivity = message.connectivity;
this.wptExtras[message.url].location = graphiteUtil.toSafeKey(
message.location
);
}
// we only sends individual groups to Graphite, not the
// total of all groups (you can calculate that yourself)
if (message.group === 'total') {
return;
}
message = filterRegistry.filterMessage(message);
if (isEmpty(message.data)) return;
// TODO Here we could add logic to either create a new timestamp or
// use the one that we have for that run. Now just use the one for the
// run
const dataPoints = this.dataGenerator.dataFromMessage(
message,
this.timestamp,
this.alias
);
if (dataPoints.length > 0) {
const data = dataPoints.join('\n') + '\n';
return this.sender.send(data).then(() => {
// Make sure we only send the annotation once per URL:
// If we run browsertime, always send on browsertime.pageSummary
// If we run WebPageTest standalone, send on webPageTestSummary
// when we configured a base url
if (
this.receivedTypesThatFireAnnotations[message.url] ===
this.messageTypesToFireAnnotations.length &&
this.resultUrls.hasBaseUrl() &&
this.sendAnnotation
) {
this.receivedTypesThatFireAnnotations[message.url] = 0;
const absolutePagePath = this.resultUrls.absoluteSummaryPagePath(
message.url
);
return sendAnnotations.send(
message.url,
message.group,
absolutePagePath,
this.useScreenshots,
this.screenshotType,
this.timestamp,
this.alias,
this.wptExtras[message.url],
this.usingBrowsertime,
this.browser,
this.options
);
}
});
} else {
return Promise.reject(
new Error(
'No data to send to graphite for message:\n' +
JSON.stringify(message, null, 2)
)
);
}
},
get config() {
return cliUtil.pluginDefaults(this.cliOptions);
}
};