VictoriaMetrics/lib/promscrape/targets_response.qtpl
Aliaksandr Valialkin d24e5d9efd
lib/promscrape: show the total number of scrapes and the total number of scrape errors per target at /targets page
This information may be useful when debugging unreliable scrape targets
2022-02-03 20:23:27 +02:00

152 lines
6.1 KiB
Text

{% import (
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
) %}
{% stripspace %}
{% func TargetsResponsePlain(jts []jobTargetsStatuses, emptyJobs []string, showOriginLabels bool) %}
{% for _, js := range jts %}
job={%q= js.job %} ({%d js.upCount %}/{%d js.targetsTotal %}{% space %}up)
{% newline %}
{% for _, ts := range js.targetsStatus %}
{%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, onlyUnhealthy bool) %}
<!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>
</head>
<body class="m-3">
<h1>Scrape targets</h1>
<div>
<button type="button" class="btn{% space %}{% if !onlyUnhealthy %}btn-primary{% else %}btn-secondary{% endif %}" onclick="location.href='targets'">
All
</button>
<button type="button" class="btn{% space %}{% if onlyUnhealthy %}btn-primary{% else %}btn-secondary{% endif %}" onclick="location.href='targets?show_only_unhealthy=true'">
Unhealthy
</button>
</div>
{% for i, js := range jts %}
{% if onlyUnhealthy && js.upCount == js.targetsTotal %}{% continue %}{% endif %}
<div>
<h4>
{%s js.job %}{% space %}({%d js.upCount %}/{%d js.targetsTotal %}{% space %}up)
<button type="button" class="btn btn-primary" onclick="document.getElementById('table-{%d i %}').style.display='none'">collapse</button>
<button type="button" class="btn btn-secondary" onclick="document.getElementById('table-{%d i %}').style.display='block'">expand</button>
</h4>
<div id="table-{%d i %}">
<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>
{% for _, ts := range js.targetsStatus %}
{% code
endpoint := ts.sw.Config.ScrapeURL
targetID := getTargetID(ts.sw)
lastScrapeTime := ts.getDurationFromLastScrape()
%}
{% if onlyUnhealthy && ts.up %}{% continue %}{% endif %}
<tr {% if !ts.up %}{%space%}class="alert alert-danger" role="alert"{% endif %}>
<td><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>
<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>
{% endfor %}
{% 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 %}
</body>
</html>
{% 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 %}
{% endstripspace %}