mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-27 02:46:47 +01:00
a3ee275149
* lib/promscrape: Enable filters for endpoint and labels * lib/promscrape: cleanup * lib/promscrape: update template * lib/promscrape: move logic filter logic to backend * lib/promscrape: updated placeholder * lib/promscrape: updated placeholder * lib/promscrape: use two different fields for filters, updated form, added error on parsing queries * lib/promscrape: rename functions * lib/promscrape: removed unused values * wip * wip * wip Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
282 lines
14 KiB
Plaintext
282 lines
14 KiB
Plaintext
{% import (
|
|
"net/url"
|
|
"time"
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
|
|
) %}
|
|
|
|
{% stripspace %}
|
|
|
|
{% func TargetsResponsePlain(jts []jobTargetsStatuses, emptyJobs []string, showOriginLabels, showOnlyUnhealthy bool, err error) %}
|
|
|
|
{% if err != nil %}
|
|
{%s= err.Error() %}
|
|
{% return %}
|
|
{% endif %}
|
|
|
|
{% for _, js := range jts %}
|
|
{% if showOnlyUnhealthy && js.upCount == js.targetsTotal %}{% continue %}{% endif %}
|
|
job={%q= js.job %} ({%d js.upCount %}/{%d js.targetsTotal %}{% space %}up)
|
|
{% newline %}
|
|
{% for _, ts := range js.targetsStatus %}
|
|
{% if showOnlyUnhealthy && ts.up %}{% continue %}{% endif %}
|
|
{%s= "\t" %}
|
|
state={% if ts.up %}up{% else %}down{% endif %},{% space %}
|
|
endpoint={%s= ts.sw.Config.ScrapeURL %},{% space %}
|
|
labels={%s= promLabelsString(promrelabel.FinalizeLabels(nil, ts.sw.Config.Labels)) %},{% space %}
|
|
{% if showOriginLabels %}originalLabels={%s= promLabelsString(ts.sw.Config.OriginalLabels) %},{% space %}{% endif %}
|
|
scrapes_total={%d ts.scrapesTotal %},{% space %}
|
|
scrapes_failed={%d ts.scrapesFailed %},{% space %}
|
|
last_scrape={%f.3 ts.getDurationFromLastScrape().Seconds() %}s ago,{% space %}
|
|
scrape_duration={%d int(ts.scrapeDuration) %}ms,{% space %}
|
|
samples_scraped={%d ts.samplesScraped %},{% space %}
|
|
error={% if ts.err != nil %}{%s= ts.err.Error() %}{% endif %}
|
|
{% newline %}
|
|
{% endfor %}
|
|
{% endfor %}
|
|
|
|
{% for _, jobName := range emptyJobs %}
|
|
job={%q= jobName %} (0/0 up)
|
|
{% newline %}
|
|
{% endfor %}
|
|
|
|
{% endfunc %}
|
|
|
|
{% func TargetsResponseHTML(jts []jobTargetsStatuses, emptyJobs []string, showOnlyUnhealthy bool, endpointSearch, labelSearch string, err error) %}
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
|
<title>Scrape targets</title>
|
|
<script>
|
|
function collapse_all() {
|
|
for (var i = 0; i < {%d len(jts) %}; i++) {
|
|
let el = document.getElementById("table-" + i);
|
|
if (!el) {
|
|
continue;
|
|
}
|
|
el.style.display = 'none';
|
|
}
|
|
}
|
|
function expand_all() {
|
|
for (var i = 0; i < {%d len(jts) %}; i++) {
|
|
let el = document.getElementById("table-" + i);
|
|
if (!el) {
|
|
continue;
|
|
}
|
|
el.style.display = 'block';
|
|
}
|
|
}
|
|
</script>
|
|
</head>
|
|
<body class="py-3">
|
|
<div class="container-fluid">
|
|
{% if err != nil %}
|
|
{%= errorNotification(err) %}
|
|
{% endif %}
|
|
<div class="row">
|
|
<main class="col-12">
|
|
<h1>Scrape targets</h1>
|
|
<hr />
|
|
<div class="row g-3 align-items-center mb-3">
|
|
<div class="col-auto">
|
|
<button type="button" class="btn{% space %}{% if !showOnlyUnhealthy %}btn-primary{% else %}btn-secondary{% endif %}" onclick="location.href='?{%= queryArgs(map[string]string{
|
|
"show_only_unhealthy": "false",
|
|
"endpoint_search": endpointSearch,
|
|
"label_search": labelSearch,
|
|
}) %}'">
|
|
All
|
|
</button>
|
|
</div>
|
|
<div class="col-auto">
|
|
<button type="button" class="btn{% space %}{% if showOnlyUnhealthy %}btn-primary{% else %}btn-secondary{% endif %}" onclick="location.href='?{%= queryArgs(map[string]string{
|
|
"show_only_unhealthy": "true",
|
|
"endpoint_search": endpointSearch,
|
|
"label_search": labelSearch,
|
|
}) %}'">
|
|
Unhealthy
|
|
</button>
|
|
</div>
|
|
<div class="col-auto">
|
|
<button type="button" class="btn btn-primary" onclick="collapse_all()">
|
|
Collapse all
|
|
</button>
|
|
</div>
|
|
<div class="col-auto">
|
|
<button type="button" class="btn btn-secondary" onclick="expand_all()">
|
|
Expand all
|
|
</button>
|
|
</div>
|
|
<div class="col-auto">
|
|
{% if endpointSearch == "" && labelSearch == "" %}
|
|
<button type="button" class="btn btn-primary" onclick="document.getElementById('filters').style.display='block'">
|
|
Filter targets
|
|
</button>
|
|
{% else %}
|
|
<button type="button" class="btn btn-primary" onclick="location.href='?'">
|
|
Clear target filters
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div id="filters" {% if endpointSearch == "" && labelSearch == "" %}style="display:none"{% endif %}>
|
|
<form class="form-horizontal">
|
|
<div class="form-group mb-3">
|
|
<label for="endpoint_search" class="col-sm-10 control-label">Endpoint filter (<a target="_blank" href="https://github.com/google/re2/wiki/Syntax">Regexp</a> is accepted)</label>
|
|
<div class="col-sm-10">
|
|
<input type="text" id="endpoint_search" name="endpoint_search"
|
|
placeholder="For example, 127.0.0.1" class="form-control" value="{%s endpointSearch %}"/>
|
|
</div>
|
|
</div>
|
|
<div class="form-group mb-3">
|
|
<label for="label_search" class="col-sm-10 control-label">Labels filter (<a target="_blank" href="https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors">Arbitrary time series selectors</a> are accepted)</label>
|
|
<div class="col-sm-10">
|
|
<input type="text" id="label_search" name="label_search"
|
|
placeholder="For example, {instance=~'.+:9100'}" class="form-control" value="{%s labelSearch %}"/>
|
|
</div>
|
|
</div>
|
|
<input type="hidden" name="show_only_unhealthy" value="{%v showOnlyUnhealthy %}"/>
|
|
<button type="submit" class="btn btn-success mb-3">Submit</button>
|
|
</form>
|
|
</div>
|
|
<hr />
|
|
<div class="row">
|
|
<div class="col-12">
|
|
{% for i, js := range jts %}
|
|
{% if showOnlyUnhealthy && js.upCount == js.targetsTotal %}{% continue %}{% endif %}
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<h4>
|
|
{%s js.job %}{% space %}({%d js.upCount %}/{%d js.targetsTotal %}{% space %}up)
|
|
</h4>
|
|
<div class="row mb-2">
|
|
<div class="col-12">
|
|
<button type="button" class="btn btn-primary me-1"
|
|
onclick="document.getElementById('table-{%d i %}').style.display='none'">collapse
|
|
</button>
|
|
<button type="button" class="btn btn-secondary me-1"
|
|
onclick="document.getElementById('table-{%d i %}').style.display='block'">expand
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div id="table-{%d i %}" class="table-responsive">
|
|
<table class="table table-striped table-hover table-bordered table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Endpoint</th>
|
|
<th scope="col">State</th>
|
|
<th scope="col" title="scrape target labels">Labels</th>
|
|
<th scope="col" title="total scrapes">Scrapes</th>
|
|
<th scope="col" title="total scrape errors">Errors</th>
|
|
<th scope="col" title="the time of the last scrape">Last Scrape</th>
|
|
<th scope="col" title="the duration of the last scrape">Duration</th>
|
|
<th scope="col" title="the number of metrics scraped during the last scrape">Samples</th>
|
|
<th scope="col" title="error from the last scrape (if any)">Last error</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="list-{%d i %}">
|
|
{% for _, ts := range js.targetsStatus %}
|
|
{% code
|
|
endpoint := ts.sw.Config.ScrapeURL
|
|
targetID := getTargetID(ts.sw)
|
|
lastScrapeTime := ts.getDurationFromLastScrape()
|
|
%}
|
|
{% if showOnlyUnhealthy && ts.up %}{% continue %}{% endif %}
|
|
<tr {% if !ts.up %}{%space%}class="alert alert-danger" role="alert" {% endif %}>
|
|
<td class="endpoint"><a href="{%s endpoint %}" target="_blank">{%s endpoint %}</a> (
|
|
<a href="target_response?id={%s targetID %}" target="_blank"
|
|
title="click to fetch target response on behalf of the scraper">response</a>
|
|
)
|
|
</td>
|
|
<td>{% if ts.up %}UP{% else %}DOWN{% endif %}</td>
|
|
<td class="labels">
|
|
<div title="click to show original labels"
|
|
onclick="document.getElementById('original_labels_{%s targetID %}').style.display='block'">
|
|
{%= formatLabel(promrelabel.FinalizeLabels(nil, ts.sw.Config.Labels)) %}
|
|
</div>
|
|
<div style="display:none" id="original_labels_{%s targetID %}">
|
|
{%= formatLabel(ts.sw.Config.OriginalLabels) %}
|
|
</div>
|
|
</td>
|
|
<td>{%d ts.scrapesTotal %}</td>
|
|
<td>{%d ts.scrapesFailed %}</td>
|
|
<td>
|
|
{% if lastScrapeTime < 365*24*time.Hour %}
|
|
{%f.3 lastScrapeTime.Seconds() %}s ago
|
|
{% else %}
|
|
none
|
|
{% endif %}
|
|
<td>{%d int(ts.scrapeDuration) %}ms</td>
|
|
<td>{%d ts.samplesScraped %}</td>
|
|
<td>{% if ts.err != nil %}{%s ts.err.Error() %}{% endif %}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
{% for _, jobName := range emptyJobs %}
|
|
<div>
|
|
<h4>
|
|
<a>{%s jobName %} (0/0 up)</a>
|
|
</h4>
|
|
<table class="table table-striped table-hover table-bordered table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Endpoint</th>
|
|
<th scope="col">State</th>
|
|
<th scope="col">Labels</th>
|
|
<th scope="col">Last Scrape</th>
|
|
<th scope="col">Scrape Duration</th>
|
|
<th scope="col">Samples Scraped</th>
|
|
<th scope="col">Error</th>
|
|
</tr>
|
|
</thead>
|
|
</table>
|
|
</div>
|
|
{% endfor %}
|
|
</main>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
{% endfunc %}
|
|
|
|
{% func queryArgs(m map[string]string) %}
|
|
{% code
|
|
qa := make(url.Values, len(m))
|
|
for k, v := range m {
|
|
qa[k] = []string{v}
|
|
}
|
|
%}
|
|
{%s qa.Encode() %}
|
|
{% endfunc %}
|
|
|
|
{% func formatLabel(labels []prompbmarshal.Label) %}
|
|
{
|
|
{% for i, label := range labels %}
|
|
{%s label.Name %}={%q label.Value %}
|
|
{% if i+1 < len(labels) %},{% space %}{% endif %}
|
|
{% endfor %}
|
|
}
|
|
{% endfunc %}
|
|
|
|
{% func errorNotification(err error) %}
|
|
<div class="alert alert-danger d-flex align-items-center" role="alert">
|
|
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Danger:">
|
|
<use xlink:href="#exclamation-triangle-fill"/></svg>
|
|
<div>
|
|
{%s err.Error() %}
|
|
</div>
|
|
</div>
|
|
{% endfunc %}
|
|
|
|
{% endstripspace %}
|