VictoriaMetrics/lib/promscrape/targetstatus.qtpl
Aliaksandr Valialkin 49bd2905fa
lib/promscrape: follow-up after 6aa50ca954
- Improve docs
- Hide `debug relabeling` column when -promscrape.dropOriginalLabels command-line flag is set
- Inline the code from the added template functions, since the code is harder to follow
  with the template functions, especially when these functions have misleading names.
  Also, these functions are used only in one place, e.g. they do not reduce the amounts of code.
- Hide `click to show original labels` title at `labels` column when original labels aren't available.
- Show the reason on whey original labels aren't available at /service-discovery page.

Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4597
2023-07-20 19:14:33 -07:00

402 lines
17 KiB
Text

{% import (
"net/url"
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/htmlcomponents"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) %}
{% stripspace %}
{% func TargetsResponsePlain(tsr *targetsStatusResult, filter *requestFilter) %}
{% if tsr.err != nil %}
{%s= tsr.err.Error() %}
{% return %}
{% endif %}
{% for _, jts := range tsr.jobTargetsStatuses %}
job={%s= jts.jobName %}{% space %}({%d jts.upCount %}/{%d jts.targetsTotal %}{% space %}up)
{% newline %}
{% for _, ts := range jts.targetsStatus %}
{%s= "\t" %}
state={% if ts.up %}up{% else %}down{% endif %},{% space %}
endpoint={%s= ts.sw.Config.ScrapeURL %},{% space %}
labels={%s= ts.sw.Config.Labels.String() %},{% space %}
{% if filter.showOriginalLabels %}originalLabels={%s= ts.sw.Config.OriginalLabels.String() %},{% space %}{% endif %}
scrapes_total={%d ts.scrapesTotal %},{% space %}
scrapes_failed={%d ts.scrapesFailed %},{% space %}
last_scrape={%d int(ts.getDurationFromLastScrape().Milliseconds()) %}ms 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 tsr.emptyJobs %}
job={%s= jobName %}{% space %}(0/0 up)
{% newline %}
{% endfor %}
{% endfunc %}
{% func TargetsResponseHTML(tsr *targetsStatusResult, filter *requestFilter) %}
<!DOCTYPE html>
<html lang="en">
<head>
{%= htmlcomponents.CommonHeader() %}
<title>Active Targets</title>
</head>
<body>
{%= htmlcomponents.Navbar() %}
<div class="container-fluid">
{% if tsr.err != nil %}
{%= htmlcomponents.ErrorNotification(tsr.err) %}
{% endif %}
<div class="row">
<main class="col-12">
<h1>Active Targets</h1>
<hr />
{%= filtersForm(filter) %}
<hr />
{%= targetsTabs(tsr, filter, "scrapeTargets") %}
</main>
</div>
</div>
</body>
</html>
{% endfunc %}
{% func ServiceDiscoveryResponse(tsr *targetsStatusResult, filter *requestFilter) %}
<!DOCTYPE html>
<html lang="en">
<head>
{%= htmlcomponents.CommonHeader() %}
<title>Discovered Targets</title>
</head>
<body>
{%= htmlcomponents.Navbar() %}
<div class="container-fluid">
{% if tsr.err != nil %}
{%= htmlcomponents.ErrorNotification(tsr.err) %}
{% endif %}
<div class="row">
<main class="col-12">
<h1>Discovered Targets</h1>
<hr />
{%= filtersForm(filter) %}
<hr />
{%= targetsTabs(tsr, filter, "discoveredTargets") %}
</main>
</div>
</div>
</body>
</html>
{% endfunc %}
{% func filtersForm(filter *requestFilter) %}
<div class="row g-3 align-items-center mb-3">
<div class="col-auto">
<button id="all-btn" type="button" class="btn{% space %}{% if !filter.showOnlyUnhealthy %}btn-secondary{% else %}btn-success{% endif %}"
onclick="location.href='?{%= queryArgs(filter, map[string]string{"show_only_unhealthy": "false"}) %}'">
All
</button>
</div>
<div class="col-auto">
<button id="unhealthy-btn" type="button" class="btn{% space %}{% if filter.showOnlyUnhealthy %}btn-secondary{% else %}btn-danger{% endif %}"
onclick="location.href='?{%= queryArgs(filter, map[string]string{"show_only_unhealthy": "true"}) %}'">
Unhealthy
</button>
</div>
<div class="col-auto">
<button type="button" class="btn btn-primary" onclick="document.querySelectorAll('.scrape-job').forEach((el) => { el.style.display = 'none'; })">
Collapse all
</button>
</div>
<div class="col-auto">
<button type="button" class="btn btn-secondary" onclick="document.querySelectorAll('.scrape-job').forEach((el) => { el.style.display = 'block'; })">
Expand all
</button>
</div>
<div class="col-auto">
<button type="button" class="btn btn-success" onclick="document.getElementById('filters').style.display='block'">
Filter targets
</button>
</div>
</div>
<div id="filters" {% if filter.endpointSearch == "" && filter.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 filter.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 filter.labelSearch %}"/>
</div>
</div>
<input type="hidden" name="show_only_unhealthy" value="{%v filter.showOnlyUnhealthy %}"/>
<input type="hidden" name="show_original_labels" value="{%v filter.showOriginalLabels %}"/>
<button type="submit" class="btn btn-success mb-3">Submit</button>
<button type="button" class="btn btn-danger mb-3" onclick="location.href='?'">Clear target filters</button>
</form>
</div>
{% endfunc %}
{% func targetsTabs(tsr *targetsStatusResult, filter *requestFilter, activeTab string) %}
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link{%if activeTab=="scrapeTargets"%}{% space %}active{%endif%}" type="button" role="tab"
onclick="location.href='targets?{%= queryArgs(filter, nil) %}'">
Active targets
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link{%if activeTab=="discoveredTargets"%}{% space %}active{%endif%}" type="button" role="tab"
onclick="location.href='service-discovery?{%= queryArgs(filter, nil) %}'">
Discovered targets
</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" role="tabpanel">
{% switch activeTab %}
{% case "scrapeTargets" %}
{%= scrapeTargets(tsr) %}
{% case "discoveredTargets" %}
{%= discoveredTargets(tsr) %}
{% endswitch %}
</div>
</div>
{% endfunc %}
{% func scrapeTargets(tsr *targetsStatusResult) %}
<div class="row mt-4">
<div class="col-12">
{% for i, jts := range tsr.jobTargetsStatuses %}
{%= scrapeJobTargets(i, jts, tsr.hasOriginalLabels) %}
{% endfor %}
{% for i, jobName := range tsr.emptyJobs %}
{% code
num := i + len(tsr.jobTargetsStatuses)
jts := &jobTargetsStatuses{
jobName: jobName,
}
%}
{%= scrapeJobTargets(num, jts, tsr.hasOriginalLabels) %}
{% endfor %}
</div>
</div>
{% endfunc %}
{% func scrapeJobTargets(num int, jts *jobTargetsStatuses, hasOriginalLabels bool) %}
<div class="row mb-4">
<div class="col-12">
<h4>
<span class="me-2">{%s jts.jobName %}{% space %}({%d jts.upCount %}/{%d jts.targetsTotal %}{% space %}up)</span>
{%= showHideScrapeJobButtons(num) %}
</h4>
<div id="scrape-job-{%d num %}" class="scrape-job 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="target labels">Labels</th>
{% if hasOriginalLabels %}
<th scope="col" title="debug relabeling">Debug relabeling</th>
{% endif %}
<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>
{% for _, ts := range jts.targetsStatus %}
{% code
endpoint := ts.sw.Config.ScrapeURL
originalLabels := ts.sw.Config.OriginalLabels
// The target is uniquely identified by a pointer to its original labels.
targetID := getLabelsID(originalLabels)
lastScrapeDuration := ts.getDurationFromLastScrape()
%}
<tr {% if !ts.up %}{%space%}class="alert alert-danger" role="alert" {% endif %}>
<td class="endpoint">
<a href="{%s endpoint %}" target="_blank">{%s endpoint %}</a>
{% if hasOriginalLabels %}
{% space %}
(<a href="target_response?id={%s targetID %}" target="_blank"
title="click to fetch target response on behalf of the scraper">response</a>)
{% endif %}
</td>
<td>
{% if ts.up %}
<span class="badge bg-success">UP</span>
{% else %}
<span class="badge bg-danger">DOWN</span>
{% endif %}
</td>
<td class="labels">
<div
{% if hasOriginalLabels %}
{% space %}title="click to show original labels"
onclick="document.getElementById('original-labels-{%s targetID %}').style.display='block'"
{% endif %}
>
{%= formatLabels(ts.sw.Config.Labels) %}
</div>
{% if hasOriginalLabels %}
<div style="display:none" id="original-labels-{%s targetID %}">
{%= formatLabels(originalLabels) %}
</div>
{% endif %}
</td>
{% if hasOriginalLabels %}
<td>
<a href="target-relabel-debug?id={%s targetID %}" target="_blank">target</a>{% space %}
<a href="metric-relabel-debug?id={%s targetID %}" target="_blank">metrics</a>
</td>
{% endif %}
<td>{%d ts.scrapesTotal %}</td>
<td>{%d ts.scrapesFailed %}</td>
<td>
{% if lastScrapeDuration < 365*24*time.Hour %}
{%d int(lastScrapeDuration.Milliseconds()) %}ms 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>
{% endfunc %}
{% func discoveredTargets(tsr *targetsStatusResult) %}
{% if !tsr.hasOriginalLabels %}
Discovered targets are unavailable when -promscrape.dropOriginalLabels command-line flag is set
{% return %}
{% endif %}
{% code tljs := tsr.getTargetLabelsByJob() %}
<div class="row mt-4">
<div class="col-12">
{% for i, tlj := range tljs %}
{%= discoveredJobTargets(i, tlj) %}
{% endfor %}
</div>
</div>
{% endfunc %}
{% func discoveredJobTargets(num int, tlj *targetLabelsByJob) %}
<h4>
<span class="me-2">{%s tlj.jobName %}{% space %}({%d tlj.activeTargets %}/{%d tlj.activeTargets+tlj.droppedTargets %}{% space %}active)</span>
{%= showHideScrapeJobButtons(num) %}
</h4>
<div id="scrape-job-{%d num %}" class="scrape-job table-responsive">
<table class="table table-striped table-hover table-bordered table-sm">
<thead>
<tr>
<th scope="col" style="width: 5%">Status</th>
<th scope="col" style="width: 60%">Discovered Labels</th>
<th scope="col" style="width: 30%">Target Labels</th>
<th scope="col" stile="width: 5%">Debug relabeling</a>
</tr>
</thead>
<tbody>
{% for _, t := range tlj.targets %}
<tr
{% if !t.up %}
{% space %}role="alert"{% space %}
{% if t.labels.Len() > 0 %}
class="alert alert-danger"
{% else %}
class="alert alert-warning"
{% endif %}
{% endif %}
>
<td>
{% if t.up %}
<span class="badge bg-success">UP</span>
{% elseif t.labels.Len() > 0 %}
<span class="badge bg-danger">DOWN</span>
{% else %}
<span class="badge bg-warning">DROPPED</span>
{% endif %}
</td>
<td class="labels">
{%= formatLabels(t.originalLabels) %}
</td>
<td class="labels">
{%= formatLabels(t.labels) %}
</td>
<td>
{% code targetID := getLabelsID(t.originalLabels) %}
<a href="target-relabel-debug?id={%s targetID %}" target="_blank">debug</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endfunc %}
{% func showHideScrapeJobButtons(num int) %}
<button type="button" class="btn btn-primary btn-sm me-1"
onclick="document.getElementById('scrape-job-{%d num %}').style.display='none'">
collapse
</button>
<button type="button" class="btn btn-secondary btn-sm me-1"
onclick="document.getElementById('scrape-job-{%d num %}').style.display='block'">
expand
</button>
{% endfunc %}
{% func queryArgs(filter *requestFilter, override map[string]string) %}
{% code
showOnlyUnhealthy := "false"
if filter.showOnlyUnhealthy {
showOnlyUnhealthy = "true"
}
m := map[string]string{
"show_only_unhealthy": showOnlyUnhealthy,
"endpoint_search": filter.endpointSearch,
"label_search": filter.labelSearch,
}
for k, v := range override {
m[k] = v
}
qa := make(url.Values, len(m))
for k, v := range m {
qa[k] = []string{v}
}
%}
{%s qa.Encode() %}
{% endfunc %}
{% func formatLabels(labels *promutils.Labels) %}
{% code labelsList := labels.GetLabels() %}
{
{% for i, label := range labelsList %}
{%s label.Name %}={%q label.Value %}
{% if i+1 < len(labelsList) %},{% space %}{% endif %}
{% endfor %}
}
{% endfunc %}
{% endstripspace %}