mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-24 05:04:05 -08:00
remove old UI (#10208)
* remove old ui Signed-off-by: Augustin Husson <husson.augustin@gmail.com> * add coments and removed unused struct Signed-off-by: Augustin Husson <husson.augustin@gmail.com> * removed tplFunc Signed-off-by: Augustin Husson <husson.augustin@gmail.com>
This commit is contained in:
parent
7321a97133
commit
727cdbff4c
|
@ -92,11 +92,6 @@ const Navigation: FC<NavbarProps> = ({ consolesLink, agentMode }) => {
|
|||
<NavItem>
|
||||
<NavLink href="https://prometheus.io/docs/prometheus/latest/getting_started/">Help</NavLink>
|
||||
</NavItem>
|
||||
{!agentMode && (
|
||||
<NavItem>
|
||||
<NavLink href={`${pathPrefix}/classic/graph${window.location.search}`}>Classic UI</NavLink>
|
||||
</NavItem>
|
||||
)}
|
||||
</Nav>
|
||||
</Collapse>
|
||||
<ThemeToggle />
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
.alert_header {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.alert_details {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.show-annotations {
|
||||
font-size: 0.8em;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
div.show-annotations:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.show-annotations button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
div.show-annotations.is-checked {
|
||||
color: #286090;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
.btn {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#config_yaml {
|
||||
display: block;
|
||||
padding: 9.5px;
|
||||
font-size: 13px;
|
||||
color:#333;
|
||||
word-break: break-all;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.eval_stats {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
div.query-history {
|
||||
font-size: 0.8em;
|
||||
padding-top: 1em;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.query-history:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.query-history button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
div.query-history.is-checked {
|
||||
color: #286090;
|
||||
}
|
||||
|
||||
div.page-options {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.graph_wrapper {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.graph {
|
||||
position: relative;
|
||||
left: 40px;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
#add_graph {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.nav-tabs > li > a {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.tab-pane {
|
||||
border-left: 1px solid #ddd;
|
||||
border-right: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.graph svg {
|
||||
border-top: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin: 0 0 0 60px;
|
||||
}
|
||||
|
||||
.graph_area {
|
||||
position: relative;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
margin: 5px 0 5px 20px;
|
||||
}
|
||||
|
||||
.y_axis {
|
||||
overflow: visible;
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
bottom: 0;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.y_axis svg {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.graph .detail .item.active {
|
||||
line-height: 1.4em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.labels {
|
||||
font-size: 11px;
|
||||
line-height: 11px;
|
||||
}
|
||||
|
||||
.graph .detail .item .detail_swatch {
|
||||
float: right;
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin: 2px 2px 0 8px;
|
||||
}
|
||||
|
||||
input[title]:hover:after {
|
||||
content: attr(title);
|
||||
}
|
||||
|
||||
.config input, .config select {
|
||||
height: 12px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.config label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.console_table {
|
||||
margin: 5px 0px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
select[name="insert_metric"] {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.datepicker {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
input[name="end_input"], input[name="range_input"] {
|
||||
margin-left: -1px;
|
||||
margin-right: -1px;
|
||||
}
|
||||
|
||||
.error, .warning {
|
||||
padding: 5px;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.btn, .form-control, .nav-tabs > li > a {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.prometheus_input_group {
|
||||
display: inline-block;
|
||||
margin: 10px 5px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.prometheus_input_group.range_input {
|
||||
margin-left: 59px;
|
||||
}
|
||||
|
||||
.prometheus_input_group .btn {
|
||||
font-size: 0.8em;
|
||||
border-color: #ccc;
|
||||
}
|
||||
|
||||
.prometheus_input_group .timepicker-picker .btn {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.prometheus_input_group .input {
|
||||
width: 100px;
|
||||
|
||||
padding: 5px 12px 6px 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.42857143;
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #ccc;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
||||
-webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
|
||||
-o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
}
|
||||
|
||||
.prometheus_input_group .date_input {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.expression_input {
|
||||
width: 100% !important;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.expression_select {
|
||||
width: 220px !important;
|
||||
margin-left: 7px;
|
||||
}
|
||||
|
||||
.graph_container .rickshaw_legend {
|
||||
background-color: #222222;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
li.active, .dropdown-item:focus, .dropdown-item:hover {
|
||||
background-color: steelblue;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button.new_ui_button {
|
||||
float: right;
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
html {
|
||||
/* https://github.com/prometheus/prometheus/issues/7434 */
|
||||
/* Scroll to hash-fragment-links counting the fixed navbar 40px tall + 16px padding */
|
||||
scroll-padding-top: 56px;
|
||||
}
|
||||
|
||||
/* Move down content because we have a fixed navbar that is 50px tall with 20px padding */
|
||||
body {
|
||||
padding-top: 70px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.state_indicator {
|
||||
padding: 0 4px 0 4px;
|
||||
}
|
||||
|
||||
.literal_output td {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tooltip-inner {
|
||||
max-width: none;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.label {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* The navbar adds horizontal padding already */
|
||||
.navbar .container-fluid {
|
||||
padding: 0;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
table th td {
|
||||
border: 1px solid black;
|
||||
}
|
||||
table th tr td h2 {
|
||||
font-size: 26px;
|
||||
}
|
||||
.rule_cell {
|
||||
white-space: pre-wrap;
|
||||
background-color: #F5F5F5;
|
||||
display: block;
|
||||
font-family: monospace;
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
h2.job_header {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h2.danger a {
|
||||
color: rgb(242, 65, 65);
|
||||
}
|
||||
|
||||
.options-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.options-container .btn {
|
||||
border-radius: 0px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.table-container button.targets {
|
||||
padding: 0.3em;
|
||||
font-size: 0.6em;
|
||||
border-radius: 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.table-container table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-container table tr td {
|
||||
height: auto;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
td.endpoint, td.labels {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
td.state, td.last-scrape {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
td.errors {
|
||||
width: 30%;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 847 B |
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
|
@ -1,71 +0,0 @@
|
|||
function init() {
|
||||
$(".alert_header").click(function() {
|
||||
var expanderIcon = $(this).find("i.icon-chevron-down");
|
||||
if (expanderIcon.length !== 0) {
|
||||
expanderIcon.removeClass("icon-chevron-down").addClass("icon-chevron-up");
|
||||
} else {
|
||||
var collapserIcon = $(this).find("i.icon-chevron-up");
|
||||
collapserIcon.removeClass("icon-chevron-up").addClass("icon-chevron-down");
|
||||
}
|
||||
$(this).next().toggle();
|
||||
});
|
||||
|
||||
$("div.show-annotations").click(function() {
|
||||
const targetEl = $('div.show-annotations');
|
||||
const icon = $(targetEl).children('i');
|
||||
|
||||
if (icon.hasClass('glyphicon-unchecked')) {
|
||||
$(".alert_annotations").show();
|
||||
$(".alert_annotations_header").show();
|
||||
$(targetEl).children('i').removeClass('glyphicon-unchecked').addClass('glyphicon-check');
|
||||
targetEl.addClass('is-checked');
|
||||
} else if (icon.hasClass('glyphicon-check')) {
|
||||
$(".alert_annotations").hide();
|
||||
$(".alert_annotations_header").hide();
|
||||
$(targetEl).children('i').removeClass('glyphicon-check').addClass('glyphicon-unchecked');
|
||||
targetEl.removeClass('is-checked');
|
||||
}
|
||||
});
|
||||
|
||||
function displayAlerts(alertState, shouldDisplay) {
|
||||
$("#alertsTable > tbody > tr." + alertState).each(function(_, container) {
|
||||
$(container).toggle(shouldDisplay);
|
||||
});
|
||||
}
|
||||
|
||||
if(localStorage.hideInactiveAlerts === "true") {
|
||||
$("#inactiveAlerts").parent().removeClass("active");
|
||||
displayAlerts("alert-success", false);
|
||||
}
|
||||
if(localStorage.hidePendingAlerts === "true") {
|
||||
$("#pendingAlerts").parent().removeClass("active");
|
||||
displayAlerts("alert-warning", false);
|
||||
}
|
||||
if(localStorage.hideFiringAlerts === "true") {
|
||||
$("#firingAlerts").parent().removeClass("active");
|
||||
displayAlerts("alert-danger", false);
|
||||
}
|
||||
|
||||
$("#alertFilters :input").change(function() {
|
||||
const target = $(this).attr("id");
|
||||
var shouldHide = $(this).parent().hasClass("active");
|
||||
var checkClass = shouldHide ? 'unchecked' : 'check';
|
||||
$(this).parent().find('i.glyphicon')
|
||||
.removeClass("glyphicon-check")
|
||||
.removeClass("glyphicon-unchecked")
|
||||
.addClass("glyphicon-" + checkClass);
|
||||
if (target === "inactiveAlerts") {
|
||||
localStorage.setItem("hideInactiveAlerts", shouldHide);
|
||||
displayAlerts("alert-success", !shouldHide);
|
||||
} else if (target === "pendingAlerts") {
|
||||
localStorage.setItem("hidePendingAlerts", shouldHide);
|
||||
displayAlerts("alert-warning", !shouldHide);
|
||||
} else if (target === "firingAlerts") {
|
||||
localStorage.setItem("hideFiringAlerts", shouldHide);
|
||||
displayAlerts("alert-danger", !shouldHide);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$(init);
|
|
@ -1,12 +0,0 @@
|
|||
function init() {
|
||||
$("#copyToClipboard").on("click", function () {
|
||||
var range = document.createRange();
|
||||
range.selectNode(document.getElementById("config_yaml"));
|
||||
window.getSelection().empty();
|
||||
window.getSelection().addRange(range);
|
||||
document.execCommand("copy");
|
||||
window.getSelection().empty();
|
||||
});
|
||||
}
|
||||
|
||||
$(init);
|
|
@ -1,164 +0,0 @@
|
|||
<div id="graph_wrapper{{id}}" class="graph_wrapper">
|
||||
<form class="query_form">
|
||||
<div class="form-row">
|
||||
<div class="col-lg-10">
|
||||
<textarea rows="1" placeholder="Expression (press Shift+Enter for newlines)" name="expr" id="expr{{id}}" class="form-control expression_input" data-provide="typeahead" autocomplete="off" spellcheck="false">{{expr}}</textarea>
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<div class="eval_stats float-right"></div>
|
||||
<img src="{{ pathPrefix }}/classic/static/img/ajax-loader.gif?v={{ buildVersion }}" class="spinner" alt="ajax_spinner">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
<input class="btn btn-primary execute_btn" type="submit" value="Execute" name="submit">
|
||||
<select class="custom-select form-control expression_select" name="insert_metric">
|
||||
<option value="">- insert metric at cursor -</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="col-lg-12">
|
||||
<div class="error alert alert-danger"></div>
|
||||
<div class="warning alert alert-warning"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
TODO: Convert this to Bootstrap navbar. This requires JavaScript
|
||||
refresh.
|
||||
-->
|
||||
<div class="form-row">
|
||||
<div class="col-lg-12 text-right">
|
||||
<a name="remove" href="#">Remove Graph</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="col-lg-12">
|
||||
<div class="list-group" role="tabpanel">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item" role="presentation"><a class="nav-link" href="#graph{{id}}" aria-controls="graph{{id}}" role="tab" data-toggle="tab">Graph</a></li>
|
||||
<li class="nav-item" role="presentation" class="active"><a class="nav-link" href="#console{{id}}" aria-controls="console{{id}}" role="tab" data-toggle="tab">Console</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane graph_container reload" id="graph{{id}}">
|
||||
<div class="clearfix">
|
||||
<!-- Extracted to force grouped inputs. -->
|
||||
<div class="prometheus_input_group range_input pull-left">
|
||||
<button
|
||||
class="btn btn-light pull-left"
|
||||
type="button"
|
||||
name="dec_range"
|
||||
title="Shrink the time range.">
|
||||
<i class="glyphicon glyphicon-minus"></i>
|
||||
</button><!--
|
||||
--><input
|
||||
class="pull-left input"
|
||||
id="range_input{{id}}"
|
||||
title="Time range of graph"
|
||||
type="text"
|
||||
name="range_input"
|
||||
size="3"
|
||||
value="{{range_input}}"><!--
|
||||
--><button
|
||||
class="btn btn-light pull-left"
|
||||
type="button"
|
||||
name="inc_range"
|
||||
title="Grow the time range.">
|
||||
<i class="glyphicon glyphicon-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Extracted to force grouped inputs. -->
|
||||
<div class="prometheus_input_group pull-left">
|
||||
|
||||
<button
|
||||
class="btn btn-light pull-left"
|
||||
type="button"
|
||||
name="dec_end"
|
||||
title="Rewind the end time.">
|
||||
<i class="glyphicon glyphicon-backward"></i>
|
||||
</button><!--
|
||||
|
||||
--><input
|
||||
class="pull-left date_input input"
|
||||
id="end{{id}}"
|
||||
title="End time of graph (UTC)"
|
||||
placeholder="Until"
|
||||
data-format="yyyy-MM-dd"
|
||||
type="text"
|
||||
name="end_input"
|
||||
size="16"
|
||||
value="{{end}}"><!--
|
||||
|
||||
--><button
|
||||
class="btn btn-light pull-left"
|
||||
type="button"
|
||||
name="inc_end"
|
||||
title="Advance the end time.">
|
||||
<i class="glyphicon glyphicon-forward"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="prometheus_input_group pull-left">
|
||||
<input class="input" title="Resolution in seconds" placeholder="Res. (s)" type="text" name="step_input" id="step_input{{id}}" value="{{step_input}}" size="6">
|
||||
</div>
|
||||
|
||||
<div class="prometheus_input_group pull-left">
|
||||
<button type="button" class="btn btn-light stacked_btn">
|
||||
<i class="glyphicon"></i> stacked
|
||||
</button>
|
||||
<input type="hidden" name="stacked" value="{{stacked}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="graph_area"></div>
|
||||
<div class="legend"></div>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane active console reload" id="console{{id}}">
|
||||
<div class="clearfix">
|
||||
<!-- Extracted to force grouped inputs. -->
|
||||
<div class="prometheus_input_group pull-left">
|
||||
|
||||
<button
|
||||
class="btn btn-light pull-left"
|
||||
type="button"
|
||||
name="dec_moment"
|
||||
title="Rewind the moment.">
|
||||
<i class="glyphicon glyphicon-backward"></i>
|
||||
</button>
|
||||
|
||||
<input
|
||||
class="pull-left date_input input"
|
||||
id="moment{{id}}"
|
||||
title="Moment of console (UTC)"
|
||||
placeholder="Moment"
|
||||
data-format="yyyy-MM-dd"
|
||||
type="text"
|
||||
name="moment_input"
|
||||
size="16"
|
||||
value="{{moment}}">
|
||||
|
||||
<button
|
||||
class="btn btn-light pull-left"
|
||||
type="button"
|
||||
name="inc_moment"
|
||||
title="Advance the moment.">
|
||||
<i class="glyphicon glyphicon-forward"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-sm table-hover console_table">
|
||||
<thead>
|
||||
<th>Element</th>
|
||||
<th>Value</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td colspan="2"><i>no data</i></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
File diff suppressed because it is too large
Load diff
|
@ -1,105 +0,0 @@
|
|||
function toggleJobTable(button, shouldExpand){
|
||||
if (button.length === 0) { return; }
|
||||
|
||||
if (shouldExpand) {
|
||||
button.removeClass("collapsed-table").addClass("expanded-table").html("show less");
|
||||
} else {
|
||||
button.removeClass("expanded-table").addClass("collapsed-table").html("show more");
|
||||
}
|
||||
|
||||
button.parents(".table-container").find("table").toggle(shouldExpand);
|
||||
button.parents(".table-container").find(".collapsed-element").toggle(shouldExpand);
|
||||
}
|
||||
|
||||
function showAll(_, container) {
|
||||
$(container).show();
|
||||
}
|
||||
|
||||
function showUnhealthy(_, container) {
|
||||
const isHealthy = $(container).find("h2").attr("class").indexOf("danger") < 0;
|
||||
if (isHealthy) { $(container).hide(); }
|
||||
}
|
||||
|
||||
var allCollapsed = false;
|
||||
|
||||
function init() {
|
||||
if ($("#unhealthy-targets").length) {
|
||||
if (!localStorage.selectedTargetsTab || localStorage.selectedTargetsTab == "all-targets") {
|
||||
$("#all-targets").parent().addClass("active");
|
||||
$(".table-container").each(showAll);
|
||||
} else if (localStorage.selectedTargetsTab == "unhealthy-targets") {
|
||||
$("#unhealthy-targets").parent().addClass("active");
|
||||
$(".table-container").each(showUnhealthy);
|
||||
}
|
||||
} else {
|
||||
$(".table-container").each(showAll);
|
||||
}
|
||||
|
||||
$("button.targets").click(function() {
|
||||
const tableTitle = $(this).closest("h2").find("a").attr("id");
|
||||
|
||||
if ($(this).hasClass("collapsed-table")) {
|
||||
localStorage.setItem(tableTitle, "expanded");
|
||||
toggleJobTable($(this), true);
|
||||
} else if ($(this).hasClass("expanded-table")) {
|
||||
localStorage.setItem(tableTitle, "collapsed");
|
||||
toggleJobTable($(this), false);
|
||||
}
|
||||
});
|
||||
|
||||
$(".collapse-all").click(function() {
|
||||
|
||||
// locally store state of allCollapsed
|
||||
previousAllCollapsed = allCollapsed;
|
||||
|
||||
// conditionally change the text of the button
|
||||
if (allCollapsed == false) {
|
||||
$(this).html("Expand All");
|
||||
allCollapsed = true;
|
||||
} else {
|
||||
$(this).html("Collapse All");
|
||||
allCollapsed = false;
|
||||
}
|
||||
|
||||
$("button.targets").each(function(_, thisButton) {
|
||||
const tableTitle = $(thisButton).closest("h2").find("a").attr("id");
|
||||
|
||||
if (previousAllCollapsed == false) {
|
||||
|
||||
// collapse all the jobs
|
||||
if ($(this).hasClass("expanded-table")) {
|
||||
localStorage.setItem(tableTitle, "collapsed");
|
||||
toggleJobTable($(thisButton), false);
|
||||
}
|
||||
} else {
|
||||
|
||||
// expand all the jobs
|
||||
if ($(this).hasClass("collapsed-table")) {
|
||||
localStorage.setItem(tableTitle, "expanded");
|
||||
toggleJobTable($(this), true);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".job_header a").each(function (_, link) {
|
||||
const cachedTableState = localStorage.getItem($(link).attr("id"));
|
||||
if (cachedTableState === "collapsed") {
|
||||
toggleJobTable($(this).siblings("button"), false);
|
||||
}
|
||||
});
|
||||
|
||||
$("#showTargets :input").change(function() {
|
||||
const target = $(this).attr("id");
|
||||
|
||||
if (target === "all-targets") {
|
||||
$(".table-container").each(showAll);
|
||||
localStorage.setItem("selectedTargetsTab", "all-targets");
|
||||
} else if (target === "unhealthy-targets") {
|
||||
$(".table-container").each(showUnhealthy);
|
||||
localStorage.setItem("selectedTargetsTab", "unhealthy-targets");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(init);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
182
web/ui/static/vendor/fuzzy/fuzzy.js
vendored
182
web/ui/static/vendor/fuzzy/fuzzy.js
vendored
|
@ -1,182 +0,0 @@
|
|||
/*
|
||||
* Fuzzy
|
||||
* https://github.com/myork/fuzzy
|
||||
*
|
||||
* Copyright (c) 2012 Matt York
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* A slightly modified version of https://github.com/mattyork/fuzzy/blob/3613086aa40c180ca722aeaf48cef575dc57eb5d/lib/fuzzy.js
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
var root = this;
|
||||
|
||||
var fuzzy = {};
|
||||
|
||||
// Use in node or in browser
|
||||
if (typeof exports !== 'undefined') {
|
||||
module.exports = fuzzy;
|
||||
} else {
|
||||
root.fuzzy = fuzzy;
|
||||
}
|
||||
|
||||
// Return all elements of `array` that have a fuzzy
|
||||
// match against `pattern`.
|
||||
fuzzy.simpleFilter = function(pattern, array) {
|
||||
return array.filter(function(str) {
|
||||
return fuzzy.test(pattern, str);
|
||||
});
|
||||
};
|
||||
|
||||
// Does `pattern` fuzzy match `str`?
|
||||
fuzzy.test = function(pattern, str) {
|
||||
return fuzzy.match(pattern, str) !== null;
|
||||
};
|
||||
|
||||
// If `pattern` matches `str`, wrap each matching character
|
||||
// in `opts.pre` and `opts.post`. If no match, return null
|
||||
fuzzy.match = function(pattern, str, opts, _fromIndex) {
|
||||
opts = opts || {};
|
||||
var patternIdx = 0
|
||||
, result = []
|
||||
, len = str.length
|
||||
, fromIndex = _fromIndex || 0
|
||||
, totalScore = 0
|
||||
, currScore = 0
|
||||
// prefix
|
||||
, pre = opts.pre || ''
|
||||
// suffix
|
||||
, post = opts.post || ''
|
||||
// String to compare against. This might be a lowercase version of the
|
||||
// raw string
|
||||
, compareString = opts.caseSensitive && str || str.toLowerCase()
|
||||
, ch;
|
||||
|
||||
pattern = opts.caseSensitive && pattern || pattern.toLowerCase();
|
||||
|
||||
// If there's an exact match, add pre/post, max out score and skip the lookup
|
||||
if (compareString === pattern) {
|
||||
return {
|
||||
rendered: pre + compareString.split('').join(post+pre) + post,
|
||||
score: Infinity
|
||||
};
|
||||
}
|
||||
|
||||
// For each character in the string, either add it to the result
|
||||
// or wrap in template if it's the next string in the pattern
|
||||
for(var idx = 0; idx < len; idx++) {
|
||||
ch = str[idx];
|
||||
if(idx >= fromIndex && compareString[idx] === pattern[patternIdx]) {
|
||||
ch = pre + ch + post;
|
||||
patternIdx += 1;
|
||||
|
||||
// consecutive characters should increase the score more than linearly
|
||||
currScore += 1 + currScore;
|
||||
} else {
|
||||
currScore = 0;
|
||||
}
|
||||
totalScore += currScore;
|
||||
result[result.length] = ch;
|
||||
}
|
||||
|
||||
// return rendered string if we have a match for every char
|
||||
if(patternIdx === pattern.length) {
|
||||
var nextPossible = str.indexOf(pattern[0], str.indexOf(pattern[0], fromIndex) + 1)
|
||||
, candidate;
|
||||
|
||||
// If possible, try to find a better match at the rest of the string
|
||||
if (nextPossible > -1 && str.length - nextPossible >= pattern.length) {
|
||||
var candidate = fuzzy.match(pattern, str, opts, nextPossible);
|
||||
}
|
||||
|
||||
return candidate && candidate.score > totalScore ? candidate : {
|
||||
rendered: result.join(''), score: totalScore
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// The normal entry point. Filters `arr` for matches against `pattern`.
|
||||
// It returns an array with matching values of the type:
|
||||
//
|
||||
// [{
|
||||
// string: '<b>lah' // The rendered string
|
||||
// , index: 2 // The index of the element in `arr`
|
||||
// , original: 'blah' // The original element in `arr`
|
||||
// }]
|
||||
//
|
||||
// `opts` is an optional argument bag. Details:
|
||||
//
|
||||
// opts = {
|
||||
// // string to put before a matching character
|
||||
// pre: '<b>'
|
||||
//
|
||||
// // string to put after matching character
|
||||
// , post: '</b>'
|
||||
//
|
||||
// // Optional function. Input is an entry in the given arr`,
|
||||
// // output should be the string to test `pattern` against.
|
||||
// // In this example, if `arr = [{crying: 'koala'}]` we would return
|
||||
// // 'koala'.
|
||||
// , extract: function(arg) { return arg.crying; }
|
||||
// }
|
||||
fuzzy.filter = function(pattern, arr, opts) {
|
||||
if(!arr || arr.length === 0) {
|
||||
return [];
|
||||
}
|
||||
if (typeof pattern !== 'string' || pattern === '') {
|
||||
return arr;
|
||||
}
|
||||
opts = opts || {};
|
||||
return arr
|
||||
.reduce(function(prev, element, idx, arr) {
|
||||
var str = element;
|
||||
if(opts.extract) {
|
||||
str = opts.extract(element);
|
||||
}
|
||||
var rendered = fuzzy.match(pattern, str, opts);
|
||||
if(rendered != null) {
|
||||
prev[prev.length] = {
|
||||
string: rendered.rendered
|
||||
, score: rendered.score
|
||||
, index: idx
|
||||
, original: element
|
||||
};
|
||||
}
|
||||
return prev;
|
||||
}, [])
|
||||
|
||||
// Sort by score. Browsers are inconsistent wrt stable/unstable
|
||||
// sorting, so force stable by using the index in the case of tie.
|
||||
// See http://ofb.net/~sethml/is-sort-stable.html
|
||||
.sort(function(a,b) {
|
||||
var compare = b.score - a.score;
|
||||
if(compare) return compare;
|
||||
return a.index - b.index;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
}());
|
File diff suppressed because one or more lines are too long
6
web/ui/static/vendor/moment/moment.min.js
vendored
6
web/ui/static/vendor/moment/moment.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,72 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="noindex,nofollow">
|
||||
<title>{{ pageTitle }}</title>
|
||||
<link rel="shortcut icon" href="{{ pathPrefix }}/classic/static/img/favicon.ico?v={{ buildVersion }}">
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/js/jquery-3.5.1.min.js?v={{ buildVersion }}"></script>
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/js/popper.min.js?v={{ buildVersion }}"></script>
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/bootstrap-4.5.2/js/bootstrap.min.js?v={{ buildVersion }}"></script>
|
||||
|
||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/vendor/bootstrap-4.5.2/css/bootstrap.min.css?v={{ buildVersion }}">
|
||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/css/prometheus.css?v={{ buildVersion }}">
|
||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/vendor/bootstrap4-glyphicons/css/bootstrap-glyphicons.min.css?v={{ buildVersion }}">
|
||||
|
||||
<script>
|
||||
var PATH_PREFIX = "{{ pathPrefix }}";
|
||||
var BUILD_VERSION = "{{ buildVersion }}";
|
||||
$(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
})
|
||||
</script>
|
||||
|
||||
{{template "head" .}}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar fixed-top navbar-expand-sm navbar-dark bg-dark">
|
||||
<div class="container-fluid">
|
||||
|
||||
<button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#nav-content" aria-expanded="false" aria-controls="nav-content" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
<!--span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span-->
|
||||
</button>
|
||||
|
||||
<a class="navbar-brand" href="{{ pathPrefix }}/classic/">Prometheus</a>
|
||||
|
||||
|
||||
<div id="nav-content" class="navbar-collapse collapse">
|
||||
<ul class="navbar-nav">
|
||||
{{$consoles := consolesPath}}
|
||||
{{if $consoles}}
|
||||
<li class="nav-item"><a class="nav-link" href="{{$consoles}}">Consoles</a></li>
|
||||
{{ end }}
|
||||
<li class="nav-item"><a class="nav-link" href="{{ pathPrefix }}/classic/alerts">Alerts</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ pathPrefix }}/classic/graph">Graph</a></li>
|
||||
<li class="nav-item dropdown">
|
||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Status <span class="caret"></span></a>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="{{ pathPrefix }}/classic/status">Runtime & Build Information</a>
|
||||
<a class="dropdown-item" href="{{ pathPrefix }}/classic/flags">Command-Line Flags</a>
|
||||
<a class="dropdown-item" href="{{ pathPrefix }}/classic/config">Configuration</a>
|
||||
<a class="dropdown-item" href="{{ pathPrefix }}/classic/rules">Rules</a>
|
||||
<a class="dropdown-item" href="{{ pathPrefix }}/classic/targets">Targets</a>
|
||||
<a class="dropdown-item" href="{{ pathPrefix }}/classic/service-discovery">Service Discovery</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class= "nav-item">
|
||||
<a class ="nav-link" href="https://prometheus.io/docs/prometheus/latest/getting_started/" target="_blank">Help</a>
|
||||
</li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ pathPrefix }}/graph">New UI</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{{template "content" .}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,94 +0,0 @@
|
|||
{{define "head"}}
|
||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/css/alerts.css?v={{ buildVersion }}">
|
||||
<script src="{{ pathPrefix }}/classic/static/js/alerts.js?v={{ buildVersion }}"></script>
|
||||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="container-fluid">
|
||||
<h1>Alerts</h1>
|
||||
<div id="alertFilters" class="btn-group btn-group-toggle" data-toggle="buttons">
|
||||
<label class="btn btn-primary active">
|
||||
<input type="checkbox" name="alertFilters" id="inactiveAlerts" autocomplete="off"> <i class="glyphicon glyphicon-check"></i> Inactive ({{ .Counts.Inactive }})
|
||||
</label>
|
||||
<label class="btn btn-primary active">
|
||||
<input type="checkbox" name="alertFilters" id="pendingAlerts" autocomplete="off"> <i class="glyphicon glyphicon-check"></i> Pending ({{ .Counts.Pending }})
|
||||
</label>
|
||||
<label class="btn btn-primary active">
|
||||
<input type="checkbox" name="alertFilters" id="firingAlerts" autocomplete="off"> <i class="glyphicon glyphicon-check"></i> Firing ({{ .Counts.Firing }})
|
||||
</label>
|
||||
</br>
|
||||
</div>
|
||||
<div class="show-annotations">
|
||||
<i class="glyphicon glyphicon-unchecked"></i>
|
||||
<button type="button" class="show-annotations" title="show annotations">Show annotations</button>
|
||||
</div>
|
||||
<table id="alertsTable" class="table table-bordered table-collapsed">
|
||||
<tbody>
|
||||
{{$alertStateToRowClass := .AlertStateToRowClass}}
|
||||
{{range .Groups}}
|
||||
<tr>
|
||||
<td style="padding: 2px">
|
||||
{{.File}} > {{.Name}}
|
||||
</td>
|
||||
</tr>
|
||||
{{range .AlertingRules}}
|
||||
{{$activeAlerts := .ActiveAlerts}}
|
||||
<tr class="alert alert-{{index $alertStateToRowClass .State}} alert_header">
|
||||
<td><i class="icon-chevron-down"></i> <b>{{.Name}}</b> ({{len $activeAlerts}} active)</td>
|
||||
</tr>
|
||||
<tr class="alert_details">
|
||||
<td>
|
||||
<div>
|
||||
<pre style="display:block; padding:9.5px; font-size:13px; color:#333; word-break:break-all; background-color:#f5f5f5; border:1px solid #ccc; border-radius:4px;" ><code>{{.HTMLSnippet (print pathPrefix "/classic")}}</code></pre>
|
||||
</div>
|
||||
{{if $activeAlerts}}
|
||||
<table class="table table-bordered table-hover table-sm alert_elements_table">
|
||||
<tr class="">
|
||||
<th>Labels</th>
|
||||
<th>State</th>
|
||||
<th>Active Since</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
{{range $activeAlerts}}
|
||||
<tr>
|
||||
<td>
|
||||
{{range $label, $value := .Labels.Map}}
|
||||
<span class="badge badge-primary">{{$label}}="{{$value}}"</span>
|
||||
{{end}}
|
||||
</td>
|
||||
<td><span class="alert alert-{{ .State | alertStateToClass }} state_indicator text-uppercase">{{.State}}</span></td>
|
||||
<td>{{.ActiveAt.UTC}}</td>
|
||||
<td>{{.Value}}</td>
|
||||
</tr>
|
||||
{{ if .Annotations.Map}}
|
||||
<tr style="display:none" class="alert_annotations">
|
||||
<th colspan="4">Annotations</th>
|
||||
</tr>
|
||||
<tr style="display:none" class="alert_annotations">
|
||||
<td colspan="4">
|
||||
<dl>
|
||||
{{range $label, $value := .Annotations.Map}}
|
||||
<dt>{{$label}}</dt>
|
||||
<dd>{{$value}}</dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</table>
|
||||
{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<tr>
|
||||
<td>
|
||||
No alerting rules defined
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
|
@ -1,12 +0,0 @@
|
|||
{{define "head"}}
|
||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/css/config.css?v={{ buildVersion }}">
|
||||
<script src="{{ pathPrefix }}/classic/static/js/config.js?v={{ buildVersion }}"></script>
|
||||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="container-fluid">
|
||||
<h2 id="configuration">Configuration <button type="button" class="btn btn-primary" id="copyToClipboard">Copy to clipboard</button></h2>
|
||||
|
||||
<pre id="config_yaml">{{.}}</pre>
|
||||
</div>
|
||||
{{end}}
|
|
@ -1,17 +0,0 @@
|
|||
{{define "head"}}<!-- nix -->{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="container-fluid">
|
||||
<h2 id="startupflags">Command-Line Flags</h2>
|
||||
<table class="table table-sm table-bordered table-striped table-hover">
|
||||
<tbody>
|
||||
{{range $key, $value := . }}
|
||||
<tr>
|
||||
<th scope="row">{{$key}}</th>
|
||||
<td>{{$value}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
|
@ -1,39 +0,0 @@
|
|||
{{define "head"}}
|
||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/vendor/rickshaw/rickshaw.min.css?v={{ buildVersion }}">
|
||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/vendor/eonasdan-bootstrap-datetimepicker/bootstrap-datetimepicker.min.css?v={{ buildVersion }}">
|
||||
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/rickshaw/vendor/d3.v3.js?v={{ buildVersion }}"></script>
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/rickshaw/vendor/d3.layout.min.js?v={{ buildVersion }}"></script>
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/rickshaw/rickshaw.min.js?v={{ buildVersion }}"></script>
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/moment/moment.min.js?v={{ buildVersion }}"></script>
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/moment/moment-timezone-with-data.min.js?v={{ buildVersion }}"></script>
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/eonasdan-bootstrap-datetimepicker/bootstrap-datetimepicker.min.js?v={{ buildVersion }}"></script>
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/bootstrap3-typeahead/bootstrap3-typeahead.min.js?v={{ buildVersion }}"></script>
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/fuzzy/fuzzy.js?v={{ buildVersion }}"></script>
|
||||
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/mustache/mustache.min.js?v={{ buildVersion }}"></script>
|
||||
<script src="{{ pathPrefix }}/classic/static/vendor/js/jquery.selection.js?v={{ buildVersion }}"></script>
|
||||
<!-- <script src="{{ pathPrefix }}/classic/static/vendor/js/jquery.hotkeys.js?v={{ buildVersion }}"></script> -->
|
||||
|
||||
<script src="{{ pathPrefix }}/classic/static/js/graph/index.js?v={{ buildVersion }}"></script>
|
||||
|
||||
<script id="graph_template" type="text/x-handlebars-template"></script>
|
||||
|
||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/css/graph.css?v={{ buildVersion }}">
|
||||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div id="graph_container" class="container-fluid">
|
||||
<div class="clearfix">
|
||||
<div class="query-history">
|
||||
<i class="glyphicon glyphicon-unchecked"></i>
|
||||
<button type="button" class="search-history" title="search previous queries">Enable query history</button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-link btn-sm new_ui_button" onclick="window.location.pathname='{{ pathPrefix }}/graph'">Back to the new UI</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div><input class="btn btn-primary" type="submit" value="Add Graph" id="add_graph"></div>
|
||||
</div>
|
||||
{{end}}
|
|
@ -1,54 +0,0 @@
|
|||
{{define "head"}}
|
||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/css/rules.css?v={{ buildVersion }}">
|
||||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="container-fluid">
|
||||
<h2>Rules</h2>
|
||||
<table class="table table-bordered">
|
||||
{{range .RuleGroups}}
|
||||
<thead>
|
||||
<tr>
|
||||
<td colspan="3"><h2><a href="#{{reReplaceAll "([^a-zA-Z0-9])" "$1" .Name}}" id="{{reReplaceAll "([^a-zA-Z0-9])" "$1" .Name}}">{{.Name}}</a></h2></td>
|
||||
<td><h2>{{if .GetLastEvaluation.IsZero}}Never{{else}}{{since .GetLastEvaluation}} ago{{end}}</h2></td>
|
||||
<td><h2>{{humanizeDuration .GetEvaluationTime.Seconds}}</h2></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="font-weight:bold">Rule</td>
|
||||
<td style="font-weight:bold">State</td>
|
||||
<td style="font-weight:bold">Error</td>
|
||||
<td style="font-weight:bold">Last Evaluation</td>
|
||||
<td style="font-weight:bold">Evaluation Time</td>
|
||||
</tr>
|
||||
{{range .Rules}}
|
||||
<tr>
|
||||
<td class="rule_cell">{{.HTMLSnippet (print pathPrefix (print pathPrefix "/classic"))}}</td>
|
||||
<td class="state">
|
||||
<span class="alert alert-{{ .Health | ruleHealthToClass }} state_indicator text-uppercase">
|
||||
{{.Health}}
|
||||
</span>
|
||||
</td>
|
||||
<td class="errors">
|
||||
{{if .LastError}}
|
||||
<span class="alert alert-danger state_indicator">{{.LastError}}</span>
|
||||
{{end}}
|
||||
</td>
|
||||
<td>
|
||||
{{if .GetEvaluationTimestamp.IsZero}}Never{{else}}{{since .GetEvaluationTimestamp}} ago{{end}}
|
||||
</td>
|
||||
<td>{{humanizeDuration .GetEvaluationDuration.Seconds}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<tr>
|
||||
<td>
|
||||
No rules defined
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
|
@ -1,79 +0,0 @@
|
|||
{{define "head"}}
|
||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/css/targets.css?v={{ buildVersion }}">
|
||||
<script src="{{ pathPrefix }}/classic/static/js/targets.js?v={{ buildVersion }}"></script>
|
||||
|
||||
<style>
|
||||
*[id]:before {
|
||||
display: block;
|
||||
content: " ";
|
||||
margin-top: -65px;
|
||||
height: 65px;
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="container-fluid">
|
||||
|
||||
<h1>Service Discovery</h1>
|
||||
<div>
|
||||
<ul>
|
||||
{{- range $i, $job := .Index}}
|
||||
<li><a href="#job-{{$job}}">{{$job}}</a> ({{ index $.Active $i }}/{{ index $.Total $i }} active targets)</li>
|
||||
{{- end}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{{- $targets := .Targets}}
|
||||
{{- range $i, $job := .Index}}
|
||||
|
||||
<div class="table-container">
|
||||
<h2 class="job_header" id="job-{{$job}}">
|
||||
{{$job}} <button type="button" class="targets collapsed-table btn btn-primary">show more</button>
|
||||
</h2>
|
||||
{{- with index $.Dropped $i}}
|
||||
{{- if gt . 100 }}
|
||||
<div class="collapsed-element" style="display:none">{{ . }} targets have been dropped, showing only the first 100 dropped targets as examples.</div>
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
<table class="table table-sm table-bordered table-striped table-hover" style="display:none">
|
||||
<thead class="job_details">
|
||||
<tr>
|
||||
<th>Discovered Labels</th>
|
||||
<th>Target Labels</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{{- range index $targets $job}}
|
||||
<tr>
|
||||
<td class="labels">
|
||||
{{- $labels := .DiscoveredLabels.Map }}
|
||||
<ul class="list-inline" style="list-style-type:none">
|
||||
{{- range $label, $value := $labels }}
|
||||
<li><span class="badge badge-primary">{{$label}}="{{$value}}"</span></li>
|
||||
{{- else -}}
|
||||
<li><span class="badge badge-default">none</span></li>
|
||||
{{- end }}
|
||||
</ul>
|
||||
</td>
|
||||
<td class="labels">
|
||||
{{- $labels := .Labels.Map }}
|
||||
<ul class="list-inline" style="list-style-type:none">
|
||||
{{- range $label, $value := $labels }}
|
||||
<li><span class="badge badge-primary">{{$label}}="{{$value}}"</span></li>
|
||||
{{- else -}}
|
||||
<li><span class="badge badge-default">Dropped</span></li>
|
||||
{{- end }}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{{- end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
|
||||
{{end}}
|
|
@ -1,175 +0,0 @@
|
|||
{{define "head"}}<!-- nix -->{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div class="container-fluid">
|
||||
<h2 id="runtime">Runtime Information</h2>
|
||||
<table class="table table-sm table-bordered table-striped table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Uptime</th>
|
||||
<td>{{.Birth}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Working Directory</th>
|
||||
<td>{{.CWD}}</td>
|
||||
</tr>
|
||||
<tr{{if not .ReloadConfigSuccess}} class="danger"{{end}}>
|
||||
<th>Configuration reload</th>
|
||||
<td>{{if .ReloadConfigSuccess}}Successful{{else}}Failed{{end}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Last successful configuration reload</th>
|
||||
<td>{{.LastConfigTime}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>WAL corruptions</th>
|
||||
<td>{{.CorruptionCount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Goroutines</th>
|
||||
<td>{{.GoroutineCount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>GOMAXPROCS</th>
|
||||
<td>{{.GOMAXPROCS}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>GOGC</th>
|
||||
<td>{{.GOGC}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>GODEBUG</th>
|
||||
<td>{{.GODEBUG}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Storage Retention</th>
|
||||
<td>{{.StorageRetention}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2 id="buildinformation">Build Information</h2>
|
||||
<table class="table table-sm table-bordered table-striped table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">Version</th>
|
||||
<td>{{.Version.Version}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Revision</th>
|
||||
<td>{{.Version.Revision}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Branch</th>
|
||||
<td>{{.Version.Branch}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">BuildUser</th>
|
||||
<td>{{.Version.BuildUser}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">BuildDate</th>
|
||||
<td>{{.Version.BuildDate}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">GoVersion</th>
|
||||
<td>{{.Version.GoVersion}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2 id="alertmanagers">Alertmanagers</h2>
|
||||
<table class="table table-sm table-bordered table-striped table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Endpoint</th>
|
||||
</tr>
|
||||
{{range .Alertmanagers}}
|
||||
<tr>
|
||||
{{/* Alertmanager URLs always have Scheme, Host and Path set */}}
|
||||
<td>{{.Scheme}}://<a href="{{.Scheme}}://{{.Host}}">{{.Host}}</a>{{.Path}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2 id="headstatus">Head Stats</h2>
|
||||
<table class="table table-sm table-bordered table-striped table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Number Of Series </th>
|
||||
<th>Number of Chunks</th>
|
||||
<th>Current Min Time</th>
|
||||
<th>Current Max Time</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td scope="row">{{ .NumSeries}}</td>
|
||||
<td>{{.ChunkCount}}</td>
|
||||
<td>{{ .MinTime | unixToTime }} ({{ .MinTime }})</td>
|
||||
<td>{{ .MaxTime | unixToTime }} ({{ .MaxTime }})</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div>Total Query Time: {{ .Duration }} Seconds</div>
|
||||
<h3 id="headstatus">Highest Cardinality Labels </h3>
|
||||
<table class="table table-sm table-bordered table-striped table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Count</th>
|
||||
</tr>
|
||||
{{ range .Stats.CardinalityLabelStats }}
|
||||
<tr>
|
||||
<td scope="row">{{.Name}}</td>
|
||||
<td>{{.Count}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="headstatus">Highest Cardinality Metric Names</h3>
|
||||
<table class="table table-sm table-bordered table-striped table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Count</th>
|
||||
</tr>
|
||||
{{ range .Stats.CardinalityMetricsStats }}
|
||||
<tr>
|
||||
<td scope="row">{{.Name}}</td>
|
||||
<td>{{.Count}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="headstatus">Label Names With Highest Cumulative Label Value Length</h3>
|
||||
<table class="table table-sm table-bordered table-striped table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Length</th>
|
||||
</tr>
|
||||
{{ range .Stats.LabelValueStats }}
|
||||
<tr>
|
||||
<td scope="row">{{.Name}}</td>
|
||||
<td>{{.Count}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="headstatus">Most Common Label Pairs</h3>
|
||||
<table class="table table-sm table-bordered table-striped table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Count</th>
|
||||
</tr>
|
||||
{{ range .Stats.LabelValuePairsStats }}
|
||||
<tr>
|
||||
<td scope="row">{{.Name}}</td>
|
||||
<td>{{.Count}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
|
@ -1,76 +0,0 @@
|
|||
{{define "head"}}
|
||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/css/targets.css?v={{ buildVersion }}">
|
||||
<script src="{{ pathPrefix }}/classic/static/js/targets.js?v={{ buildVersion }}"></script>
|
||||
{{end}}
|
||||
|
||||
|
||||
{{define "content"}}
|
||||
<div class="container-fluid">
|
||||
<h1>Targets</h1>
|
||||
<div class="options-container">
|
||||
<div id="showTargets" class="btn-group btn-group-toggle" data-toggle="buttons">
|
||||
<label class="btn btn-primary">
|
||||
<input type="radio" name="targets" id="all-targets" autocomplete="off" checked> All
|
||||
</label>
|
||||
<label class="btn btn-primary">
|
||||
<input type="radio" name="targets" id="unhealthy-targets" autocomplete="off"> Unhealthy
|
||||
</label>
|
||||
<br />
|
||||
</div>
|
||||
<button type="button" class="collapse-all btn btn-primary">Collapse All</button>
|
||||
</div>
|
||||
|
||||
{{- range $job, $pool := .TargetPools}}
|
||||
{{- $healthy := numHealthy $pool}}
|
||||
{{- $total := len $pool}}
|
||||
|
||||
<div class="table-container">
|
||||
<h2 class="job_header{{if lt $healthy $total}} danger{{end}}">
|
||||
<a id="job-{{$job}}" href="#job-{{$job}}">{{$job}} ({{$healthy}}/{{$total}} up)</a>
|
||||
<button type="button" class="targets expanded-table btn btn-primary">show less</button>
|
||||
</h2>
|
||||
<table class="table table-sm table-bordered table-striped table-hover">
|
||||
<thead class="job_details">
|
||||
<tr>
|
||||
<th>Endpoint</th>
|
||||
<th>State</th>
|
||||
<th>Labels</th>
|
||||
<th>Last Scrape</th>
|
||||
<th>Scrape Duration</th>
|
||||
<th>Error</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{- range $pool}}
|
||||
<tr>
|
||||
<td class="endpoint">
|
||||
<a href="{{.URL | globalURL}}">{{.URL.Scheme}}://{{.URL.Host}}{{.URL.Path}}</a><br>
|
||||
{{- range $label, $values := .URL.Query }}
|
||||
{{- range $i, $value := $values}}
|
||||
<span class="badge badge-primary">{{$label}}="{{$value}}"</span>
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
</td>
|
||||
<td class="state">
|
||||
<span class="alert alert-{{ .Health | targetHealthToClass }} state_indicator text-uppercase">{{.Health}}</span>
|
||||
</td>
|
||||
<td class="labels">
|
||||
<span class="cursor-pointer" data-toggle="tooltip" title="" data-html=true data-original-title="<b>Before relabeling:</b>{{range $k, $v := .DiscoveredLabels.Map}}<br>{{$ev := $v | html}}{{$k}}="{{$ev}}"{{end}}">
|
||||
{{- range $label, $value := .Labels.Map}}
|
||||
<span class="badge badge-primary">{{$label}}="{{$value}}"</span>
|
||||
{{- else -}}
|
||||
<span class="badge badge-default">none</span>
|
||||
{{- end}}
|
||||
</span>
|
||||
</td>
|
||||
<td class="last-scrape">{{- if .LastScrape.IsZero}}Never{{else}}{{since .LastScrape}} ago{{end}}</td>
|
||||
<td class="scrape-duration">{{- humanizeDuration .LastScrapeDuration.Seconds}}</td>
|
||||
<td class="errors">{{- if .LastError}}<span class="alert alert-danger state_indicator">{{.LastError}}</span>{{end}}</td>
|
||||
</tr>
|
||||
{{- end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
{{end}}
|
10
web/ui/ui.go
10
web/ui/ui.go
|
@ -57,15 +57,7 @@ var Assets = func() http.FileSystem {
|
|||
},
|
||||
)
|
||||
|
||||
templates := filter.Keep(
|
||||
http.Dir(path.Join(assetsPrefix, "templates")),
|
||||
func(path string, fi os.FileInfo) bool {
|
||||
return fi.IsDir() || strings.HasSuffix(path, ".html")
|
||||
},
|
||||
)
|
||||
|
||||
return union.New(map[string]http.FileSystem{
|
||||
"/templates": templates,
|
||||
"/static": static,
|
||||
"/static": static,
|
||||
})
|
||||
}()
|
||||
|
|
394
web/web.go
394
web/web.go
|
@ -31,11 +31,9 @@ import (
|
|||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
template_text "text/template"
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/units"
|
||||
|
@ -61,8 +59,6 @@ import (
|
|||
"github.com/prometheus/prometheus/scrape"
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
"github.com/prometheus/prometheus/template"
|
||||
"github.com/prometheus/prometheus/tsdb"
|
||||
"github.com/prometheus/prometheus/tsdb/index"
|
||||
"github.com/prometheus/prometheus/util/httputil"
|
||||
api_v1 "github.com/prometheus/prometheus/web/api/v1"
|
||||
"github.com/prometheus/prometheus/web/ui"
|
||||
|
@ -364,38 +360,13 @@ func New(logger log.Logger, o *Options) *Handler {
|
|||
router.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, path.Join(o.ExternalURL.Path, homePage), http.StatusFound)
|
||||
})
|
||||
router.Get("/classic/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, path.Join(o.ExternalURL.Path, "/classic/graph"), http.StatusFound)
|
||||
})
|
||||
|
||||
// Redirect the original React UI's path (under "/new") to its new path at the root.
|
||||
router.Get("/new/*path", func(w http.ResponseWriter, r *http.Request) {
|
||||
p := route.Param(r.Context(), "path")
|
||||
http.Redirect(w, r, path.Join(o.ExternalURL.Path, p)+"?"+r.URL.RawQuery, http.StatusFound)
|
||||
})
|
||||
|
||||
router.Get("/classic/alerts", readyf(h.alerts))
|
||||
router.Get("/classic/graph", readyf(h.graph))
|
||||
router.Get("/classic/status", readyf(h.status))
|
||||
router.Get("/classic/flags", readyf(h.flags))
|
||||
router.Get("/classic/config", readyf(h.serveConfig))
|
||||
router.Get("/classic/rules", readyf(h.rules))
|
||||
router.Get("/classic/targets", readyf(h.targets))
|
||||
router.Get("/classic/service-discovery", readyf(h.serviceDiscovery))
|
||||
// The console library examples at 'console_libraries/prom.lib' still depend on old asset files being served under `classic`.
|
||||
router.Get("/classic/static/*filepath", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.URL.Path = path.Join("/static", route.Param(r.Context(), "filepath"))
|
||||
fs := server.StaticFileServer(ui.Assets)
|
||||
fs.ServeHTTP(w, r)
|
||||
})
|
||||
// Make sure that "<path-prefix>/classic" is redirected to "<path-prefix>/classic/" and
|
||||
// not just the naked "/classic/", which would be the default behavior of the router
|
||||
// with the "RedirectTrailingSlash" option (https://pkg.go.dev/github.com/julienschmidt/httprouter#Router.RedirectTrailingSlash),
|
||||
// and which breaks users with a --web.route-prefix that deviates from the path derived
|
||||
// from the external URL.
|
||||
// See https://github.com/prometheus/prometheus/issues/6163#issuecomment-553855129.
|
||||
router.Get("/classic", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, path.Join(o.ExternalURL.Path, "classic")+"/", http.StatusFound)
|
||||
})
|
||||
|
||||
router.Get("/version", h.version)
|
||||
router.Get("/metrics", promhttp.Handler().ServeHTTP)
|
||||
|
@ -633,44 +604,6 @@ func (h *Handler) Run(ctx context.Context, listener net.Listener, webConfig stri
|
|||
}
|
||||
}
|
||||
|
||||
func (h *Handler) alerts(w http.ResponseWriter, r *http.Request) {
|
||||
var groups []*rules.Group
|
||||
for _, group := range h.ruleManager.RuleGroups() {
|
||||
if group.HasAlertingRules() {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
}
|
||||
|
||||
alertStatus := AlertStatus{
|
||||
Groups: groups,
|
||||
AlertStateToRowClass: map[rules.AlertState]string{
|
||||
rules.StateInactive: "success",
|
||||
rules.StatePending: "warning",
|
||||
rules.StateFiring: "danger",
|
||||
},
|
||||
Counts: alertCounts(groups),
|
||||
}
|
||||
h.executeTemplate(w, "alerts.html", alertStatus)
|
||||
}
|
||||
|
||||
func alertCounts(groups []*rules.Group) AlertByStateCount {
|
||||
result := AlertByStateCount{}
|
||||
|
||||
for _, group := range groups {
|
||||
for _, alert := range group.AlertingRules() {
|
||||
switch alert.State() {
|
||||
case rules.StateInactive:
|
||||
result.Inactive++
|
||||
case rules.StatePending:
|
||||
result.Pending++
|
||||
case rules.StateFiring:
|
||||
result.Firing++
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (h *Handler) consoles(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
name := route.Param(ctx, "filepath")
|
||||
|
@ -753,91 +686,6 @@ func (h *Handler) consoles(w http.ResponseWriter, r *http.Request) {
|
|||
io.WriteString(w, result)
|
||||
}
|
||||
|
||||
func (h *Handler) graph(w http.ResponseWriter, r *http.Request) {
|
||||
h.executeTemplate(w, "graph.html", nil)
|
||||
}
|
||||
|
||||
func (h *Handler) status(w http.ResponseWriter, r *http.Request) {
|
||||
status := struct {
|
||||
Birth time.Time
|
||||
CWD string
|
||||
Version *PrometheusVersion
|
||||
Alertmanagers []*url.URL
|
||||
GoroutineCount int
|
||||
GOMAXPROCS int
|
||||
GOGC string
|
||||
GODEBUG string
|
||||
CorruptionCount int64
|
||||
ChunkCount int64
|
||||
TimeSeriesCount int64
|
||||
LastConfigTime time.Time
|
||||
ReloadConfigSuccess bool
|
||||
StorageRetention string
|
||||
NumSeries uint64
|
||||
MaxTime int64
|
||||
MinTime int64
|
||||
Stats *index.PostingsStats
|
||||
Duration string
|
||||
}{
|
||||
Birth: h.birth,
|
||||
CWD: h.cwd,
|
||||
Version: h.versionInfo,
|
||||
Alertmanagers: h.notifier.Alertmanagers(),
|
||||
GoroutineCount: runtime.NumGoroutine(),
|
||||
GOMAXPROCS: runtime.GOMAXPROCS(0),
|
||||
GOGC: os.Getenv("GOGC"),
|
||||
GODEBUG: os.Getenv("GODEBUG"),
|
||||
}
|
||||
|
||||
if h.options.TSDBRetentionDuration != 0 {
|
||||
status.StorageRetention = h.options.TSDBRetentionDuration.String()
|
||||
}
|
||||
if h.options.TSDBMaxBytes != 0 {
|
||||
if status.StorageRetention != "" {
|
||||
status.StorageRetention = status.StorageRetention + " or "
|
||||
}
|
||||
status.StorageRetention = status.StorageRetention + h.options.TSDBMaxBytes.String()
|
||||
}
|
||||
|
||||
metrics, err := h.gatherer.Gather()
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("error gathering runtime status: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
for _, mF := range metrics {
|
||||
switch *mF.Name {
|
||||
case "prometheus_tsdb_head_chunks":
|
||||
status.ChunkCount = int64(toFloat64(mF))
|
||||
case "prometheus_tsdb_head_series":
|
||||
status.TimeSeriesCount = int64(toFloat64(mF))
|
||||
case "prometheus_tsdb_wal_corruptions_total":
|
||||
status.CorruptionCount = int64(toFloat64(mF))
|
||||
case "prometheus_config_last_reload_successful":
|
||||
status.ReloadConfigSuccess = toFloat64(mF) != 0
|
||||
case "prometheus_config_last_reload_success_timestamp_seconds":
|
||||
status.LastConfigTime = time.Unix(int64(toFloat64(mF)), 0).UTC()
|
||||
}
|
||||
}
|
||||
|
||||
startTime := time.Now().UnixNano()
|
||||
s, err := h.localStorage.Stats("__name__")
|
||||
if err != nil {
|
||||
if errors.Cause(err) == tsdb.ErrNotReady {
|
||||
http.Error(w, tsdb.ErrNotReady.Error(), http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
http.Error(w, fmt.Sprintf("error gathering local storage statistics: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
status.Duration = fmt.Sprintf("%.3f", float64(time.Now().UnixNano()-startTime)/float64(1e9))
|
||||
status.Stats = s.IndexPostingStats
|
||||
status.NumSeries = s.NumSeries
|
||||
status.MaxTime = s.MaxTime
|
||||
status.MinTime = s.MinTime
|
||||
|
||||
h.executeTemplate(w, "status.html", status)
|
||||
}
|
||||
|
||||
func (h *Handler) runtimeInfo() (api_v1.RuntimeInfo, error) {
|
||||
status := api_v1.RuntimeInfo{
|
||||
StartTime: h.birth,
|
||||
|
@ -889,82 +737,6 @@ func toFloat64(f *io_prometheus_client.MetricFamily) float64 {
|
|||
return math.NaN()
|
||||
}
|
||||
|
||||
func (h *Handler) flags(w http.ResponseWriter, r *http.Request) {
|
||||
h.executeTemplate(w, "flags.html", h.flagsMap)
|
||||
}
|
||||
|
||||
func (h *Handler) serveConfig(w http.ResponseWriter, r *http.Request) {
|
||||
h.mtx.RLock()
|
||||
defer h.mtx.RUnlock()
|
||||
|
||||
h.executeTemplate(w, "config.html", h.config.String())
|
||||
}
|
||||
|
||||
func (h *Handler) rules(w http.ResponseWriter, r *http.Request) {
|
||||
h.executeTemplate(w, "rules.html", h.ruleManager)
|
||||
}
|
||||
|
||||
func (h *Handler) serviceDiscovery(w http.ResponseWriter, r *http.Request) {
|
||||
var index []string
|
||||
targets := h.scrapeManager.TargetsAll()
|
||||
for job := range targets {
|
||||
index = append(index, job)
|
||||
}
|
||||
sort.Strings(index)
|
||||
scrapeConfigData := struct {
|
||||
Index []string
|
||||
Targets map[string][]*scrape.Target
|
||||
Active []int
|
||||
Dropped []int
|
||||
Total []int
|
||||
}{
|
||||
Index: index,
|
||||
Targets: make(map[string][]*scrape.Target),
|
||||
Active: make([]int, len(index)),
|
||||
Dropped: make([]int, len(index)),
|
||||
Total: make([]int, len(index)),
|
||||
}
|
||||
for i, job := range scrapeConfigData.Index {
|
||||
scrapeConfigData.Targets[job] = make([]*scrape.Target, 0, len(targets[job]))
|
||||
scrapeConfigData.Total[i] = len(targets[job])
|
||||
for _, target := range targets[job] {
|
||||
// Do not display more than 100 dropped targets per job to avoid
|
||||
// returning too much data to the clients.
|
||||
if target.Labels().Len() == 0 {
|
||||
scrapeConfigData.Dropped[i]++
|
||||
if scrapeConfigData.Dropped[i] > 100 {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
scrapeConfigData.Active[i]++
|
||||
}
|
||||
scrapeConfigData.Targets[job] = append(scrapeConfigData.Targets[job], target)
|
||||
}
|
||||
}
|
||||
|
||||
h.executeTemplate(w, "service-discovery.html", scrapeConfigData)
|
||||
}
|
||||
|
||||
func (h *Handler) targets(w http.ResponseWriter, r *http.Request) {
|
||||
tps := h.scrapeManager.TargetsActive()
|
||||
for _, targets := range tps {
|
||||
sort.Slice(targets, func(i, j int) bool {
|
||||
iJobLabel := targets[i].Labels().Get(model.JobLabel)
|
||||
jJobLabel := targets[j].Labels().Get(model.JobLabel)
|
||||
if iJobLabel == jJobLabel {
|
||||
return targets[i].Labels().Get(model.InstanceLabel) < targets[j].Labels().Get(model.InstanceLabel)
|
||||
}
|
||||
return iJobLabel < jJobLabel
|
||||
})
|
||||
}
|
||||
|
||||
h.executeTemplate(w, "targets.html", struct {
|
||||
TargetPools map[string][]*scrape.Target
|
||||
}{
|
||||
TargetPools: tps,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) version(w http.ResponseWriter, r *http.Request) {
|
||||
dec := json.NewEncoder(w)
|
||||
if err := dec.Encode(h.versionInfo); err != nil {
|
||||
|
@ -1004,170 +776,6 @@ func (h *Handler) consolesPath() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func tmplFuncs(consolesPath string, opts *Options) template_text.FuncMap {
|
||||
return template_text.FuncMap{
|
||||
"since": func(t time.Time) time.Duration {
|
||||
return time.Since(t) / time.Millisecond * time.Millisecond
|
||||
},
|
||||
"unixToTime": func(i int64) time.Time {
|
||||
t := time.Unix(i/int64(time.Microsecond), 0).UTC()
|
||||
return t
|
||||
},
|
||||
"consolesPath": func() string { return consolesPath },
|
||||
"pathPrefix": func() string { return opts.ExternalURL.Path },
|
||||
"pageTitle": func() string { return opts.PageTitle },
|
||||
"buildVersion": func() string { return opts.Version.Revision },
|
||||
"globalURL": func(u *url.URL) *url.URL {
|
||||
host, port, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
return u
|
||||
}
|
||||
for _, lhr := range api_v1.LocalhostRepresentations {
|
||||
if host == lhr {
|
||||
_, ownPort, err := net.SplitHostPort(opts.ListenAddress)
|
||||
if err != nil {
|
||||
return u
|
||||
}
|
||||
|
||||
if port == ownPort {
|
||||
// Only in the case where the target is on localhost and its port is
|
||||
// the same as the one we're listening on, we know for sure that
|
||||
// we're monitoring our own process and that we need to change the
|
||||
// scheme, hostname, and port to the externally reachable ones as
|
||||
// well. We shouldn't need to touch the path at all, since if a
|
||||
// path prefix is defined, the path under which we scrape ourselves
|
||||
// should already contain the prefix.
|
||||
u.Scheme = opts.ExternalURL.Scheme
|
||||
u.Host = opts.ExternalURL.Host
|
||||
} else {
|
||||
// Otherwise, we only know that localhost is not reachable
|
||||
// externally, so we replace only the hostname by the one in the
|
||||
// external URL. It could be the wrong hostname for the service on
|
||||
// this port, but it's still the best possible guess.
|
||||
host, _, err := net.SplitHostPort(opts.ExternalURL.Host)
|
||||
if err != nil {
|
||||
return u
|
||||
}
|
||||
u.Host = host + ":" + port
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return u
|
||||
},
|
||||
"numHealthy": func(pool []*scrape.Target) int {
|
||||
alive := len(pool)
|
||||
for _, p := range pool {
|
||||
if p.Health() != scrape.HealthGood {
|
||||
alive--
|
||||
}
|
||||
}
|
||||
|
||||
return alive
|
||||
},
|
||||
"targetHealthToClass": func(th scrape.TargetHealth) string {
|
||||
switch th {
|
||||
case scrape.HealthUnknown:
|
||||
return "warning"
|
||||
case scrape.HealthGood:
|
||||
return "success"
|
||||
default:
|
||||
return "danger"
|
||||
}
|
||||
},
|
||||
"ruleHealthToClass": func(rh rules.RuleHealth) string {
|
||||
switch rh {
|
||||
case rules.HealthUnknown:
|
||||
return "warning"
|
||||
case rules.HealthGood:
|
||||
return "success"
|
||||
default:
|
||||
return "danger"
|
||||
}
|
||||
},
|
||||
"alertStateToClass": func(as rules.AlertState) string {
|
||||
switch as {
|
||||
case rules.StateInactive:
|
||||
return "success"
|
||||
case rules.StatePending:
|
||||
return "warning"
|
||||
case rules.StateFiring:
|
||||
return "danger"
|
||||
default:
|
||||
panic("unknown alert state")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) getTemplate(name string) (string, error) {
|
||||
var tmpl string
|
||||
|
||||
appendf := func(name string) error {
|
||||
f, err := ui.Assets.Open(path.Join("/templates", name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
b, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpl += string(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := appendf("_base.html")
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "error reading base template")
|
||||
}
|
||||
err = appendf(name)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error reading page template %s", name)
|
||||
}
|
||||
|
||||
return tmpl, nil
|
||||
}
|
||||
|
||||
func (h *Handler) executeTemplate(w http.ResponseWriter, name string, data interface{}) {
|
||||
text, err := h.getTemplate(name)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
tmpl := template.NewTemplateExpander(
|
||||
h.context,
|
||||
text,
|
||||
name,
|
||||
data,
|
||||
h.now(),
|
||||
template.QueryFunc(rules.EngineQueryFunc(h.queryEngine, h.storage)),
|
||||
h.options.ExternalURL,
|
||||
nil,
|
||||
)
|
||||
tmpl.Funcs(tmplFuncs(h.consolesPath(), h.options))
|
||||
|
||||
result, err := tmpl.ExpandHTML(nil)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
io.WriteString(w, result)
|
||||
}
|
||||
|
||||
// AlertStatus bundles alerting rules and the mapping of alert states to row classes.
|
||||
type AlertStatus struct {
|
||||
Groups []*rules.Group
|
||||
AlertStateToRowClass map[rules.AlertState]string
|
||||
Counts AlertByStateCount
|
||||
}
|
||||
|
||||
type AlertByStateCount struct {
|
||||
Inactive int32
|
||||
Pending int32
|
||||
Firing int32
|
||||
}
|
||||
|
||||
func setPathWithPrefix(prefix string) func(handlerName string, handler http.HandlerFunc) http.HandlerFunc {
|
||||
return func(handlerName string, handler http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -49,54 +49,6 @@ func TestMain(m *testing.M) {
|
|||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestGlobalURL(t *testing.T) {
|
||||
opts := &Options{
|
||||
ListenAddress: ":9090",
|
||||
ExternalURL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "externalhost:80",
|
||||
Path: "/path/prefix",
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
inURL string
|
||||
outURL string
|
||||
}{
|
||||
{
|
||||
// Nothing should change if the input URL is not on localhost, even if the port is our listening port.
|
||||
inURL: "http://somehost:9090/metrics",
|
||||
outURL: "http://somehost:9090/metrics",
|
||||
},
|
||||
{
|
||||
// Port and host should change if target is on localhost and port is our listening port.
|
||||
inURL: "http://localhost:9090/metrics",
|
||||
outURL: "https://externalhost:80/metrics",
|
||||
},
|
||||
{
|
||||
// Only the host should change if the port is not our listening port, but the host is localhost.
|
||||
inURL: "http://localhost:8000/metrics",
|
||||
outURL: "http://externalhost:8000/metrics",
|
||||
},
|
||||
{
|
||||
// Alternative localhost representations should also work.
|
||||
inURL: "http://127.0.0.1:9090/metrics",
|
||||
outURL: "https://externalhost:80/metrics",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
inURL, err := url.Parse(test.inURL)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
globalURL := tmplFuncs("", opts)["globalURL"].(func(u *url.URL) *url.URL)
|
||||
outURL := globalURL(inURL)
|
||||
|
||||
require.Equal(t, test.outURL, outURL.String())
|
||||
}
|
||||
}
|
||||
|
||||
type dbAdapter struct {
|
||||
*tsdb.DB
|
||||
}
|
||||
|
@ -177,13 +129,6 @@ func TestReadyAndHealthy(t *testing.T) {
|
|||
|
||||
for _, u := range []string{
|
||||
baseURL + "/-/ready",
|
||||
baseURL + "/classic/graph",
|
||||
baseURL + "/classic/flags",
|
||||
baseURL + "/classic/rules",
|
||||
baseURL + "/classic/service-discovery",
|
||||
baseURL + "/classic/targets",
|
||||
baseURL + "/classic/status",
|
||||
baseURL + "/classic/config",
|
||||
} {
|
||||
resp, err = http.Get(u)
|
||||
require.NoError(t, err)
|
||||
|
@ -207,13 +152,6 @@ func TestReadyAndHealthy(t *testing.T) {
|
|||
for _, u := range []string{
|
||||
baseURL + "/-/healthy",
|
||||
baseURL + "/-/ready",
|
||||
baseURL + "/classic/graph",
|
||||
baseURL + "/classic/flags",
|
||||
baseURL + "/classic/rules",
|
||||
baseURL + "/classic/service-discovery",
|
||||
baseURL + "/classic/targets",
|
||||
baseURL + "/classic/status",
|
||||
baseURL + "/classic/config",
|
||||
} {
|
||||
resp, err = http.Get(u)
|
||||
require.NoError(t, err)
|
||||
|
|
Loading…
Reference in a new issue