Comparing version 1.1.2 to 1.2.0
# History | ||
## 1.2.0 | ||
* Tidy up the data format. | ||
* Implement HTTP archive data mapper. | ||
* Add support for syslog-compatible logging. | ||
## 1.1.2 | ||
@@ -4,0 +10,0 @@ |
{ | ||
"name": "boomcatch", | ||
"version": "1.1.2", | ||
"version": "1.2.0", | ||
"description": "Standalone beacon server for boomerang.", | ||
@@ -35,4 +35,10 @@ "homepage": "https://github.com/nature/boomcatch", | ||
"get-off-my-log": "0.1.x", | ||
"qs": "0.6.x" | ||
"qs": "0.6.x", | ||
"useragent": "2.0.x", | ||
"yamlparser": "0.0.x", | ||
"request": "2.34.x" | ||
}, | ||
"optionalDependencies": { | ||
"rconsole": "0.2.x" | ||
}, | ||
"devDependencies": { | ||
@@ -39,0 +45,0 @@ "jshint": "2.4.x", |
@@ -10,3 +10,3 @@ # boomcatch | ||
* **boomcatch version**: *1.1.2* | ||
* **boomcatch version**: *1.2.0* | ||
* **node.js versions**: *0.8, 0.10, 0.11* | ||
@@ -89,2 +89,6 @@ | ||
* `--syslog <facility>`: | ||
Use [syslog]-compatible logging, | ||
with the specified facility level. | ||
* `--validator <path>`: | ||
@@ -173,5 +177,6 @@ Validator used to accept or reject request data. | ||
for back-end stats consumers. | ||
Currently, one mapper is available out-of-the-box, | ||
which formats the metrics | ||
as [statsd] timers. | ||
Currently, two mappers are available out-of-the-box, | ||
which format the metrics | ||
as [statsd] timers | ||
or as a reduced subset [HTTP archive][har]. | ||
@@ -346,3 +351,5 @@ Defining a custom data mapper | ||
[require]: http://nodejs.org/api/globals.html#globals_require | ||
[syslog]: http://en.wikipedia.org/wiki/Syslog | ||
[statsd]: https://github.com/etsy/statsd/ | ||
[har]: http://www.softwareishard.com/blog/har-12-spec/ | ||
[mapper]: https://github.com/nature/boomcatch/blob/master/src/mappers/statsd.js | ||
@@ -349,0 +356,0 @@ [forwarder]: https://github.com/nature/boomcatch/blob/master/src/forwarders/udp.js |
@@ -20,3 +20,3 @@ #!/usr/bin/env node | ||
/*globals require, process */ | ||
/*globals require, process, console */ | ||
@@ -42,2 +42,3 @@ 'use strict'; | ||
.option('-s, --silent', 'prevent the command from logging output to the console') | ||
.option('-y, --syslog <facility>', 'use syslog-compatible logging, with the specified facility level') | ||
.option('-v, --validator <path>', 'validator to use, default is permissive') | ||
@@ -73,3 +74,3 @@ .option('-m, --mapper <path>', 'data mapper to use, default is statsd') | ||
if (!cli.silent) { | ||
cli.log = console.log.bind(console); | ||
cli.log = getLog(); | ||
} | ||
@@ -80,1 +81,33 @@ | ||
function getLog () { | ||
if (cli.syslog) { | ||
initialiseSyslog(); | ||
return console; | ||
} | ||
return getFallbackLog(); | ||
} | ||
function initialiseSyslog () { | ||
try { | ||
require('rconsole'); | ||
console.set({ | ||
facility: cli.syslog, | ||
title: 'boomcatch', | ||
stdout: true, | ||
stderr: true, | ||
showLine: false, | ||
showFile: false, | ||
showTime: true | ||
}); | ||
} catch (e) { | ||
console.log('Failed to initialise syslog, exiting.'); | ||
process.exit(1); | ||
} | ||
} | ||
function getFallbackLog () { | ||
return require('get-off-my-log').initialise('boomcatch', console.log); | ||
} | ||
333
src/index.js
@@ -26,3 +26,2 @@ // Copyright © 2014 Nature Publishing Group | ||
qs = require('qs'), | ||
logger = require('get-off-my-log'), | ||
@@ -37,8 +36,13 @@ defaults = { | ||
maxSize: -1, | ||
log: function () {}, | ||
log: { | ||
info: function () {}, | ||
error: function () {} | ||
}, | ||
validator: 'permissive', | ||
mapper: 'statsd', | ||
forwarder: 'udp' | ||
}; | ||
}, | ||
normalisationMaps; | ||
/** | ||
@@ -58,5 +62,3 @@ * Public function `listen`. | ||
* @option maxSize {number} Maximum body size for POST requests. | ||
* @option log {function} Log function that expects a single string argument | ||
* (without terminating newline character). Defaults to | ||
* `function () {}`. | ||
* @option log {object} Object with `info` and `error` log functions. | ||
* @option validator {string} Validator used to accept or reject beacon requests. | ||
@@ -117,6 +119,6 @@ * Defaults to 'permissive'. | ||
check.verify.maybe.positiveNumber(options.maxSize, 'Invalid max size'); | ||
check.verify.maybe.fn(options.log, 'Invalid log function'); | ||
check.verify.maybe.unemptyString(options.validator, 'Invalid validator'); | ||
verifyOrigin(options.origin); | ||
verifyLog(options.log); | ||
@@ -141,2 +143,11 @@ verifyMapperOptions(options); | ||
function verifyLog (log) { | ||
check.verify.maybe.object(log, 'Invalid log object'); | ||
if (check.object(log)) { | ||
check.verify.fn(log.info, 'Invalid log.info function'); | ||
check.verify.fn(log.error, 'Invalid log.error function'); | ||
} | ||
} | ||
function verifyMapperOptions (options) { | ||
@@ -163,3 +174,3 @@ check.verify.maybe.unemptyString(options.mapper, 'Invalid data mapper'); | ||
function getLog (options) { | ||
return logger.initialise('boomcatch', getOption('log', options)); | ||
return getOption('log', options); | ||
} | ||
@@ -243,3 +254,3 @@ | ||
function handleRequest (log, path, referer, origin, limit, maxSize, validator, mapper, forwarder, request, response) { | ||
var queryIndex, requestPath, state; | ||
var requestPath, remoteAddress, state; | ||
@@ -254,4 +265,3 @@ logRequest(log, request); | ||
queryIndex = request.url.indexOf('?'); | ||
requestPath = queryIndex === -1 ? request.url : request.url.substr(0, queryIndex); | ||
requestPath = getRequestPath(request); | ||
@@ -270,3 +280,5 @@ if (requestPath !== path) { | ||
if (!checkLimit(limit, request)) { | ||
remoteAddress = getRemoteAddress(request); | ||
if (!checkLimit(limit, remoteAddress)) { | ||
return fail(log, request, response, 429, 'Exceeded rate `' + limit.time + '`'); | ||
@@ -280,3 +292,3 @@ } | ||
request.on('data', receive.bind(null, log, state, maxSize, request, response)); | ||
request.on('end', send.bind(null, log, state, validator, mapper, forwarder, request, response)); | ||
request.on('end', send.bind(null, log, state, remoteAddress, validator, mapper, forwarder, request, response)); | ||
} | ||
@@ -320,2 +332,8 @@ | ||
function getRequestPath (request) { | ||
var queryIndex = request.url.indexOf('?'); | ||
return queryIndex === -1 ? request.url : request.url.substr(0, queryIndex); | ||
} | ||
function isValidContentType (contentType) { | ||
@@ -333,5 +351,21 @@ if (!contentType) { | ||
function checkLimit (limit, request) { | ||
var now, address, lastRequest, proxiedAddresses, proxy; | ||
function getRemoteAddress (request) { | ||
var proxiedAddresses = request.headers['x-forwarded-for'], filteredAddresses; | ||
if (proxiedAddresses) { | ||
filteredAddresses = proxiedAddresses.split(',').map(function (address) { | ||
return address.trim(); | ||
}).filter(check.unemptyString); | ||
if (filteredAddresses.length > 0) { | ||
return filteredAddresses[0]; | ||
} | ||
} | ||
return request.socket.remoteAddress; | ||
} | ||
function checkLimit (limit, remoteAddress) { | ||
var now, lastRequest; | ||
if (limit === null) { | ||
@@ -342,11 +376,4 @@ return true; | ||
now = Date.now(); | ||
address = request.socket.remoteAddress; | ||
lastRequest = limit.requests[address]; | ||
proxiedAddresses = request.headers['x-forwarded-for']; | ||
lastRequest = limit.requests[remoteAddress]; | ||
if (check.object(lastRequest)) { | ||
proxy = lastRequest; | ||
lastRequest = lastRequest[proxiedAddresses || 'self']; | ||
} | ||
if (check.positiveNumber(lastRequest) && now <= lastRequest + limit.time) { | ||
@@ -356,14 +383,4 @@ return false; | ||
if (proxiedAddresses) { | ||
if (!proxy) { | ||
proxy = limit.requests[address] = { | ||
self: lastRequest | ||
}; | ||
} | ||
limit.requests[remoteAddress] = now; | ||
proxy[proxiedAddresses] = now; | ||
} else { | ||
limit.requests[address] = now; | ||
} | ||
return true; | ||
@@ -384,3 +401,3 @@ } | ||
function send (log, state, validator, mapper, forwarder, request, response) { | ||
function send (log, state, remoteAddress, validator, mapper, forwarder, request, response) { | ||
try { | ||
@@ -416,3 +433,3 @@ var successStatus, data, mappedData; | ||
mappedData = mapper(normaliseData(data), request.headers.referer); | ||
mappedData = mapper(normaliseData(data), request.headers.referer, request.headers['user-agent'], remoteAddress); | ||
if (mappedData === '') { | ||
@@ -437,33 +454,38 @@ throw null; | ||
function normaliseData (data) { | ||
// TODO: Add metadata for URL, browser, geolocation | ||
return { | ||
boomerang: normaliseBoomerangData(data), | ||
navtiming: normaliseNavigationTimingApiData(data), | ||
restiming: normaliseResourceTimingApiData(data) | ||
rt: normaliseRtData(data), | ||
navtiming: normaliseNavtimingData(data), | ||
restiming: normaliseRestimingData(data) | ||
}; | ||
} | ||
function normaliseBoomerangData (data) { | ||
function normaliseRtData (data) { | ||
/*jshint camelcase:false */ | ||
var startTime, timeToFirstByte, timeToLoad; | ||
var start, timeToFirstByte, timeToLastByte, timeToLoad; | ||
if (data['rt.tstart']) { | ||
startTime = parseInt(data['rt.tstart']); | ||
} | ||
if (data.t_resp) { | ||
timeToFirstByte = parseInt(data.t_resp); | ||
} | ||
start = getOptionalDatum(data, 'rt.tstart'); | ||
timeToFirstByte = getOptionalDatum(data, 't_resp'); | ||
timeToLastByte = getOptionalSum(data, 't_resp', 't_page'); | ||
timeToLoad = parseInt(data.t_done); | ||
if ( | ||
check.maybe.positiveNumber(startTime) && | ||
check.maybe.positiveNumber(start) && | ||
check.maybe.positiveNumber(timeToFirstByte) && | ||
check.positiveNumber(timeToLoad) | ||
check.maybe.positiveNumber(timeToLastByte) && | ||
check.positiveNumber(timeToLoad) && | ||
check.unemptyString(data.r) | ||
) { | ||
return { | ||
start: startTime, | ||
firstbyte: timeToFirstByte, | ||
load: timeToLoad | ||
timestamps: { | ||
start: start | ||
}, | ||
events: {}, | ||
durations: { | ||
firstbyte: timeToFirstByte, | ||
lastbyte: timeToLastByte, | ||
load: timeToLoad | ||
}, | ||
url: data.r | ||
}; | ||
@@ -473,80 +495,151 @@ } | ||
function normaliseNavigationTimingApiData (data) { | ||
function getOptionalDatum (data, key) { | ||
if (data[key]) { | ||
return parseInt(data[key]); | ||
} | ||
} | ||
function getOptionalSum (data, aKey, bKey) { | ||
if (data[aKey] && data[bKey]) { | ||
return parseInt(data[aKey]) + parseInt(data[bKey]); | ||
} | ||
} | ||
function normaliseNavtimingData (data) { | ||
/*jshint camelcase:false */ | ||
var result = normaliseCategory(normalisationMaps.navtiming, data, 'nt_nav_st'); | ||
var startTime, redirectDuration, dnsDuration, connectDuration, timeToFirstByte, timeToDomLoad, timeToLoad; | ||
if (result) { | ||
result.type = data.nt_nav_type; | ||
} | ||
startTime = parseInt(data.nt_nav_st); | ||
redirectDuration = parseInt(data.nt_red_end) - parseInt(data.nt_red_st); | ||
dnsDuration = parseInt(data.nt_dns_end) - parseInt(data.nt_dns_st); | ||
connectDuration = parseInt(data.nt_con_end) - parseInt(data.nt_con_st); | ||
timeToFirstByte = parseInt(data.nt_res_st) - parseInt(data.nt_fet_st); | ||
timeToDomLoad = parseInt(data.nt_domcontloaded_st) - parseInt(data.nt_fet_st); | ||
timeToLoad = parseInt(data.nt_load_st) - parseInt(data.nt_fet_st); | ||
return result; | ||
} | ||
if ( | ||
check.positiveNumber(startTime) && | ||
check.number(redirectDuration) && | ||
check.number(dnsDuration) && | ||
check.number(connectDuration) && | ||
check.positiveNumber(timeToFirstByte) && | ||
check.positiveNumber(timeToDomLoad) && | ||
check.positiveNumber(timeToLoad) | ||
) { | ||
normalisationMaps = { | ||
navtiming: { | ||
timestamps: [ | ||
{ key: 'nt_nav_st', name: 'start' }, | ||
{ key: 'nt_fet_st', name: 'fetchStart' }, | ||
{ key: 'nt_ssl_st', name: 'sslStart', optional: true }, | ||
{ key: 'nt_req_st', name: 'requestStart' }, | ||
{ key: 'nt_domint', name: 'domInteractive' } | ||
], | ||
events: [ | ||
{ start: 'nt_unload_st', end: 'nt_unload_end', name: 'unload' }, | ||
{ start: 'nt_red_st', end: 'nt_red_end', name: 'redirect' }, | ||
{ start: 'nt_dns_st', end: 'nt_dns_end', name: 'dns' }, | ||
{ start: 'nt_con_st', end: 'nt_con_end', name: 'connect' }, | ||
{ start: 'nt_res_st', end: 'nt_res_end', name: 'response' }, | ||
{ start: 'nt_domloading', end: 'nt_domcomp', name: 'dom' }, | ||
{ start: 'nt_domcontloaded_st', end: 'nt_domcontloaded_end', name: 'domContent' }, | ||
{ start: 'nt_load_st', end: 'nt_load_end', name: 'load' } | ||
], | ||
durations: [] | ||
}, | ||
restiming: { | ||
timestamps: [ | ||
{ key: 'rt_st', name: 'start' }, | ||
{ key: 'rt_fet_st', name: 'fetchStart' }, | ||
{ key: 'rt_scon_st', name: 'sslStart', optional: true }, | ||
{ key: 'rt_req_st', name: 'requestStart', optional: true } | ||
], | ||
events: [ | ||
{ start: 'rt_red_st', end: 'rt_red_end', name: 'redirect', optional: true }, | ||
{ start: 'rt_dns_st', end: 'rt_dns_end', name: 'dns', optional: true }, | ||
{ start: 'rt_con_st', end: 'rt_con_end', name: 'connect', optional: true }, | ||
{ start: 'rt_res_st', end: 'rt_res_end', name: 'response', optional: true } | ||
], | ||
durations: [] | ||
} | ||
}; | ||
function normaliseCategory (map, data, startKey) { | ||
try { | ||
return { | ||
start: startTime, | ||
redirect: redirectDuration, | ||
dns: dnsDuration, | ||
connect: connectDuration, | ||
firstbyte: timeToFirstByte, | ||
domload: timeToDomLoad, | ||
load: timeToLoad | ||
timestamps: normaliseTimestamps(map, data), | ||
events: normaliseEvents(map, data), | ||
durations: normaliseDurations(map, data, startKey) | ||
}; | ||
} catch (e) { | ||
} | ||
} | ||
function normaliseResourceTimingApiData (data) { | ||
/*jshint camelcase:false */ | ||
function normaliseTimestamps (map, data) { | ||
return map.timestamps.reduce(function (result, timestamp) { | ||
var value, verify; | ||
var startTime, redirectDuration, dnsDuration, connectDuration, timeToFirstByte, timeToLoad; | ||
if (data[timestamp.key]) { | ||
value = parseInt(data[timestamp.key]); | ||
} | ||
verify = timestamp.optional ? check.verify.maybe : check.verify; | ||
verify.positiveNumber(value); | ||
if (value) { | ||
result[timestamp.name] = value; | ||
} | ||
return result; | ||
}, {}); | ||
} | ||
function normaliseEvents (map, data) { | ||
return map.events.reduce(function (result, event) { | ||
var start, end, verify; | ||
if (data[event.start] && data[event.end]) { | ||
start = parseInt(data[event.start]); | ||
end = parseInt(data[event.end]); | ||
} | ||
verify = event.optional ? check.verify.maybe : check.verify; | ||
verify.positiveNumber(start); | ||
verify.positiveNumber(end); | ||
if (start && end) { | ||
result[event.name] = { | ||
start: start, | ||
end: end | ||
}; | ||
} | ||
return result; | ||
}, {}); | ||
} | ||
function normaliseDurations (map, data, startKey) { | ||
var start = parseInt(data[startKey]); | ||
return map.durations.reduce(function (result, duration) { | ||
var value, verify; | ||
if (data[duration.end]) { | ||
value = parseInt(data[duration.end]) - start; | ||
} | ||
verify = duration.optional ? check.verify.maybe : check.verify; | ||
verify.number(value); | ||
check.verify.not.negativeNumber(value); | ||
if (value) { | ||
result[duration.name] = value; | ||
} | ||
return result; | ||
}, {}); | ||
} | ||
function normaliseRestimingData (data) { | ||
/*jshint camelcase:false */ | ||
if (check.array(data.restiming)) { | ||
return data.restiming.map(function (resource) { | ||
// NOTE: We are wilfully reducing precision here from 1/1000th of a millisecond, | ||
// for consistency with the Navigation Timing API. Open a pull request if | ||
// you think that is the wrong decision! :) | ||
startTime = parseInt(resource.rt_st); | ||
redirectDuration = getOptionalResourceTiming(resource, 'rt_red_end', 'rt_red_st'); | ||
dnsDuration = getOptionalResourceTiming(resource, 'rt_dns_end', 'rt_dns_st'); | ||
connectDuration = getOptionalResourceTiming(resource, 'rt_con_end', 'rt_con_st'); | ||
timeToFirstByte = getOptionalResourceTiming(resource, 'rt_res_st', 'rt_st'); | ||
timeToLoad = parseInt(resource.rt_dur); | ||
return data.restiming.map(function (datum) { | ||
var result = normaliseCategory(normalisationMaps.restiming, datum, 'rt_st'); | ||
// HACK: Google Chrome sometimes reports a zero responseEnd timestamp (which is not | ||
// conformant behaviour), leading to a negative duration. A negative duration | ||
// is manifestly nonsense, so force it to zero instead. Bug report: | ||
// https://code.google.com/p/chromium/issues/detail?id=346960 | ||
if (timeToLoad < 0) { | ||
timeToLoad = 0; | ||
if (result) { | ||
result.name = datum.rt_name; | ||
result.type = datum.rt_in_type; | ||
} | ||
if ( | ||
check.positiveNumber(startTime) && | ||
check.maybe.number(redirectDuration) && | ||
check.maybe.number(dnsDuration) && | ||
check.maybe.number(connectDuration) && | ||
check.maybe.positiveNumber(timeToFirstByte) && | ||
check.number(timeToLoad) | ||
) { | ||
return { | ||
name: resource.rt_name, | ||
type: resource.rt_in_type, | ||
start: startTime, | ||
redirect: redirectDuration, | ||
dns: dnsDuration, | ||
connect: connectDuration, | ||
firstbyte: timeToFirstByte, | ||
load: timeToLoad | ||
}; | ||
} | ||
return result; | ||
}); | ||
@@ -556,8 +649,2 @@ } | ||
function getOptionalResourceTiming (data, endKey, startKey) { | ||
if (data[endKey] && data[startKey]) { | ||
return parseInt(data[endKey]) - parseInt(data[startKey]); | ||
} | ||
} | ||
function pass (log, response, status, bytes) { | ||
@@ -564,0 +651,0 @@ log.info('sent ' + bytes + ' bytes'); |
@@ -48,9 +48,9 @@ // Copyright © 2014 Nature Publishing Group | ||
Object.keys(metrics).forEach(function (category) { | ||
if (data.hasOwnProperty(category)) { | ||
if (category === 'restiming') { | ||
mapper = mapResourceTimingMetrics; | ||
} else { | ||
mapper = mapMetrics; | ||
} | ||
if (category === 'restiming') { | ||
mapper = mapRestimingMetrics; | ||
} else { | ||
mapper = mapMetrics; | ||
} | ||
if (data[category]) { | ||
result += mapper(metrics[category], prefix + category + '.', data[category], referer); | ||
@@ -63,5 +63,4 @@ } | ||
function mapResourceTimingMetrics (metrics, prefix, data, referer) { | ||
function mapRestimingMetrics (metrics, prefix, data, referer) { | ||
return data.map(function (resource, index) { | ||
/*jshint camelcase:false */ | ||
return mapMetrics(metrics, [ | ||
@@ -83,13 +82,12 @@ prefix + base36Encode(referer), | ||
function mapMetrics (metrics, prefix, data) { | ||
return mapTimestamps(metrics, prefix, data) + mapDurations(metrics, prefix, data); | ||
return mapEvents(metrics, prefix, data) + | ||
mapDurations(metrics, prefix, data); | ||
} | ||
function mapTimestamps (metrics, prefix, data) { | ||
return mapStuff(metrics.timestamps, prefix, data, 'g'); | ||
} | ||
function mapEvents (metrics, prefix, data) { | ||
return metrics.events.map(function (metric) { | ||
var datum = data.events[metric]; | ||
function mapStuff (metrics, prefix, data, suffix) { | ||
return metrics.map(function (metric) { | ||
if (check.number(data[metric])) { | ||
return prefix + metric + ':' + data[metric] + '|' + suffix + '\n'; | ||
if (check.object(datum)) { | ||
return mapMetric(prefix, metric, datum.end - datum.start); | ||
} | ||
@@ -101,5 +99,17 @@ | ||
function mapMetric (prefix, name, value) { | ||
return prefix + name + ':' + value + '|ms' + '\n'; | ||
} | ||
function mapDurations (metrics, prefix, data) { | ||
return mapStuff(metrics.durations, prefix, data, 'ms'); | ||
return metrics.durations.map(function (metric) { | ||
var datum = data.durations[metric]; | ||
if (check.number(datum)) { | ||
return mapMetric(prefix, metric, datum); | ||
} | ||
return ''; | ||
}).join(''); | ||
} | ||
@@ -23,15 +23,29 @@ // Copyright © 2014 Nature Publishing Group | ||
module.exports = { | ||
boomerang: { | ||
// Metric definitions for use by data mappers. There are currently three categories | ||
// of metric, corresponding to three different boomerang plugins: rt, navtiming and | ||
// restiming. | ||
// | ||
// Within each category, there are three types of metric that are available for | ||
// data mappers: timestamps, durations and events. | ||
// | ||
// * timestamps: milliseconds since the epoch for some event. | ||
// * events: an object with `start` and `end` timestamp properties. | ||
// * durations: milliseconds difference between the start timestamp and some | ||
// other point. | ||
rt: { | ||
timestamps: [ 'start' ], | ||
durations: [ 'firstbyte', 'load' ] | ||
events: [], | ||
durations: [ 'firstbyte', 'lastbyte', 'load' ] | ||
}, | ||
navtiming: { | ||
timestamps: [ 'start' ], | ||
durations: [ 'redirect', 'dns', 'connect', 'firstbyte', 'domload', 'load' ] | ||
timestamps: [ 'start', 'fetchStart', 'sslStart', 'requestStart', 'domInteractive' ], | ||
events: [ 'unload', 'redirect', 'dns', 'connect', 'response', 'dom', 'domContent', 'load' ], | ||
durations: [] | ||
}, | ||
restiming: { | ||
timestamps: [ 'start' ], | ||
durations: [ 'redirect', 'dns', 'connect', 'firstbyte', 'load' ] | ||
timestamps: [ 'start', 'fetchStart', 'sslStart', 'requestStart' ], | ||
events: [ 'redirect', 'dns', 'connect', 'response' ], | ||
durations: [] | ||
} | ||
}; | ||
@@ -107,36 +107,69 @@ // Copyright © 2014 Nature Publishing Group | ||
result = mapper({ | ||
boomerang: { | ||
start: 1, | ||
firstbyte: 2, | ||
load: 3 | ||
rt: { | ||
timestamps: { | ||
start: 1 | ||
}, | ||
events: {}, | ||
durations: { | ||
firstbyte: 2, | ||
lastbyte: 3, | ||
load: 4 | ||
}, | ||
r: 'http://www.example.com/foo' | ||
}, | ||
navtiming: { | ||
start: 4, | ||
redirect: 5, | ||
dns: 6, | ||
connect: 7, | ||
firstbyte: 8, | ||
domload: 9, | ||
load: 10 | ||
timestamps: { | ||
start: 5, | ||
fetchStart: 6, | ||
sslStart: 7, | ||
requestStart: 8, | ||
domInteractive: 9 | ||
}, | ||
events: { | ||
unload: { start: 10, end: 20 }, | ||
redirect: { start: 11, end: 22 }, | ||
dns: { start: 12, end: 24 }, | ||
connect: { start: 13, end: 26 }, | ||
response: { start: 14, end: 28 }, | ||
dom: { start: 15, end: 30 }, | ||
domContent: { start: 16, end: 32 }, | ||
load: { start: 17, end: 34 } | ||
}, | ||
durations: {}, | ||
type: 'bar' | ||
}, | ||
restiming: [ | ||
{ | ||
name: 'http://www.example.com/foo', | ||
type: 'css', | ||
start: 11, | ||
redirect: 12, | ||
dns: 13, | ||
connect: 14, | ||
firstbyte: 15, | ||
load: 16 | ||
timestamps: { | ||
start: 18, | ||
fetchStart: 19, | ||
sslStart: 20, | ||
requestStart: 21 | ||
}, | ||
events: { | ||
redirect: { start: 22, end: 44 }, | ||
dns: { start: 23, end: 46 }, | ||
connect: { start: 24, end: 48 }, | ||
response: { start: 25, end: 50 } | ||
}, | ||
durations: {}, | ||
name: 'http://www.example.com/baz', | ||
type: 'css' | ||
}, | ||
{ | ||
name: 'foo bar baz qux', | ||
type: 'img', | ||
start: 17, | ||
redirect: 18, | ||
dns: 19, | ||
connect: 20, | ||
firstbyte: 21, | ||
load: 22 | ||
timestamps: { | ||
start: 26, | ||
fetchStart: 27, | ||
sslStart: 28, | ||
requestStart: 29 | ||
}, | ||
events: { | ||
redirect: { start: 30, end: 60 }, | ||
dns: { start: 31, end: 62 }, | ||
connect: { start: 32, end: 64 }, | ||
response: { start: 33, end: 66 } | ||
}, | ||
durations: {}, | ||
name: 'http://www.example.com/qux', | ||
type: 'img' | ||
} | ||
@@ -153,24 +186,21 @@ ] | ||
assert.strictEqual(result, [ | ||
'boomerang.start:1|g', | ||
'boomerang.firstbyte:2|ms', | ||
'boomerang.load:3|ms', | ||
'navtiming.start:4|g', | ||
'navtiming.redirect:5|ms', | ||
'navtiming.dns:6|ms', | ||
'navtiming.connect:7|ms', | ||
'navtiming.firstbyte:8|ms', | ||
'navtiming.domload:9|ms', | ||
'navtiming.load:10|ms', | ||
'restiming.3b2x2q2q302t.0.css.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b2u3333.start:11|g', | ||
'restiming.3b2x2q2q302t.0.css.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b2u3333.redirect:12|ms', | ||
'restiming.3b2x2q2q302t.0.css.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b2u3333.dns:13|ms', | ||
'restiming.3b2x2q2q302t.0.css.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b2u3333.connect:14|ms', | ||
'restiming.3b2x2q2q302t.0.css.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b2u3333.firstbyte:15|ms', | ||
'restiming.3b2x2q2q302t.0.css.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b2u3333.load:16|ms', | ||
'restiming.3b2x2q2q302t.1.img.2u3333w2q2p36w2q2p3ew35393c.start:17|g', | ||
'restiming.3b2x2q2q302t.1.img.2u3333w2q2p36w2q2p3ew35393c.redirect:18|ms', | ||
'restiming.3b2x2q2q302t.1.img.2u3333w2q2p36w2q2p3ew35393c.dns:19|ms', | ||
'restiming.3b2x2q2q302t.1.img.2u3333w2q2p36w2q2p3ew35393c.connect:20|ms', | ||
'restiming.3b2x2q2q302t.1.img.2u3333w2q2p36w2q2p3ew35393c.firstbyte:21|ms', | ||
'restiming.3b2x2q2q302t.1.img.2u3333w2q2p36w2q2p3ew35393c.load:22|ms', | ||
'rt.firstbyte:2|ms', | ||
'rt.lastbyte:3|ms', | ||
'rt.load:4|ms', | ||
'navtiming.unload:10|ms', | ||
'navtiming.redirect:11|ms', | ||
'navtiming.dns:12|ms', | ||
'navtiming.connect:13|ms', | ||
'navtiming.response:14|ms', | ||
'navtiming.dom:15|ms', | ||
'navtiming.domContent:16|ms', | ||
'navtiming.load:17|ms', | ||
'restiming.3b2x2q2q302t.0.css.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b2q2p3e.redirect:22|ms', | ||
'restiming.3b2x2q2q302t.0.css.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b2q2p3e.dns:23|ms', | ||
'restiming.3b2x2q2q302t.0.css.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b2q2p3e.connect:24|ms', | ||
'restiming.3b2x2q2q302t.0.css.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b2q2p3e.response:25|ms', | ||
'restiming.3b2x2q2q302t.1.img.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b35393c.redirect:30|ms', | ||
'restiming.3b2x2q2q302t.1.img.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b35393c.dns:31|ms', | ||
'restiming.3b2x2q2q302t.1.img.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b35393c.connect:32|ms', | ||
'restiming.3b2x2q2q302t.1.img.2w3838341m1b1b3b3b3b1a2t3c2p3134302t1a2r33311b35393c.response:33|ms', | ||
'' | ||
@@ -186,13 +216,8 @@ ].join('\n')); | ||
result = mapper({ | ||
navtiming: { | ||
start: 111, | ||
redirect: 222, | ||
dns: 333, | ||
connect: 444, | ||
firstbyte: 555, | ||
domload: 666, | ||
load: 777 | ||
}, | ||
boomerang: { | ||
load: 888 | ||
rt: { | ||
timestamps: {}, | ||
events: {}, | ||
durations: { | ||
load: 10 | ||
} | ||
} | ||
@@ -207,13 +232,3 @@ }); | ||
test('result was correct', function () { | ||
assert.strictEqual(result, [ | ||
'boomerang.load:888|ms', | ||
'navtiming.start:111|g', | ||
'navtiming.redirect:222|ms', | ||
'navtiming.dns:333|ms', | ||
'navtiming.connect:444|ms', | ||
'navtiming.firstbyte:555|ms', | ||
'navtiming.domload:666|ms', | ||
'navtiming.load:777|ms', | ||
'' | ||
].join('\n')); | ||
assert.strictEqual(result, 'rt.load:10|ms\n'); | ||
}); | ||
@@ -261,4 +276,8 @@ }); | ||
result = mapper({ | ||
boomerang: { | ||
load: 1 | ||
rt: { | ||
timestamps: {}, | ||
events: {}, | ||
durations: { | ||
load: 1 | ||
} | ||
} | ||
@@ -273,3 +292,3 @@ }); | ||
test('result was correct', function () { | ||
assert.strictEqual(result, 'foo.boomerang.load:1|ms\n'); | ||
assert.strictEqual(result, 'foo.rt.load:1|ms\n'); | ||
}); | ||
@@ -301,4 +320,8 @@ }); | ||
result = mapper({ | ||
boomerang: { | ||
load: 2 | ||
rt: { | ||
timestamps: {}, | ||
events: {}, | ||
durations: { | ||
load: 2 | ||
} | ||
} | ||
@@ -313,3 +336,3 @@ }); | ||
test('result was correct', function () { | ||
assert.strictEqual(result, 'bar.boomerang.load:2|ms\n'); | ||
assert.strictEqual(result, 'bar.rt.load:2|ms\n'); | ||
}); | ||
@@ -316,0 +339,0 @@ }); |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
272183
23
5001
357
8
22
+ Addedrequest@2.34.x
+ Addeduseragent@2.0.x
+ Addedyamlparser@0.0.x
+ Addedasn1@0.1.11(transitive)
+ Addedassert-plus@0.1.5(transitive)
+ Addedasync@0.9.2(transitive)
+ Addedaws-sign2@0.5.0(transitive)
+ Addedbindings@1.5.0(transitive)
+ Addedboom@0.4.2(transitive)
+ Addedcolors@0.6.2(transitive)
+ Addedcombined-stream@0.0.7(transitive)
+ Addedcryptiles@0.2.2(transitive)
+ Addedctype@0.5.3(transitive)
+ Addeddelayed-stream@0.0.5(transitive)
+ Addedfile-uri-to-path@1.0.0(transitive)
+ Addedforever-agent@0.5.2(transitive)
+ Addedform-data@0.1.4(transitive)
+ Addedhawk@1.0.0(transitive)
+ Addedhoek@0.9.1(transitive)
+ Addedhttp-signature@0.10.1(transitive)
+ Addedjson-stringify-safe@5.0.1(transitive)
+ Addedlru-cache@2.2.4(transitive)
+ Addedmime@1.2.11(transitive)
+ Addednode-uuid@1.4.8(transitive)
+ Addedoauth-sign@0.3.0(transitive)
+ Addedrconsole@0.2.0(transitive)
+ Addedrequest@2.34.0(transitive)
+ Addedsntp@0.2.4(transitive)
+ Addedtldts@6.1.61(transitive)
+ Addedtldts-core@6.1.61(transitive)
+ Addedtough-cookie@5.0.0(transitive)
+ Addedtunnel-agent@0.3.0(transitive)
+ Addeduseragent@2.0.10(transitive)
+ Addedyamlparser@0.0.2(transitive)