Created
April 16, 2013 10:43
-
-
Save clintongormley/5394980 to your computer and use it in GitHub Desktop.
Revisions
-
clintongormley created this gist
Apr 16, 2013 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,66 @@ <!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 hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,454 @@ /* 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 hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,304 @@ 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 hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,247 @@ 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, '>'); }