Skip to content

Instantly share code, notes, and snippets.

@clintongormley
Created April 16, 2013 10:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save clintongormley/5394980 to your computer and use it in GitHub Desktop.
Save clintongormley/5394980 to your computer and use it in GitHub Desktop.
<!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>
/* 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);
}
}
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" ]
}
}
];
}
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 = '&gt;';
limits = rule.upper_limit;
} else if (rule.lower_limit) {
sign = '&lt;';
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:&nbsp;&nbsp; ' +
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, '&amp;').replace(/"/g, '&quot;').replace(
/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment