guv
Version:
Grid Utilization Virgilante
113 lines (91 loc) • 4.1 kB
text/coffeescript
Insights = require 'node-insights'
async = require 'async'
debug = require('debug')('guv:insights')
getTimeIntervals = (start, end, intervalMinutes) ->
# use milliseconds Unix epoch time for calculations
startT = start.getTime()
endT = end.getTime()
intervalT = intervalMinutes*60*1000
differenceT = (endT - startT)
intervals = []
nIntervals = Math.ceil(differenceT/intervalT)
for i in [0...nIntervals]
s = startT + intervalT*i
e = startT + intervalT*(i+1)
intervals.push
start: new Date(s)
end: new Date(e)
return intervals
getScaleEventsChunk = (insights, start, end, app, fields, event, andWhere, callback) ->
start = start.toISOString()
end = end.toISOString()
limit = 999
query = "SELECT #{fields} FROM #{event}"
query += " WHERE appName = '#{app}'" if app
query += " WHERE #{andWhere}" if andWhere
query += " SINCE '#{start}' UNTIL '#{end}'"
query += " LIMIT #{limit}"
debug 'query', query
insights.query query, (err, body) ->
return callback err if err
return callback new Error "#{body.error}" if body.error
matches = body.performanceStats.matchCount
return callback new Error "Number of events for interval was above max limit #{matches}" if matches >= limit
results = body.results[0].events
return callback new Error 'No results returned' if not results? # empty array is fine though
return callback null, results
getScaleEvents = (options, callback) ->
insights = new Insights options
# unfortunately, New Relic insights does not allow more than 1000 results per query (max LIMIT)
# OFFSET support also seems pretty broken: offset+limit cannot be more than 1000
# so we subdivide our desired period into many small chunks, (hopefully) smaller than this limit
# build subqueries
queries = []
end = options.end
start = new Date (end.getTime()-options.period*24*60*60*1000)
queries = getTimeIntervals start, end, options.queryInterval
# execute queries
getChunk = (period, cb) ->
return getScaleEventsChunk insights, period.start, period.end, options.app, options.fields, options.event, options.where, cb
debug "Executing #{queries.length} over #{options.period} days"
throw new Error "Extremely high number of queries needed, over 3k: #{queries.length}" if queries.length > 3000
async.mapLimit queries, options.concurrency, getChunk, (err, chunks) ->
return callback err if err
# flatten list
res = []
for chunk in chunks
for r in chunk
res.push r
return callback null, res
parse = (args) ->
addApp = (app, list) ->
list.push app
return list
program = require 'commander'
program
.option('--query-key <hostname>', 'Query Key to access New Relic Insights API', String, '')
.option('--account-id <port>', 'Account ID used to access New Relic Insights API', String, '')
.option('--app <app>', 'App name in New Relic to query for.', String, '')
.option('--period <days>', 'Number of days to get data for', Number, 7)
.option('--fields <one,two>', 'Fields to collect. Comma separated.', String, '*')
.option('--event <EventName>', 'Event to query for', String, 'GuvScaled')
.option('--end <DATETIME>', 'End time of queried period.', String, 'now')
.option('--query-interval <minutes>', 'How big chucks to request at a time', Number, 30)
.option('--concurrency <N>', 'Number of concurrent commands/subprocesses', Number, 5)
.option('--where <CLAUSE>', 'Extra where clause to further limit', String, '')
.parse(args)
normalize = (options) ->
options.accountId = process.env.NEW_RELIC_ACCOUNT_ID if not options.accountId
options.queryKey = process.env.NEW_RELIC_QUERY_KEY if not options.queryKey
if options.end == 'now' or not options.end
options.end = new Date()
else
options.end = new Date options.end
return options
exports.main = main = () ->
options = parse process.argv
options = normalize options
getScaleEvents options, (err, results) ->
throw err if err
console.log JSON.stringify(results, null, 2)
main() if not module.parent