| <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> |
| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| <html><head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| <title>Storm UI</title> |
| <link href="/css/bootstrap-3.3.1.min.css" rel="stylesheet" type="text/css"> |
| <link href="/css/jquery.dataTables.1.10.4.min.css" rel="stylesheet" type="text/css"> |
| <link href="/css/dataTables.bootstrap.css" rel="stylesheet" type="text/css"> |
| <link href="/css/jsonFormatter.min.css" rel="stylesheet" type="text/css"> |
| <link href="/css/style.css?_ts=${packageTimestamp}" rel="stylesheet" type="text/css"> |
| <script src="/js/jquery-1.11.1.min.js" type="text/javascript"></script> |
| <script src="/js/jquery.dataTables.1.10.4.min.js" type="text/javascript"></script> |
| <script src="/js/jquery.cookies.2.2.0.min.js" type="text/javascript"></script> |
| <script src="/js/jquery.mustache.js" type="text/javascript"></script> |
| <script src="/js/url.min.js" type="text/javascript"></script> |
| <script src="/js/bootstrap-3.3.1.min.js" type="text/javascript"></script> |
| <script src="/js/jquery.blockUI.min.js" type="text/javascript"></script> |
| <script src="/js/jsonFormatter.min.js" type="text/javascript"></script> |
| <script src="/js/script.js?_ts=${packageTimestamp}" type="text/javascript"></script> |
| <script src="/js/dataTables.bootstrap.min.js" type="text/javascript"></script> |
| </head> |
| <body> |
| <div class="container-fluid"> |
| <div class="row"> |
| <div class="col-md-11"> |
| <h1><a href="/">Storm UI</a></h1> |
| </div> |
| <div id="ui-user" class="col-md-1"></div> |
| </div> |
| <div class="row"> |
| <div class="col-md-12" id="search-form"> |
| </div> |
| </div> |
| <div class="row"> |
| <div class="col-md-12"> |
| <h2>Topology summary</h2> |
| <div id="topology-summary"></div> |
| </div> |
| </div> |
| <div class="row"> |
| <div class="col-md-12"> |
| <h2 id="topology-resources-header">Topology resources</h2> |
| <div id="topology-resources"></div> |
| </div> |
| </div> |
| <div class="row"> |
| <div id="topology-actions" class="col-md-12"></div> |
| </div> |
| <div class="row"> |
| <div id="topology-stats" class="col-md-12"></div> |
| </div> |
| <div class="row"> |
| <div id="topology-spouts-lag" class="col-md-12"></div> |
| </div> |
| <div class="row"> |
| <div id="spout-stats" class="col-md-12"></div> |
| </div> |
| <div class="row"> |
| <div id="bolt-stats" class="col-md-12"></div> |
| </div> |
| <div class="row"> |
| <div id="worker-stats" class="col-md-12"></div> |
| </div> |
| <div class="row"> |
| <div id="topology-visualization" class="col-md-12"></div> |
| </div> |
| <div class="row"> |
| <div id="topology-configuration" class="col-md-12"></div> |
| </div> |
| <div class="row"> |
| <div class="col-md-12"> |
| <p id="toggle-switch" style="display: block;" class="js-only"></p> |
| </div> |
| </div> |
| <div class="row"> |
| <div id="json-response-error" class="col-md-12"></div> |
| </div> |
| <div> |
| <p id="page-rendered-at-timestamp"></p> |
| </div> |
| </div> |
| </body> |
| <script> |
| |
| function jsError(other) { |
| try { |
| other(); |
| } catch (err) { |
| getStatic("/templates/json-error-template.html", function(template) { |
| $("#json-response-error").append(Mustache.render($(template).filter("#json-error-template").html(),{error: "JS Error", errorMessage: err})); |
| }); |
| } |
| } |
| |
| var toggleChangeLogLevel; |
| |
| function closeChangeLogLevel (){ |
| var container = $("#change-log-level"); |
| container.hide(); |
| } |
| |
| function clearLoggerLevel(id){ |
| $("#loggerRemove-" + id).val("true"); |
| sendLoggerLevel(id); |
| $("#logger-" + id).remove(); |
| } |
| |
| function sendLoggerLevel(id){ |
| var topologyId = $.url("?id"); |
| var shouldRemove = $("#loggerRemove-" + id).val() === "true"; |
| var level = $("#loggerLevel-" + id).val(); |
| var timeout = parseInt($("#loggerTimeout-" + id).val()); |
| timeout = isNaN(timeout) ? 0 : timeout; |
| var loggerName = $("#loggerName-" + id).val(); |
| if (level === 'Pick Level'){ |
| alert ('Please pick a valid log level'); |
| return; |
| } |
| var levelBelowInfo = level === 'DEBUG'; |
| |
| if (!shouldRemove && levelBelowInfo && timeout <= 0){ |
| timeout = parseInt ( |
| prompt ("You must provide a timeout > 0 for DEBUG log level. What timeout would you like (secs)?", 30)); |
| if (!timeout){ |
| return; |
| } |
| } |
| if (!loggerName) { |
| alert ("Logger name must be provided. Use \"ROOT\" if you want to change current default log behavior."); |
| return; |
| } |
| var data = {}; |
| var loggerSetting; |
| |
| if (id === 0) { |
| data["namedLoggerLevels"] = {}; |
| data["namedLoggerLevels"]["ROOT"] = {}; |
| loggerSetting = data["namedLoggerLevels"]["ROOT"]; |
| } else { |
| data["namedLoggerLevels"] = {}; |
| data["namedLoggerLevels"][loggerName] = {}; |
| loggerSetting = data["namedLoggerLevels"][loggerName]; |
| } |
| |
| loggerSetting.target_level = shouldRemove ? null : level; |
| loggerSetting.reset_level = "INFO"; |
| loggerSetting.timeout = timeout; |
| |
| sendRequest (topologyId, "logconfig", null, data, toggleChangeLogLevel); |
| }; |
| |
| function renderLogLevelForm (template, responseData){ |
| var topologyId = $.url("?id"); |
| var container = $("#change-log-level"); |
| |
| var levels = [ |
| {name: "Pick Level"}, |
| {name: "ALL"}, |
| {name: "TRACE"}, |
| {name: "DEBUG"}, |
| {name: "INFO" }, |
| {name: "WARN" }, |
| {name: "ERROR"}, |
| {name: "FATAL"}, |
| {name: "OFF"} |
| ]; |
| var partialTemplates = $(template).filter('.partials'); |
| var partials = {}; |
| |
| $.each(partialTemplates, function (ix, partial){ |
| var obj = $(partial); |
| partials [obj.attr('id')] = obj.html(); |
| }); |
| |
| var logLevelTemplate = $(template).filter("#topology-change-log-level-template").html(); |
| |
| var calcAbsoluteTimeout = function (timeout_epoch) { |
| var absoluteTimeout = ""; |
| if (timeout_epoch) { |
| var d = new Date(0); |
| d.setUTCSeconds(timeout_epoch / 1000); |
| absoluteTimeout = d.toLocaleDateString() + " " + d.toLocaleTimeString(); |
| } |
| return absoluteTimeout; |
| }; |
| var renderImpl = function (data){ |
| var loggers = []; |
| var loggerCount = 1; |
| var obj = data.namedLoggerLevels; |
| if (!obj) { |
| obj = {}; |
| data.namedLoggerLevels = obj; |
| } |
| |
| var sortedLoggers = Object.keys(obj).sort(function (l1, l2){ |
| if (l1 === "ROOT") return -1; |
| if (l2 === "ROOT") return 1; |
| return l1 > l2; |
| }); |
| |
| sortedLoggers.forEach (function (l){ |
| var obj = data.namedLoggerLevels[l]; |
| obj.loggerId = loggerCount++; |
| obj.loggerName = l; |
| obj.named = l != "ROOT"; |
| obj.cls = "namedLoggers"; |
| obj.levelSelected = function (obj){ |
| return function (){ |
| return this.name === obj.target_level ? "selected" : ""; |
| } |
| }(obj); |
| obj.absoluteTimeout = calcAbsoluteTimeout (obj.timeout_epoch); |
| obj.canClear = true; |
| loggers.push(obj); |
| }); |
| |
| loggers.push({ |
| loggerId: loggerCount, |
| isNew: true, |
| cls: 'newLogger' |
| }); |
| |
| var tmplData = { |
| loggers: loggers, |
| levels: levels |
| }; |
| |
| container.html(Mustache.render(logLevelTemplate, tmplData, partials)); |
| container.show('fast'); |
| }; |
| if (!responseData) { |
| var topologyId = $.url("?id"); |
| $.get('/api/v1/topology/' + topologyId + '/logconfig', renderImpl); |
| } else { |
| renderImpl (responseData); |
| } |
| } |
| $(document).ajaxStop($.unblockUI); |
| $(document).ajaxStart(function(){ |
| if ($("#topology-visualization").children().size() == 0) { |
| $.blockUI({ message: '<img src="images/spinner.gif" /> <h3>Loading topology summary...</h3>'}); |
| } |
| }); |
| $(document).ready(function() { |
| var topologyId = $.url("?id"); |
| var tableStateKey = ":".concat(topologyId); |
| var window = $.url("?window"); |
| var sys = $.cookies.get("sys") || "false"; |
| var url = "/api/v1/topology/"+topologyId+"?sys="+sys; |
| if(window) url += "&window="+window; |
| $.ajaxSetup({ |
| "error":function(jqXHR,textStatus,response) { |
| var errorJson = jQuery.parseJSON(jqXHR.responseText); |
| getStatic("/templates/json-error-template.html", function(template) { |
| $("#json-response-error").append(Mustache.render($(template).filter("#json-error-template").html(),errorJson)); |
| }); |
| } |
| }); |
| |
| $.getJSON("/api/v1/cluster/configuration",function(response,status,jqXHR) { |
| $.extend( $.fn.dataTable.defaults, { |
| stateSave: true, |
| stateSaveCallback: function (oSettings, oData) { |
| sessionStorage.setItem( oSettings.sTableId.concat(tableStateKey), JSON.stringify(oData) ); |
| }, |
| stateLoadCallback: function (oSettings) { |
| return JSON.parse( sessionStorage.getItem(oSettings.sTableId.concat(tableStateKey)) ); |
| }, |
| lengthMenu: [[20,40,60,100,-1], [20, 40, 60, 100, "All"]], |
| pageLength: response["ui.pagination"] |
| }); |
| }); |
| |
| renderToggleSys($("#toggle-switch")); |
| |
| $.getJSON(url,function(response,status,jqXHR) { |
| var uiUser = $("#ui-user"); |
| getStatic("/templates/user-template.html", function(template) { |
| uiUser.append(Mustache.render($(template).filter("#user-template").html(),response)); |
| $('#ui-user [data-toggle="tooltip"]').tooltip(); |
| }); |
| |
| var topologySummary = $("#topology-summary"); |
| var topologyResources = $("#topology-resources"); |
| var topologyStats = $("#topology-stats"); |
| var topologySpoutsLag = $("#topology-spouts-lag"); |
| var spoutStats = $("#spout-stats"); |
| var boltStats = $("#bolt-stats"); |
| var workerStats = $("#worker-stats"); |
| var config = $("#topology-configuration"); |
| var topologyActions = $("#topology-actions"); |
| var topologyVisualization = $("#topology-visualization") |
| var searchForm = $("#search-form") |
| var formattedConfig = formatConfigData(response["configuration"]); |
| var buttonJsonData = topologyActionJson(response["id"],response["encodedId"],response["name"],response["status"] |
| ,response["msgTimeout"],response["configuration"]["topology.eventlogger.executors"],response["debug"],response["samplingPct"]); |
| $.ajax ({url: "/templates/topology-page-template.html", success: function(template) { |
| toggleChangeLogLevel = function (data) { |
| renderLogLevelForm (template, data); |
| } |
| searchForm.append(Mustache.render($(template).filter("#search-form-template").html(),{id: topologyId})); |
| topologySummary.append(Mustache.render($(template).filter("#topology-summary-template").html(),response)); |
| topologyResources.append(Mustache.render($(template).filter("#topology-resources-template").html(),response)); |
| var displayResource = response["schedulerDisplayResource"]; |
| if (!displayResource){ |
| $('#topology-resources-header').hide(); |
| $('#topology-resources').hide(); |
| } |
| topologyActions.append(Mustache.render($(template).filter("#topology-actions-template").html(),buttonJsonData)); |
| topologyStats.append(Mustache.render($(template).filter("#topology-stats-template").html(),response)); |
| //window, emitted, transferred, complete latency, acked, failed |
| $("#topology-stats-table").DataTable({ |
| paging: false, |
| info: false, |
| searching: false, |
| columnDefs: [ |
| {type: "num", targets: [1, 2, 3, 4, 5]}, |
| {type: "time-str", targets: [0]} |
| ] |
| }); |
| |
| spoutStats.append(Mustache.render($(template).filter("#spout-stats-template").html(),response)); |
| dtAutoPage("#spout-stats-table", { |
| columnDefs: [ |
| {type: "num", targets: 'table-num'} |
| ] |
| }); |
| |
| boltStats.append(Mustache.render($(template).filter("#bolt-stats-template").html(),response)); |
| dtAutoPage("#bolt-stats-table", { |
| columnDefs: [ |
| {type: "num", targets: 'table-num'} |
| ] |
| }); |
| |
| jsError(function() { |
| workerStats.append(Mustache.render($(template).filter("#worker-stats-template").html(),response)); |
| makeTopologyWorkerStatsTable (response, '#worker-stats-table', '#worker-stats'); |
| }); |
| |
| jsError(function() { |
| topologyVisualization.append(Mustache.render($(template).filter("#topology-visualization-template").html(), response)); |
| var sys = $.cookies.get("sys") || "false"; |
| $("#show-hide-visualization").click(function () { show_visualization(sys) }); |
| $("#open-visualization").click(function() { open_visualization(sys); }); |
| |
| config.append(Mustache.render($(template).filter("#topology-configuration-template").html(),formattedConfig)); |
| $('#topology-configuration td').jsonFormatter() |
| //key, value |
| dtAutoPage("#topology-configuration-table", {}); |
| |
| var errorCells = document.getElementsByClassName("errorSpan"); |
| for (i =0; i < errorCells.length; i++) |
| { |
| var timeLapsedInSecs = errorCells[i].id; |
| if (parseInt(timeLapsedInSecs) < 1800) { |
| errorCells[i].style.color = "#9d261d"; |
| errorCells[i].style.borderBottomColor = "#9d261d"; |
| } |
| errorCells[i].style.whiteSpace = "pre"; |
| } |
| |
| var errorTime = document.getElementsByClassName("errorTime"); |
| for (i=0; i < errorTime.length; i++) |
| { |
| if((errorTime[i].id)) |
| { |
| var a = new Date(parseInt(errorTime[i].id) * 1000); |
| var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; |
| var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat']; |
| var year = a.getFullYear(); |
| var month = months[a.getMonth()]; |
| var date = a.getDate(); |
| var hour = a.getHours(); |
| var min = a.getMinutes(); |
| var sec = a.getSeconds(); |
| var day = days[a.getDay()]; |
| if (hour < 10) {hour = "0"+hour;} |
| if (min < 10) {min = "0"+min;} |
| if (sec < 10) {sec = "0"+sec;} |
| var time = day + ', '+date + ' ' + month + ' ' + year + ' ' + hour + ':' + min + ':' + sec; |
| |
| errorTime[i].innerHTML = time; |
| var sec_num = parseInt(errorTime[i].title, 10); |
| var hours = Math.floor(sec_num / 3600); |
| var minutes = Math.floor((sec_num - (hours * 3600)) / 60); |
| var seconds = sec_num - (hours * 3600) - (minutes * 60); |
| if (hours < 10) {hours = "0"+hours;} |
| if (minutes < 10) {minutes = "0"+minutes;} |
| if (seconds < 10) {seconds = "0"+seconds;} |
| var time = hours+':'+minutes+':'+seconds; |
| errorTime[i].title = "Elapsed Time Since Error: " + time; |
| } |
| } |
| $('#topology-summary [data-toggle="tooltip"]').tooltip(); |
| $('#topology-stats [data-toggle="tooltip"]').tooltip(); |
| $('#spout-stats [data-toggle="tooltip"]').tooltip(); |
| $('#bolt-stats [data-toggle="tooltip"]').tooltip(); |
| $('#topology-configuration [data-toggle="tooltip"]').tooltip(); |
| $('#topology-actions [data-toggle="tooltip"]').tooltip(); |
| $('#topology-visualization [data-toggle="tooltip"]').tooltip(); |
| |
| var lagUrl = "/api/v1/topology/"+topologyId+"/lag"; |
| $.getJSON(lagUrl,function(lagResponse,status,jqXHR) { |
| if (lagResponse !== null && lagResponse !== undefined) { |
| var kafkaSpoutsLagTemplate = $(template).filter("#topology-kafka-spouts-lag-template").html(); |
| var spoutsErrorTemplate = $(template).filter("#topology-spouts-lag-error-template").html(); |
| |
| var data = {}; |
| data.kafkaSpoutsLagResults = []; |
| data.spoutsLagErrorResults = []; |
| for (var spoutId in lagResponse) { |
| var spout = lagResponse[spoutId]; |
| var spoutType = spout.spoutType; |
| if (spoutType !== "KAFKA") { |
| continue; |
| } |
| var spoutLagResult = spout.spoutLagResult; |
| var errorInfo = spout.errorInfo; |
| if (spoutLagResult !== undefined) { |
| for (var topicName in spoutLagResult) { |
| var topicLagResult = spoutLagResult[topicName]; |
| for (var partitionId in topicLagResult) { |
| var partitionLagResult = topicLagResult[partitionId]; |
| data.kafkaSpoutsLagResults.push({ |
| id: spoutId, |
| topic: topicName, |
| partition: partitionId, |
| logHeadOffset: partitionLagResult.logHeadOffset, |
| consumerCommittedOffset: partitionLagResult.consumerCommittedOffset, |
| lag: partitionLagResult.lag |
| }); |
| } |
| } |
| } else if (errorInfo !== undefined) { |
| data.spoutsLagErrorResults.push({ |
| spoutId: spoutId, |
| spoutType: spout.spoutType, |
| errorInfo: errorInfo |
| }); |
| } |
| } |
| |
| if (data.kafkaSpoutsLagResults.length > 0) { |
| topologySpoutsLag.append(Mustache.render(kafkaSpoutsLagTemplate, data)); |
| } |
| if (data.spoutsLagErrorResults.length > 0) { |
| topologySpoutsLag.append(Mustache.render(spoutsErrorTemplate, data)); |
| } |
| } |
| }); |
| }); |
| }}); |
| }); |
| }); |
| |
| getPageRenderedTimestamp("page-rendered-at-timestamp"); |
| |
| </script> |
| </html> |
| |