Created
April 16, 2013 10:43
-
-
Save clintongormley/5394980 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" /> | |
<title>Elasticsearch stats analyzer</title> | |
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> | |
<script type="text/javascript" src="rules.js"></script> | |
<script type="text/javascript" src="stats.js"></script> | |
<script type="text/javascript" src="parser.js"></script> | |
<script type="text/javascript"> | |
$(function(){ | |
$('#es_host').submit(function(e){ | |
e.preventDefault(); | |
analyze($('#host').val()); | |
}); | |
analyze($('#host').val()); | |
}) | |
</script> | |
<style type="text/css"> | |
html { color: #333; font-family: sans; font-size: 90% } | |
#results {padding-bottom: 300px; padding-right: 500px; display: inline-block} | |
h1 { font-size: 1.8em } | |
table { border-collapse: collapse; font-size: 85%; margin-left: 20px; } | |
th,td { text-align: left; padding: 2px 5px; border:1px solid #eee;white-space: pre} | |
th { padding-left: 15px} | |
th.group { | |
padding-left: 5px; | |
padding-top: 20px; | |
border: none; | |
border-bottom: 1px solid #999; | |
font-size: 110%; | |
} | |
td > div, th > div {position: relative;} | |
.desc,.lookups { position: absolute; width: 500px; | |
display: none; left: 30px; top: 40px; background: white; | |
border: 2px solid #ccc; padding: 5px 10px; | |
font-weight: normal; z-index: 10000; | |
box-shadow: 5px 5px 5px #ddd; | |
white-space: normal; | |
font-size: 115%; | |
} | |
tr:hover { box-shadow: 5px 5px 5px #ddd; } | |
th:hover .desc { display: block} | |
td:hover .lookups { display: block} | |
.error { font-weight: bold; color: red;} | |
.red { background: #ff8484 } | |
.amber { background: #ffbd7d } | |
.green { background: #ddffba } | |
.grey { background: #ddd} | |
.lookups, .code { font-family: monospace; } | |
</style> | |
</head> | |
<body> | |
<div> | |
<h1>Nodes stats analyzer</h1> | |
<form id="es_host"> | |
<p> | |
<label for="host">Hostname:</label> | |
<input id="host" type="text" value="localhost:9200"> | |
<button type="submit">Analyze</button> | |
</p> | |
</form> | |
</div> | |
<div id="results"></div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Jison generated parser */ | |
var parser = (function(){ | |
var parser = {trace: function trace() { }, | |
yy: {}, | |
symbols_: {"error":2,"expressions":3,"e":4,"EOF":5,"+":6,"-":7,"*":8,"/":9,"(":10,")":11,"NUMBER":12,"KEY":13,"$accept":0,"$end":1}, | |
terminals_: {2:"error",5:"EOF",6:"+",7:"-",8:"*",9:"/",10:"(",11:")",12:"NUMBER",13:"KEY"}, | |
productions_: [0,[3,2],[4,3],[4,3],[4,3],[4,3],[4,3],[4,1],[4,1]], | |
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$) { | |
var $0 = $$.length - 1; | |
switch (yystate) { | |
case 1: return $$[$0-1]; | |
break; | |
case 2:this.$ = $$[$0-2]+$$[$0]; | |
break; | |
case 3:this.$ = $$[$0-2]-$$[$0]; | |
break; | |
case 4:this.$ = $$[$0-2]*$$[$0]; | |
break; | |
case 5:this.$ = $$[$0] ? $$[$0-2]/$$[$0] : 0; | |
break; | |
case 6:this.$ = $$[$0-1]; | |
break; | |
case 7:this.$ = Number(yytext); | |
break; | |
case 8: this.$=yy.get_key(yytext); | |
break; | |
} | |
}, | |
table: [{3:1,4:2,10:[1,3],12:[1,4],13:[1,5]},{1:[3]},{5:[1,6],6:[1,7],7:[1,8],8:[1,9],9:[1,10]},{4:11,10:[1,3],12:[1,4],13:[1,5]},{5:[2,7],6:[2,7],7:[2,7],8:[2,7],9:[2,7],11:[2,7]},{5:[2,8],6:[2,8],7:[2,8],8:[2,8],9:[2,8],11:[2,8]},{1:[2,1]},{4:12,10:[1,3],12:[1,4],13:[1,5]},{4:13,10:[1,3],12:[1,4],13:[1,5]},{4:14,10:[1,3],12:[1,4],13:[1,5]},{4:15,10:[1,3],12:[1,4],13:[1,5]},{11:[1,16],6:[1,7],7:[1,8],8:[1,9],9:[1,10]},{6:[2,2],7:[2,2],8:[1,9],9:[1,10],5:[2,2],11:[2,2]},{6:[2,3],7:[2,3],8:[1,9],9:[1,10],5:[2,3],11:[2,3]},{6:[2,4],7:[2,4],8:[2,4],9:[2,4],5:[2,4],11:[2,4]},{6:[2,5],7:[2,5],8:[2,5],9:[2,5],5:[2,5],11:[2,5]},{5:[2,6],6:[2,6],7:[2,6],8:[2,6],9:[2,6],11:[2,6]}], | |
defaultActions: {6:[2,1]}, | |
parseError: function parseError(str, hash) { | |
throw new Error(str); | |
}, | |
parse: function parse(input) { | |
var self = this, | |
stack = [0], | |
vstack = [null], // semantic value stack | |
lstack = [], // location stack | |
table = this.table, | |
yytext = '', | |
yylineno = 0, | |
yyleng = 0, | |
recovering = 0, | |
TERROR = 2, | |
EOF = 1; | |
//this.reductionCount = this.shiftCount = 0; | |
this.lexer.setInput(input); | |
this.lexer.yy = this.yy; | |
this.yy.lexer = this.lexer; | |
this.yy.parser = this; | |
if (typeof this.lexer.yylloc == 'undefined') | |
this.lexer.yylloc = {}; | |
var yyloc = this.lexer.yylloc; | |
lstack.push(yyloc); | |
var ranges = this.lexer.options && this.lexer.options.ranges; | |
if (typeof this.yy.parseError === 'function') | |
this.parseError = this.yy.parseError; | |
function popStack (n) { | |
stack.length = stack.length - 2*n; | |
vstack.length = vstack.length - n; | |
lstack.length = lstack.length - n; | |
} | |
function lex() { | |
var token; | |
token = self.lexer.lex() || 1; // $end = 1 | |
// if token isn't its numeric value, convert | |
if (typeof token !== 'number') { | |
token = self.symbols_[token] || token; | |
} | |
return token; | |
} | |
var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected; | |
while (true) { | |
// retreive state number from top of stack | |
state = stack[stack.length-1]; | |
// use default actions if available | |
if (this.defaultActions[state]) { | |
action = this.defaultActions[state]; | |
} else { | |
if (symbol === null || typeof symbol == 'undefined') { | |
symbol = lex(); | |
} | |
// read action for current state and first input | |
action = table[state] && table[state][symbol]; | |
} | |
// handle parse error | |
_handle_error: | |
if (typeof action === 'undefined' || !action.length || !action[0]) { | |
var errStr = ''; | |
if (!recovering) { | |
// Report error | |
expected = []; | |
for (p in table[state]) if (this.terminals_[p] && p > 2) { | |
expected.push("'"+this.terminals_[p]+"'"); | |
} | |
if (this.lexer.showPosition) { | |
errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + (this.terminals_[symbol] || symbol)+ "'"; | |
} else { | |
errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + | |
(symbol == 1 /*EOF*/ ? "end of input" : | |
("'"+(this.terminals_[symbol] || symbol)+"'")); | |
} | |
this.parseError(errStr, | |
{text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); | |
} | |
// just recovered from another error | |
if (recovering == 3) { | |
if (symbol == EOF) { | |
throw new Error(errStr || 'Parsing halted.'); | |
} | |
// discard current lookahead and grab another | |
yyleng = this.lexer.yyleng; | |
yytext = this.lexer.yytext; | |
yylineno = this.lexer.yylineno; | |
yyloc = this.lexer.yylloc; | |
symbol = lex(); | |
} | |
// try to recover from error | |
while (1) { | |
// check for error recovery rule in this state | |
if ((TERROR.toString()) in table[state]) { | |
break; | |
} | |
if (state === 0) { | |
throw new Error(errStr || 'Parsing halted.'); | |
} | |
popStack(1); | |
state = stack[stack.length-1]; | |
} | |
preErrorSymbol = symbol == 2 ? null : symbol; // save the lookahead token | |
symbol = TERROR; // insert generic error symbol as new lookahead | |
state = stack[stack.length-1]; | |
action = table[state] && table[state][TERROR]; | |
recovering = 3; // allow 3 real symbols to be shifted before reporting a new error | |
} | |
// this shouldn't happen, unless resolve defaults are off | |
if (action[0] instanceof Array && action.length > 1) { | |
throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); | |
} | |
switch (action[0]) { | |
case 1: // shift | |
//this.shiftCount++; | |
stack.push(symbol); | |
vstack.push(this.lexer.yytext); | |
lstack.push(this.lexer.yylloc); | |
stack.push(action[1]); // push state | |
symbol = null; | |
if (!preErrorSymbol) { // normal execution/no error | |
yyleng = this.lexer.yyleng; | |
yytext = this.lexer.yytext; | |
yylineno = this.lexer.yylineno; | |
yyloc = this.lexer.yylloc; | |
if (recovering > 0) | |
recovering--; | |
} else { // error just occurred, resume old lookahead f/ before error | |
symbol = preErrorSymbol; | |
preErrorSymbol = null; | |
} | |
break; | |
case 2: // reduce | |
//this.reductionCount++; | |
len = this.productions_[action[1]][1]; | |
// perform semantic action | |
yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 | |
// default location, uses first token for firsts, last for lasts | |
yyval._$ = { | |
first_line: lstack[lstack.length-(len||1)].first_line, | |
last_line: lstack[lstack.length-1].last_line, | |
first_column: lstack[lstack.length-(len||1)].first_column, | |
last_column: lstack[lstack.length-1].last_column | |
}; | |
if (ranges) { | |
yyval._$.range = [lstack[lstack.length-(len||1)].range[0], lstack[lstack.length-1].range[1]]; | |
} | |
r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); | |
if (typeof r !== 'undefined') { | |
return r; | |
} | |
// pop off stack | |
if (len) { | |
stack = stack.slice(0,-1*len*2); | |
vstack = vstack.slice(0, -1*len); | |
lstack = lstack.slice(0, -1*len); | |
} | |
stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) | |
vstack.push(yyval.$); | |
lstack.push(yyval._$); | |
// goto new state = table[STATE][NONTERMINAL] | |
newState = table[stack[stack.length-2]][stack[stack.length-1]]; | |
stack.push(newState); | |
break; | |
case 3: // accept | |
return true; | |
} | |
} | |
return true; | |
}}; | |
/* Jison generated lexer */ | |
var lexer = (function(){ | |
var lexer = ({EOF:1, | |
parseError:function parseError(str, hash) { | |
if (this.yy.parser) { | |
this.yy.parser.parseError(str, hash); | |
} else { | |
throw new Error(str); | |
} | |
}, | |
setInput:function (input) { | |
this._input = input; | |
this._more = this._less = this.done = false; | |
this.yylineno = this.yyleng = 0; | |
this.yytext = this.matched = this.match = ''; | |
this.conditionStack = ['INITIAL']; | |
this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; | |
if (this.options.ranges) this.yylloc.range = [0,0]; | |
this.offset = 0; | |
return this; | |
}, | |
input:function () { | |
var ch = this._input[0]; | |
this.yytext += ch; | |
this.yyleng++; | |
this.offset++; | |
this.match += ch; | |
this.matched += ch; | |
var lines = ch.match(/(?:\r\n?|\n).*/g); | |
if (lines) { | |
this.yylineno++; | |
this.yylloc.last_line++; | |
} else { | |
this.yylloc.last_column++; | |
} | |
if (this.options.ranges) this.yylloc.range[1]++; | |
this._input = this._input.slice(1); | |
return ch; | |
}, | |
unput:function (ch) { | |
var len = ch.length; | |
var lines = ch.split(/(?:\r\n?|\n)/g); | |
this._input = ch + this._input; | |
this.yytext = this.yytext.substr(0, this.yytext.length-len-1); | |
//this.yyleng -= len; | |
this.offset -= len; | |
var oldLines = this.match.split(/(?:\r\n?|\n)/g); | |
this.match = this.match.substr(0, this.match.length-1); | |
this.matched = this.matched.substr(0, this.matched.length-1); | |
if (lines.length-1) this.yylineno -= lines.length-1; | |
var r = this.yylloc.range; | |
this.yylloc = {first_line: this.yylloc.first_line, | |
last_line: this.yylineno+1, | |
first_column: this.yylloc.first_column, | |
last_column: lines ? | |
(lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: | |
this.yylloc.first_column - len | |
}; | |
if (this.options.ranges) { | |
this.yylloc.range = [r[0], r[0] + this.yyleng - len]; | |
} | |
return this; | |
}, | |
more:function () { | |
this._more = true; | |
return this; | |
}, | |
less:function (n) { | |
this.unput(this.match.slice(n)); | |
}, | |
pastInput:function () { | |
var past = this.matched.substr(0, this.matched.length - this.match.length); | |
return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); | |
}, | |
upcomingInput:function () { | |
var next = this.match; | |
if (next.length < 20) { | |
next += this._input.substr(0, 20-next.length); | |
} | |
return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); | |
}, | |
showPosition:function () { | |
var pre = this.pastInput(); | |
var c = new Array(pre.length + 1).join("-"); | |
return pre + this.upcomingInput() + "\n" + c+"^"; | |
}, | |
next:function () { | |
if (this.done) { | |
return this.EOF; | |
} | |
if (!this._input) this.done = true; | |
var token, | |
match, | |
tempMatch, | |
index, | |
col, | |
lines; | |
if (!this._more) { | |
this.yytext = ''; | |
this.match = ''; | |
} | |
var rules = this._currentRules(); | |
for (var i=0;i < rules.length; i++) { | |
tempMatch = this._input.match(this.rules[rules[i]]); | |
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { | |
match = tempMatch; | |
index = i; | |
if (!this.options.flex) break; | |
} | |
} | |
if (match) { | |
lines = match[0].match(/(?:\r\n?|\n).*/g); | |
if (lines) this.yylineno += lines.length; | |
this.yylloc = {first_line: this.yylloc.last_line, | |
last_line: this.yylineno+1, | |
first_column: this.yylloc.last_column, | |
last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; | |
this.yytext += match[0]; | |
this.match += match[0]; | |
this.matches = match; | |
this.yyleng = this.yytext.length; | |
if (this.options.ranges) { | |
this.yylloc.range = [this.offset, this.offset += this.yyleng]; | |
} | |
this._more = false; | |
this._input = this._input.slice(match[0].length); | |
this.matched += match[0]; | |
token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); | |
if (this.done && this._input) this.done = false; | |
if (token) return token; | |
else return; | |
} | |
if (this._input === "") { | |
return this.EOF; | |
} else { | |
return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), | |
{text: "", token: null, line: this.yylineno}); | |
} | |
}, | |
lex:function lex() { | |
var r = this.next(); | |
if (typeof r !== 'undefined') { | |
return r; | |
} else { | |
return this.lex(); | |
} | |
}, | |
begin:function begin(condition) { | |
this.conditionStack.push(condition); | |
}, | |
popState:function popState() { | |
return this.conditionStack.pop(); | |
}, | |
_currentRules:function _currentRules() { | |
return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; | |
}, | |
topState:function () { | |
return this.conditionStack[this.conditionStack.length-2]; | |
}, | |
pushState:function begin(condition) { | |
this.begin(condition); | |
}}); | |
lexer.options = {}; | |
lexer.performAction = function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) { | |
var YYSTATE=YY_START | |
switch($avoiding_name_collisions) { | |
case 0:/* skip whitespace */ | |
break; | |
case 1:return 13 | |
break; | |
case 2:return 12 | |
break; | |
case 3:return 8 | |
break; | |
case 4:return 9 | |
break; | |
case 5:return 7 | |
break; | |
case 6:return 6 | |
break; | |
case 7:return '^' | |
break; | |
case 8:return '!' | |
break; | |
case 9:return '%' | |
break; | |
case 10:return 10 | |
break; | |
case 11:return 11 | |
break; | |
case 12:return 5 | |
break; | |
case 13:return 'INVALID' | |
break; | |
} | |
}; | |
lexer.rules = [/^(?:\s+)/,/^(?:[a-z]([a-zA-Z_.]|\\\s)+)/,/^(?:[0-9]+(\.[0-9]+)?\b)/,/^(?:\*)/,/^(?:\/)/,/^(?:-)/,/^(?:\+)/,/^(?:\^)/,/^(?:!)/,/^(?:%)/,/^(?:\()/,/^(?:\))/,/^(?:$)/,/^(?:.)/]; | |
lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}}; | |
return lexer;})() | |
parser.lexer = lexer; | |
function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; | |
return new Parser; | |
})(); | |
if (typeof require !== 'undefined' && typeof exports !== 'undefined') { | |
exports.parser = parser; | |
exports.Parser = parser.Parser; | |
exports.parse = function () { return parser.parse.apply(parser, arguments); } | |
exports.main = function commonjsMain(args) { | |
if (!args[1]) | |
throw new Error('Usage: '+args[0]+' FILE'); | |
var source, cwd; | |
if (typeof process !== 'undefined') { | |
source = require("fs").readFileSync(require("path").resolve(args[1]), "utf8"); | |
} else { | |
source = require("file").path(require("file").cwd()).join(args[1]).read({charset: "utf-8"}); | |
} | |
return exports.parser.parse(source); | |
} | |
if (typeof module !== 'undefined' && require.main === module) { | |
exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Rules = [ | |
"General", general_rules(), | |
"Filesystem", fs_rules(), | |
"Actions", action_rules(), | |
"Cache", cache_rules(), | |
"Memory", memory_rules(), | |
// "Threads", thread_rules(), | |
"Network", network_rules() | |
]; | |
function general_rules() { | |
return [ | |
{ | |
"Name" : { | |
"val" : "stats.name" | |
} | |
}, | |
{ | |
"IP" : { | |
"val" : "stats.transport_address" | |
} | |
}, | |
{ | |
"ID" : { | |
"val" : "id" | |
} | |
}, | |
{ | |
"ES Uptime" : { | |
"unit" : "days", | |
"format" : "float", | |
"val" : "stats.jvm.uptime_in_millis / 1000 / 60 / 60 / 24" | |
} | |
}, | |
{ | |
"CPU": { | |
"val": "info.os.cpu.model" | |
} | |
}, | |
{ | |
"Cores": { | |
"val": "info.os.cpu.total_cores" | |
} | |
} | |
]; | |
} | |
function fs_rules() { | |
return [ | |
{ | |
"Store size" : { | |
"val" : "stats.indices.store.size" | |
} | |
}, | |
{ | |
"Docs total" : { | |
"format" : "comma", | |
"val" : "stats.indices.docs.count" | |
} | |
}, | |
{ | |
"Docs deleted %" : { | |
"comment" : "High values indicate insufficient merging. Slow I/O?", | |
"format" : "pct", | |
"val" : "stats.indices.docs.deleted / stats.indices.docs.count", | |
"upper_limit" : [ "0.1", "0.25" ] | |
} | |
}, | |
{ | |
"Merge size" : { | |
"val" : "stats.indices.merges.total_size" | |
} | |
}, | |
{ | |
"Merge time" : { | |
"val" : "stats.indices.merges.total_time" | |
} | |
}, | |
{ | |
"Merge rate" : { | |
"unit" : "MB/s", | |
"comment" : "Low rates indicate throttling or slow I/O", | |
"format" : "float", | |
"val" : "stats.indices.merges.total_size_in_bytes / stats.indices.merges.total_time_in_millis / 1000" | |
} | |
}, | |
{ | |
"File descriptors" : { | |
"format" : "comma", | |
"val" : "stats.process.open_file_descriptors" | |
} | |
} | |
]; | |
} | |
function action_rules() { | |
return [ | |
{ | |
"Indexing - index" : { | |
"comment" : "High values indicate complex documents or slow I/O or CPU.", | |
"format" : "ms", | |
"val" : "stats.indices.indexing.index_time_in_millis / stats.indices.indexing.index_total", | |
"upper_limit" : [ "10", "50" ] | |
} | |
}, | |
{ | |
"Indexing - delete" : { | |
"comment" : "High values indicate slow I/O.", | |
"format" : "ms", | |
"val" : "stats.indices.indexing.delete_time_in_millis / stats.indices.indexing.delete_total", | |
"upper_limit" : [ "5", "10" ] | |
} | |
}, | |
{ | |
"Search - query" : { | |
"comment" : "High values indicate complex or inefficient queries, insufficient use of filters, insufficient RAM for caching, slow I/O or CPU.", | |
"format" : "ms", | |
"val" : "stats.indices.search.query_time_in_millis / stats.indices.search.query_total", | |
"upper_limit" : [ "50", "500" ] | |
} | |
}, | |
{ | |
"Search - fetch" : { | |
"comment" : "High values indicate slow I/O, large docs, or fetching too many docs, eg deep paging.", | |
"format" : "ms", | |
"val" : "stats.indices.search.fetch_time_in_millis / stats.indices.search.fetch_total", | |
"upper_limit" : [ "8", "15" ] | |
} | |
}, | |
{ | |
"Get - total" : { | |
"comment" : "High values indicate slow I/O.", | |
"format" : "ms", | |
"val" : "stats.indices.get.time_in_millis / stats.indices.get.total", | |
"upper_limit" : [ "5", "10" ] | |
} | |
}, | |
{ | |
"Get - exists" : { | |
"comment" : "???", | |
"format" : "ms", | |
"val" : "stats.indices.get.exists_time_in_millis / stats.indices.get.exists_total", | |
"upper_limit" : [ "5", "10" ] | |
} | |
}, | |
{ | |
"Get - missing" : { | |
"comment" : "???", | |
"format" : "ms", | |
"val" : "stats.indices.get.missing_time_in_millis / stats.indices.get.missing_total", | |
"upper_limit" : [ "2", "5" ] | |
} | |
}, | |
{ | |
"Refresh" : { | |
"comment" : "High values indicate slow I/O.", | |
"format" : "ms", | |
"val" : "stats.indices.refresh.total_time_in_millis / stats.indices.refresh.total", | |
"upper_limit" : [ "10", "20" ] | |
} | |
}, | |
{ | |
"Flush" : { | |
"comment" : "High values indicate slow I/O.", | |
"format" : "ms", | |
"val" : "stats.indices.flush.total_time_in_millis / stats.indices.flush.total", | |
"upper_limit" : [ "750", "1500" ] | |
} | |
} | |
]; | |
} | |
function cache_rules() { | |
return [ | |
{ | |
"Field size" : { | |
"val" : "stats.indices.fielddata.memory_size" | |
} | |
}, | |
{ | |
"Field evictions" : { | |
"comment" : "Field values should not be evicted - insufficient RAM for current queries.", | |
"format" : "comma", | |
"val" : "stats.indices.fielddata.evictions", | |
"upper_limit" : [ "0", "0" ] | |
} | |
}, | |
{ | |
"Filter size" : { | |
"val" : "stats.indices.cache.filter_size" | |
} | |
}, | |
{ | |
"Filter evictions" : { | |
"unit" : "per query", | |
"comment" : "High values indicate insufficient RAM for current queries, or frequent use of one-off values in filters.", | |
"format" : "float", | |
"val" : "stats.indices.cache.filter_evictions / stats.indices.search.query_total", | |
"upper_limit" : [ "0.1", "0.2" ] | |
} | |
}, | |
{ | |
"ID size" : { | |
"val" : "stats.indices.cache.id_cache_size" | |
} | |
}, | |
{ | |
"ID %" : { | |
"val" : "stats.indices.cache.id_cache_size_in_bytes / stats.jvm.mem.heap_committed_in_bytes", | |
"format" : "pct", | |
"upper_limit": ["0.2","0.4"], | |
"comment": "Large parent/child ID caches reduce the amount of memory available on the heap." | |
} | |
} | |
]; | |
} | |
function memory_rules() { | |
return [ | |
{ | |
"Total mem" : { | |
"unit" : "gb", | |
"format" : "comma", | |
"val" : "( stats.os.mem.actual_used_in_bytes + stats.os.mem.actual_free_in_bytes ) / 1024 / 1024 / 1024" | |
} | |
}, | |
{ | |
"Heap size" : { | |
"unit" : "gb", | |
"comment" : "A heap size over 32GB causes the JVM to use uncompressed pointers and can slow GC.", | |
"format" : "float", | |
"val" : "stats.jvm.mem.heap_committed_in_bytes / 1024 / 1024 / 1024", | |
"upper_limit" : [ "30", "32" ] | |
} | |
}, | |
{ | |
"Heap % of RAM" : { | |
"comment" : "Approx 40-50% of RAM should be available to the kernel for file caching.", | |
"format" : "pct", | |
"val" : "stats.jvm.mem.heap_committed_in_bytes / (stats.os.mem.actual_used_in_bytes + stats.os.mem.actual_free_in_bytes)", | |
"upper_limit" : [ "0.6", "0.75" ] | |
} | |
}, | |
{ | |
"Heap used %" : { | |
"format" : "pct", | |
"val" : "stats.jvm.mem.heap_used_in_bytes / stats.jvm.mem.heap_committed_in_bytes", | |
} | |
}, | |
{ | |
"GC MarkSweep frequency" : { | |
"unit" : "s", | |
"comment" : "Too frequent GC indicates memory pressure and need for more heap space.", | |
"format" : "comma", | |
"val" : "stats.jvm.uptime_in_millis / stats.jvm.gc.collectors.ConcurrentMarkSweep.collection_count / 1000", | |
"lower_limit" : [ "30", "15", "0" ] | |
} | |
}, | |
{ | |
"GC MarkSweep duration" : { | |
"comment" : "Long durations may indicate that swapping is slowing down GC, or need for more heap space.", | |
"format" : "ms", | |
"val" : "stats.jvm.gc.collectors.ConcurrentMarkSweep.collection_time_in_millis / stats.jvm.gc.collectors.ConcurrentMarkSweep.collection_count", | |
"upper_limit" : [ "150", "400" ] | |
} | |
}, | |
{ | |
"GC ParNew frequency" : { | |
"unit" : "s", | |
"format" : "comma", | |
"val" : "stats.jvm.uptime_in_millis / stats.jvm.gc.collectors.ParNew.collection_count / 1000" | |
} | |
}, | |
{ | |
"GC ParNew duration" : { | |
"format" : "ms", | |
"val" : "stats.jvm.gc.collectors.ParNew.collection_time_in_millis / stats.jvm.gc.collectors.ParNew.collection_count", | |
"upper_limit" : [ "100", "200" ] | |
} | |
}, | |
{ | |
"Swap" : { | |
"val": "stats.os.swap.used_in_bytes / 1024 / 1024", | |
"unit": "mb", | |
"upper_limit": ["1","1"], | |
"comment": "Any use of swap by the JVM, no matter how small, can greatly impact the speed of the garbage collector." | |
} | |
} | |
]; | |
} | |
function network_rules() { | |
return [ | |
{ | |
"HTTP connection rate" : { | |
"unit" : "per sec", | |
"comment" : "Too many HTTP connection per second may exhaust the number of sockets available in the kernel, and cause a service outage.", | |
"format" : "comma", | |
"val" : "stats.http.total_opened / stats.jvm.uptime_in_millis * 1000", | |
"upper_limit" : [ "5", "30" ] | |
} | |
} | |
]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Rules; | |
var Formats = { | |
comma : function(n) { | |
n = Math.round(n) + ''; | |
var re = /^([-+]?\d+)(\d{3})/; | |
while (1) { | |
var new_n = n.replace(re, '$1,$2'); | |
if (new_n === n) { | |
break; | |
} | |
n = new_n | |
} | |
return n; | |
}, | |
pct : function(n) { | |
n = Math.round(n * 1000) / 10; | |
return n + '%'; | |
}, | |
ms : function(n) { | |
n = Math.round(n * 100) / 100; | |
return n + 'ms'; | |
}, | |
float : function(n) { | |
return Math.round(n * 10) / 10; | |
}, | |
}; | |
var lookups = {}; | |
function init_parser(top_data) { | |
return function(key) { | |
var data = top_data; | |
key = key.replace(/\\/g, ''); | |
var parts = key.split('.'); | |
var last = parts.pop(); | |
for (i = 0; i < parts.length; i++) { | |
var part = parts[i]; | |
if (!typeof data[part] === 'object') { | |
throw new Error("Invalid key: " + key) | |
} | |
data = data[part]; | |
} | |
if (data === undefined || data[last] === undefined) { | |
throw new Error("Invalid key: " + key) | |
} | |
lookups[key] = data[last]; | |
return data[last]; | |
} | |
} | |
function rule_lookups() { | |
var temp = []; | |
var keys = Object.keys(lookups).sort(); | |
for (var i = 0; i < keys.length; i++) { | |
var key = keys[i]; | |
temp.push([key,lookups[key]]); | |
} | |
return temp; | |
} | |
function reset_parser() { | |
for (var key in lookups) { | |
delete lookups[key]; | |
} | |
} | |
function es_request(host, uri) { | |
return $.ajax({ | |
url : 'http://' + host + uri, | |
dataType : "jsonp", | |
timeout: 2000 | |
}); | |
} | |
function analyze(host) { | |
var host = $('#host').val(); | |
var data = { | |
nodes : {} | |
}; | |
set_contents('<h1>Fetching...</h1>'); | |
es_request(host, '/_cluster/nodes/stats?all=1').done(function(nstats) { | |
es_request(host, "/_nodes?all=1").done(function(ninfo) { | |
data.cluster_name = nstats.cluster_name; | |
for (node in nstats.nodes) { | |
data.nodes[node] = { | |
stats : nstats.nodes[node], | |
info : ninfo.nodes[node] | |
}; | |
} | |
calc_stats(data); | |
}) | |
}).fail(function() { | |
set_contents('<h1>Failed to retrieve data from: ' +escape(host)+'</h1>'); | |
}); | |
} | |
function calc_stats(data) { | |
var stats = []; | |
var lookups = {}; | |
for (id in data.nodes) { | |
var node = data.nodes[id]; | |
parser.yy.get_key = init_parser(node,lookups); | |
node.id = id; | |
node.stats.transport_address = | |
node.stats.transport_address.replace(/inet\[\/([^\]]+)\]/, "$1"); | |
var entry = []; | |
for (var i = 1; i < Rules.length; i=i+2) { | |
Rules[i].forEach(function(el) { | |
reset_parser(); | |
for (title in el) { | |
var rule = el[title]; | |
try { | |
var val = parser.parse(rule.val); | |
var color = choose_color(rule, val); | |
if (rule.format) { | |
val = Formats[rule.format](val) | |
} | |
if (rule.unit) { | |
val = val + ' ' + rule.unit | |
} | |
var lookups = rule_lookups(); | |
entry.push([ val, color,lookups ]); | |
} catch (e) { | |
entry.push([ '?', 'grey',[['Error',e+'']] ]); | |
} | |
} | |
}); | |
} | |
stats.push(entry); | |
} | |
generate_table(data.cluster_name, stats); | |
} | |
function choose_color(rule, val) { | |
if (rule.upper_limit) { | |
return val <= rule.upper_limit[0] ? 'green' | |
: val <= rule.upper_limit[1] ? 'amber' : 'red'; | |
} | |
if (rule.lower_limit) { | |
if (rule.lower_limit.length === 3 && val === 0) { | |
return 'green'; | |
} | |
return val >= rule.lower_limit[0] ? 'green' | |
: val >= rule.lower_limit[1] ? 'amber' : 'red'; | |
} | |
return '' | |
} | |
function generate_table(cluster_name, stats) { | |
stats.sort(function(a, b) { | |
if (a[0][0] < b[0][0]) { | |
return -1 | |
} | |
if (a[0][0] > b[0][0]) { | |
return 1 | |
} | |
return 0 | |
}); | |
var table = ''; | |
var cols = stats.length + 1; | |
for (var i = 0; i < Rules.length-1; i=i+2) { | |
var group_title = Rules[i]; | |
var rules = Rules[i+1]; | |
table = table + '<tr><th class="group" colspan="' + cols + '">' + escape(group_title) + ':</th></tr>'; | |
rules.forEach(function(el) { | |
for (title in el) { | |
var rule = el[title]; | |
var limits, sign; | |
if (rule.upper_limit) { | |
sign = '>'; | |
limits = rule.upper_limit; | |
} else if (rule.lower_limit) { | |
sign = '<'; | |
limits = rule.lower_limit | |
} | |
var comment = rule.comment; | |
var row = title_cell(title, rule.val, limits, sign, rule.comment); | |
stats.forEach(function(col) { | |
var cell = col.shift(); | |
var lookups = format_lookups(cell[2]); | |
row = row + '<td class="' + cell[1] + '">' + cell[0] + lookups + '</td>' | |
}); | |
table = table + '<tr>' + row + '</tr>'; | |
} | |
}); | |
} | |
table = '<table cellspacing="0">' + table + '</table>'; | |
var date = new Date(); | |
var timestamp = '<p>Created on: ' + date.toLocaleString() + '</p>'; | |
set_contents('<h1>Cluster: ' + escape(cluster_name) + '</h1>' + timestamp + table); | |
} | |
function set_contents(html) { | |
$('#results').html(html); | |
} | |
function title_cell(title, val, limits, sign, comment) { | |
title = escape(title); | |
val = escape(val); | |
comment = comment ? '<p>' + escape(comment) + '</p>' : ''; | |
var limit = ''; | |
if (limits) { | |
limit = | |
'<p><b>Limits:</b></p>' + '<p class="code">Amber: ' + sign + ' ' + | |
limits[0] + '</p>' + '<p class="code">Red: ' + | |
sign + ' ' + limits[1] + '</p>' | |
} | |
return '<th>' + '<div>' + title + '<div class="desc">' + '<p><b>' + title + | |
'</p></b>' + '<p class="code">' + val + '</p>' + limit + comment + | |
'</div>' + '</div>' + '</th>' | |
} | |
function format_lookups(vals) { | |
if (!vals) { return ''}; | |
var rows = []; | |
for (var i = 0; i < vals.length; i++) { | |
var key = vals[i][0]; | |
var val = Formats.comma(vals[i][1]); | |
if (val == "NaN") { val = vals[i][1]} | |
rows.push('<b>'+escape(key)+'</b>: '+escape(val)); | |
} | |
return '<div class="lookups_wrapper">'+ | |
'<div class="lookups">' + | |
rows.join("<br />") + | |
'</div></div>'; | |
} | |
function escape(str) { | |
return String(str).replace(/&/g, '&').replace(/"/g, '"').replace( | |
/'/g, ''').replace(/</g, '<').replace(/>/g, '>'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment