basicadmin.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. /**
  2. * Gemini Basic Administration
  3. */
  4. var gba = (function() {
  5. /** Pad a number to two digits. */
  6. function pad(n) {
  7. return (n < 10) ? '0' + n : n
  8. }
  9. /** Render a full date. */
  10. function standardDateString(d) {
  11. return d.getFullYear() + '-'
  12. + pad(d.getMonth() + 1)+'-'
  13. + pad(d.getDate()) + ' '
  14. + pad(d.getHours()) + ':'
  15. + pad(d.getMinutes()) + ':'
  16. + pad(d.getSeconds());
  17. }
  18. /** Render a date object as time only. */
  19. function standardTimeString(d) {
  20. return pad(d.getHours()) + ':'
  21. + pad(d.getMinutes()) + ':'
  22. + pad(d.getSeconds());
  23. }
  24. /** Truncate and add an abbr tag. */
  25. function abbrTruncate(inputText, desiredLength, abbrMaxLength) {
  26. if (inputText.length > desiredLength) {
  27. if ( (abbrMaxLength != undefined)
  28. && (inputText.length > abbrMaxLength)
  29. ) {
  30. return '<abbr title="' + inputText.substring(0, abbrMaxLength - 3) + '...">' + inputText.substring(0, desiredLength - 3) + '...</abbr>';
  31. } else {
  32. return '<abbr title="' + inputText + '">' + inputText.substring(0, desiredLength - 3) + '...</abbr>';
  33. }
  34. }
  35. else {
  36. return inputText;
  37. }
  38. }
  39. /** Render the list of monitored commands in the performance monitor. */
  40. function renderPerformanceMonitorList(commands, detail, exceptionalCoefficient,
  41. includeSpecialTime, concurrencyHighlight) {
  42. var st = false, // Special time
  43. cp = false, // Compact mode
  44. avgw = 8, // Width in columns
  45. worw = 6, // Width in columns
  46. totalRequests = 0,
  47. totalConcurrent = 0,
  48. lastHourRequests = 0,
  49. totalDispatches = 0, // Rough totals for averages across all commands.
  50. totalQueries = 0,
  51. totalQueryErrors = 0,
  52. totalQueryTime = 0,
  53. totalLogicTime = 0,
  54. totalRenderTime = 0,
  55. totalSpecialTime = 0,
  56. totalTotalTime = 0,
  57. totalCpuTime = 0,
  58. worstQueries = 0, // Worsts across all commands.
  59. worstQueryErrors = 0,
  60. worstQueryTime = 0,
  61. worstSpecialTime = 0,
  62. worstLogicTime = 0,
  63. worstRenderTime = 0,
  64. worstCpuTime = 0,
  65. content = ''; // The HTML to render.
  66. if (includeSpecialTime == 1) {
  67. // Standard mode, do include ST (special time).
  68. st = true;
  69. avgw = 9;
  70. worw = 7;
  71. }
  72. else if (includeSpecialTime == 2) {
  73. // Compact mode.
  74. cp = true;
  75. avgw = 6;
  76. worw = 5;
  77. }
  78. for (var i = 0; i < commands.length; i++) {
  79. content += '<tr><td class="admconfitem">'
  80. + (!cp ? '<a href="' + detail + commands[i].command + '">' : '')
  81. + abbrTruncate(commands[i].command, 30)
  82. + (!cp ? '</a>' : '')
  83. + '</td><td class="admconfintvalue'
  84. + ((commands[i].currentload >= concurrencyHighlight) ? ' exceptional' : (commands[i].currentload == 0 ? ' inactive' : '')) + '">'
  85. + commands[i].currentload + '</td>'
  86. + '<td class="admconfintvalue">' + commands[i].count + '</td>'
  87. + '<td class="admconfintvalue' + (commands[i].ci != null ? '">' + commands[i].ci.count : ' inactive">--') + '</td>'
  88. + '<td></td>';
  89. totalRequests += commands[i].count;
  90. totalConcurrent += commands[i].currentload;
  91. if (commands[i].ci != null) {
  92. content += (!cp ? '<td class="admconfintvalue">' + commands[i].ci.avgdisp + '</td>' : '')
  93. + '<td class="admconfintvalue">' + commands[i].ci.avgqr + '</td>'
  94. + (!cp ? '<td class="admconfintvalue">' + commands[i].ci.avgqe + '</td>' : '')
  95. + '<td class="admconfintvalue">' + commands[i].ci.avgqt + '</td>'
  96. + (st ? '<td class="admconfintvalue">' + commands[i].ci.avgsp + '</td>' : '')
  97. + '<td class="admconfintvalue">' + commands[i].ci.avglg + '</td>'
  98. + '<td class="admconfintvalue">' + commands[i].ci.avgrn + '</td>'
  99. + '<td class="admconfintvalue">' + commands[i].ci.avgcp + '</td>'
  100. + '<td class="admconfintvalue">' + commands[i].ci.avgto + '</td>'
  101. + '<td></td><td class="admconfintvalue'
  102. + ((commands[i].ci.worqr > commands[i].ci.avgqr * exceptionalCoefficient) ? ' exceptional' : '') + '">' + commands[i].ci.worqr + '</td>'
  103. + (!cp ? '<td class="admconfintvalue' : '')
  104. + (!cp ? ((commands[i].ci.worqe > commands[i].ci.avgqe * exceptionalCoefficient) ? ' exceptional' : '') + '">' + commands[i].ci.worqe + '</td>' : '')
  105. + '<td class="admconfintvalue'
  106. + ((commands[i].ci.worqt > commands[i].ci.avgqt * exceptionalCoefficient) ? ' exceptional' : '') + '">' + commands[i].ci.worqt + '</td>'
  107. + (st ? '<td class="admconfintvalue' : '')
  108. + (st ? ((commands[i].ci.worsp > commands[i].ci.avgsp * exceptionalCoefficient) ? ' exceptional' : '') + '">' + commands[i].ci.worsp + '</td>' : '')
  109. + '<td class="admconfintvalue'
  110. + ((commands[i].ci.worlg > commands[i].ci.avglg * exceptionalCoefficient) ? ' exceptional' : '') + '">' + commands[i].ci.worlg + '</td>'
  111. + '<td class="admconfintvalue'
  112. + ((commands[i].ci.worrn > commands[i].ci.avgrn * exceptionalCoefficient) ? ' exceptional' : '') + '">' + commands[i].ci.worrn + '</td>'
  113. + '<td class="admconfintvalue'
  114. + ((commands[i].ci.worcp > commands[i].ci.avgcp * exceptionalCoefficient) ? ' exceptional' : '') + '">' + commands[i].ci.worcp + '</td>';
  115. lastHourRequests += commands[i].ci.count;
  116. totalDispatches += (commands[i].ci.count * commands[i].ci.avgdisp);
  117. totalQueries += (commands[i].ci.count * commands[i].ci.avgqr);
  118. totalQueryErrors += (commands[i].ci.count * commands[i].ci.avgqe);
  119. totalQueryTime += (commands[i].ci.count * commands[i].ci.avgqt);
  120. if (st) {
  121. totalSpecialTime += (commands[i].ci.count * commands[i].ci.avgsp);
  122. }
  123. totalLogicTime += (commands[i].ci.count * commands[i].ci.avglg);
  124. totalRenderTime += (commands[i].ci.count * commands[i].ci.avgrn);
  125. totalCpuTime += (commands[i].ci.count * commands[i].ci.avgcp);
  126. totalTotalTime += (commands[i].ci.count * commands[i].ci.avgto);
  127. if (commands[i].ci.worqr > worstQueries) {
  128. worstQueries = commands[i].ci.worqr;
  129. }
  130. if (commands[i].ci.worqe > worstQueryErrors) {
  131. worstQueryErrors = commands[i].ci.worqe;
  132. }
  133. if (commands[i].ci.worqt > worstQueryTime) {
  134. worstQueryTime = commands[i].ci.worqt;
  135. }
  136. if (commands[i].ci.worsp > worstSpecialTime) {
  137. worstSpecialTime = commands[i].ci.worsp;
  138. }
  139. if (commands[i].ci.worlg > worstLogicTime) {
  140. worstLogicTime = commands[i].ci.worlg;
  141. }
  142. if (commands[i].ci.worrn > worstRenderTime) {
  143. worstRenderTime = commands[i].ci.worrn;
  144. }
  145. if (commands[i].ci.worcp > worstCpuTime) {
  146. worstCpuTime = commands[i].ci.worcp;
  147. }
  148. }
  149. else {
  150. for (var k = 0; k < avgw; k++) {
  151. content += '<td class="admconfintvalue inactive">--</td>';
  152. }
  153. content += '<td></td>';
  154. for (var k = 0; k < worw; k++) {
  155. content += '<td class="admconfintvalue inactive">--</td>';
  156. }
  157. }
  158. if (!cp) {
  159. content += '<td></td><td class="admconfvalue">' + standardTimeString(new Date(commands[i].last.time)) + '</td>'
  160. + '<td class="admconfintvalue">' + commands[i].last.disp + '</td>'
  161. + '<td class="admconfintvalue">' + commands[i].last.queries + '</td>'
  162. + '<td class="admconfintvalue">' + commands[i].last.queryexc + '</td>'
  163. + '<td class="admconfintvalue">' + commands[i].last.querytime + '</td>'
  164. + (includeSpecialTime == 1 ? '<td class="admconfintvalue">' + commands[i].last.special + '</td>' : '')
  165. + '<td class="admconfintvalue">' + commands[i].last.logic + '</td>'
  166. + '<td class="admconfintvalue">' + commands[i].last.render + '</td>'
  167. + '<td class="admconfintvalue">' + commands[i].last.cpu + '</td>'
  168. + '<td class="admconfintvalue">' + commands[i].last.total + '</td>'
  169. + '</tr>';
  170. }
  171. }
  172. content += '<tr class="admplainsubheader"><td colspan="4">Totals</td><td></td>'
  173. + '<td colspan="' + avgw + '">Averages across all commands</td><td></td>'
  174. + '<td colspan="' + worw + '">Worst across all commands</td></tr>'
  175. + '<tr><td class="admconfitem"><b>' + commands.length + ' command' + (commands.length == 1 ? '' : 's')
  176. + '</b></td><td class="admconfintvalue">' + totalConcurrent + '</td>'
  177. + '<td class="admconfintvalue">' + totalRequests + '</td>'
  178. + '<td class="admconfintvalue">' + lastHourRequests + '</td>'
  179. + '<td></td>';
  180. if (lastHourRequests > 0) {
  181. content += (!cp ? '<td class="admconfintvalue">' + Math.floor(totalDispatches / lastHourRequests) + '</td>' : '')
  182. + '<td class="admconfintvalue">' + Math.floor(totalQueries / lastHourRequests) + '</td>'
  183. + (!cp ? '<td class="admconfintvalue">' + Math.floor(totalQueryErrors / lastHourRequests) + '</td>' : '')
  184. + '<td class="admconfintvalue">' + Math.floor(totalQueryTime / lastHourRequests) + '</td>'
  185. + (includeSpecialTime == 1 ? '<td class="admconfintvalue">' + Math.floor(totalSpecialTime / lastHourRequests) + '</td>' : '')
  186. + '<td class="admconfintvalue">' + Math.floor(totalLogicTime / lastHourRequests) + '</td>'
  187. + '<td class="admconfintvalue">' + Math.floor(totalRenderTime / lastHourRequests) + '</td>'
  188. + '<td class="admconfintvalue">' + Math.floor(totalCpuTime / lastHourRequests) + '</td>'
  189. + '<td class="admconfintvalue">' + Math.floor(totalTotalTime / lastHourRequests) + '</td>'
  190. + '<td></td>'
  191. + '<td class="admconfintvalue">' + worstQueries + '</td>'
  192. + (!cp ? '<td class="admconfintvalue">' + worstQueryErrors + '</td>' : '')
  193. + '<td class="admconfintvalue">' + worstQueryTime + '</td>'
  194. + (includeSpecialTime == 1 ? '<td class="admconfintvalue">' + worstSpecialTime + '</td>' : '')
  195. + '<td class="admconfintvalue">' + worstLogicTime + '</td>'
  196. + '<td class="admconfintvalue">' + worstRenderTime + '</td>'
  197. + '<td class="admconfintvalue">' + worstCpuTime + '</td>'
  198. + '</tr>';
  199. }
  200. else {
  201. for (var k = 0; k < avgw; k++) {
  202. content += '<td class="admconfintvalue inactive">--</td>';
  203. }
  204. content += '<td></td>';
  205. for (var k = 0; k < worw; k++) {
  206. content += '<td class="admconfintvalue inactive">--</td>';
  207. }
  208. }
  209. return content;
  210. }
  211. /** Render the current requests in the performance monitor. */
  212. function renderCurrentRequestList(requests, detail, includeSpecialTime) {
  213. var st = false, // Standard mode.
  214. cp = false, // Compact mode.
  215. cols = 8, // Width in columns.
  216. totalDispatches = 0, // Rough totals for averages across all commands.
  217. totalQueries = 0,
  218. totalQueryErrors = 0,
  219. totalQueryTime = 0,
  220. totalLogicTime = 0,
  221. totalRenderTime = 0,
  222. totalSpecialTime = 0,
  223. totalTotalTime = 0,
  224. totalCpuTime = 0,
  225. worstQueries = 0, // Worsts across all commands.
  226. worstQueryErrors = 0,
  227. worstQueryTime = 0,
  228. worstSpecialTime = 0,
  229. worstLogicTime = 0,
  230. worstRenderTime = 0,
  231. worstCpuTime = 0,
  232. content = ''; // The HTML to render.
  233. if (includeSpecialTime == 1) {
  234. // Standard mode, include ST.
  235. st = true;
  236. cols = 9;
  237. }
  238. else if (includeSpecialTime == 2) {
  239. // Compact mode.
  240. cp = true;
  241. }
  242. for (var i = 0; i < requests.length; i++) {
  243. content += '<tr><td class="admconfitem">'
  244. + (!cp ? '<a href="' + detail + requests[i].thread + '">' : '')
  245. + abbrTruncate(requests[i].command, 30)
  246. + (!cp ? '</a>' : '')
  247. + '</td><td class="admconfintvalue">' + requests[i].reqnum + '</td>'
  248. + '<td class="admconfintvalue">' + requests[i].thread + '</td>'
  249. + '<td class="admconfvalue">' + standardTimeString(new Date(requests[i].time)) + '</td>'
  250. + '<td></td>'
  251. + '<td class="admconfintvalue">' + requests[i].disp + '</td>'
  252. + '<td class="admconfintvalue">' + requests[i].queries + '</td>'
  253. + '<td class="admconfintvalue">' + requests[i].queryexc + '</td>'
  254. + '<td class="admconfintvalue">' + requests[i].querytime + ' ms</td>'
  255. + (st ? '<td class="admconfintvalue">' + requests[i].special + ' ms</td>' : '')
  256. + '<td class="admconfintvalue">' + requests[i].logic + ' ms</td>'
  257. + '<td class="admconfintvalue">' + requests[i].render + ' ms</td>'
  258. + '<td class="admconfintvalue">' + requests[i].cpu + ' ms</td>'
  259. + '<td class="admconfintvalue">' + requests[i].total + ' ms</td>'
  260. + '</tr>';
  261. totalDispatches += requests[i].disp;
  262. totalQueries += requests[i].queries;
  263. totalQueryErrors += requests[i].queryexc;
  264. totalQueryTime += requests[i].querytime;
  265. totalLogicTime += requests[i].logic;
  266. totalSpecialTime += requests[i].special;
  267. totalRenderTime += requests[i].render;
  268. totalCpuTime += requests[i].cpu;
  269. totalTotalTime += requests[i].total;
  270. }
  271. content += '<tr class="admplainsubheader"><td colspan="4">Totals</td><td></td>'
  272. + '<td colspan="' + cols + '">Total across all commands</td></tr>'
  273. + '<tr><td colspan="4" class="admconfitem"><b>' + requests.length + ' command' + (requests.length == 1 ? '' : 's')
  274. + '</b></td><td></td>';
  275. content += '<td class="admconfintvalue">' + totalDispatches + '</td>'
  276. + '<td class="admconfintvalue">' + totalQueries + '</td>'
  277. + '<td class="admconfintvalue">' + totalQueryErrors + '</td>'
  278. + '<td class="admconfintvalue">' + totalQueryTime + ' ms</td>'
  279. + (st ? '<td class="admconfintvalue">' + totalSpecialTime + ' ms</td>' : '')
  280. + '<td class="admconfintvalue">' + totalLogicTime + ' ms</td>'
  281. + '<td class="admconfintvalue">' + totalRenderTime + ' ms</td>'
  282. + '<td class="admconfintvalue">' + totalCpuTime + ' ms</td>'
  283. + '<td class="admconfintvalue">' + totalTotalTime + ' ms</td>';
  284. + '</tr>';
  285. return content;
  286. }
  287. /** Use Flot to render a chart of health over time. */
  288. function renderHealthMonitorFlot(h, timezoneShift, interval, targetElement) {
  289. var disps = new Array(),
  290. dispc = new Array(),
  291. memto = new Array(),
  292. memfr = new Array(),
  293. memus = new Array(),
  294. pages = new Array(),
  295. pagec = new Array(),
  296. quers = new Array(),
  297. querc = new Array(),
  298. threa = new Array(),
  299. block = new Array(),
  300. waits = new Array(),
  301. blcwa = new Array(),
  302. lastTime,
  303. healthOptions,
  304. healthData;
  305. // Build the series arrays.
  306. for (var i = 0; i < h.length; i++) {
  307. if (h[i] != null) {
  308. lastTime = h[i].start + timezoneShift;
  309. disps[i] = [lastTime, h[i].disps];
  310. dispc[i] = [lastTime, h[i].dispcon];
  311. if (h[i].totalmem > 0) {
  312. memto[i] = [lastTime, h[i].totalmem];
  313. memfr[i] = [lastTime, h[i].freemem];
  314. memus[i] = [lastTime, h[i].totalmem - h[i].freemem];
  315. }
  316. pages[i] = [lastTime, h[i].pages];
  317. pagec[i] = [lastTime, h[i].pagecon];
  318. quers[i] = [lastTime, h[i].queries];
  319. querc[i] = [lastTime, h[i].querycon];
  320. if (h[i].threads > 0) {
  321. threa[i] = [lastTime, h[i].threads];
  322. block[i] = [lastTime, h[i].blocked];
  323. waits[i] = [lastTime, h[i].waiting];
  324. blcwa[i] = [lastTime, h[i].waiting + h[i].blocked];
  325. }
  326. }
  327. }
  328. // Flot rendering options.
  329. healthOptions = {
  330. xaxis: { mode: "time", timeformat: "%h:%M", minTickSize: [1, "minute"] },
  331. legend: { position: "nw" },
  332. yaxis: { labelWidth: 25, min: 0, minTickSize: 1, tickDecimals: 0 },
  333. y2axis: { labelWidth: 50, min: 0, minTickSize: 1, tickDecimals: 0, tickFormatter: function(number) {
  334. return Math.floor(number / 1024 / 1024) + " M";
  335. } },
  336. selection: { mode: "x" },
  337. colors: [ "#5060F0" ],
  338. grid: { hoverable: true, backgroundColor: { colors: ["#FFF", "#FFF", "#FFF", "#F0F4F8"] },
  339. series: { stack: false } }
  340. };
  341. // Data for Flot to render.
  342. healthData =
  343. [
  344. { label: 'Used heap &gt;', color: '#B88810', data: memus, yaxis: 2 },
  345. { label: '&lt; Pages', color: '#90A0FF', data: pages },
  346. { label: '&lt; Dispatches', color: '#0000E0', data: disps },
  347. { label: '&lt; Queries', color: '#C00000', data: quers },
  348. { label: '&lt; Blocked/Waiting', color: '#009000', data: blcwa },
  349. { label: 'Total heap &gt;', color: '#C0C0C2', data: memto, yaxis: 2 }
  350. ];
  351. $.plot($(targetElement), healthData, healthOptions);
  352. }
  353. return {
  354. standardDateString: standardDateString,
  355. standardTimeString: standardTimeString,
  356. abbrTruncate: abbrTruncate,
  357. renderPerformanceMonitorList: renderPerformanceMonitorList,
  358. renderCurrentRequestList: renderCurrentRequestList,
  359. renderHealthMonitorFlot: renderHealthMonitorFlot
  360. };
  361. })();