mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-03-11 15:34:56 +00:00
app/vmalert: follow-up after b60dcbe11f
* support case-insensitive search * reflect search condition in URL, so link can be sharable * support filtering on /alerts page * fix collapseAll/expandAll logic to respect only shown entries * add changelogb60dcbe11f
Signed-off-by: hagen1778 <roman@victoriametrics.com> (cherry picked from commit11b03d9fc8
)
This commit is contained in:
parent
f79abd54b0
commit
6c63fd831d
4 changed files with 805 additions and 729 deletions
|
@ -1,10 +1,15 @@
|
||||||
function expandAll() {
|
function expandAll() {
|
||||||
$(".group-heading").show()
|
$('.group-heading').each(function () {
|
||||||
$('.collapse').addClass('show');
|
let style = $(this).attr("style")
|
||||||
|
// display only elements that are currently visible
|
||||||
|
if (style === "display: none;") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$(this).next().addClass('show')
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function collapseAll() {
|
function collapseAll() {
|
||||||
$(".group-heading").show()
|
|
||||||
$('.collapse').removeClass('show');
|
$('.collapse').removeClass('show');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +24,7 @@ function toggleByID(id) {
|
||||||
|
|
||||||
function debounce(func, delay) {
|
function debounce(func, delay) {
|
||||||
let timer;
|
let timer;
|
||||||
return function(...args) {
|
return function (...args) {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
func.apply(this, args);
|
func.apply(this, args);
|
||||||
|
@ -27,75 +32,88 @@ function debounce(func, delay) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#filter').on("keyup", debounce(filter, 500));
|
$('#search').on("keyup", debounce(search, 500));
|
||||||
|
|
||||||
function filter(){
|
// search shows or hides groups&rules that satisfy the search phrase.
|
||||||
$(".rule-table").removeClass('show');
|
// case-insensitive, respects GET param `search`.
|
||||||
|
function search() {
|
||||||
$(".rule").show();
|
$(".rule").show();
|
||||||
|
|
||||||
if($("#filter").val().length === 0){
|
let groupHeader = $(".group-heading")
|
||||||
$(".group-heading").show()
|
let searchPhrase = $("#search").val().toLowerCase()
|
||||||
|
if (searchPhrase.length === 0) {
|
||||||
|
groupHeader.show()
|
||||||
|
setParamURL('search', '')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
$(".group-heading").hide()
|
$(".rule-table").removeClass('show');
|
||||||
|
groupHeader.hide()
|
||||||
|
|
||||||
filterRuleByName();
|
searchPhrase = searchPhrase.toLowerCase()
|
||||||
filterRuleByLabels();
|
filterRuleByName(searchPhrase);
|
||||||
filterGroupsByName();
|
filterRuleByLabels(searchPhrase);
|
||||||
|
filterGroupsByName(searchPhrase);
|
||||||
|
|
||||||
|
setParamURL('search', searchPhrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterGroupsByName(){
|
function setParamURL(key, value) {
|
||||||
$( ".group-heading" ).each(function() {
|
let url = new URL(location.href)
|
||||||
const groupName = $(this).attr('data-group-name');
|
url.searchParams.set(key, value);
|
||||||
const filter = $("#filter").val()
|
window.history.replaceState(null, null, `?${url.searchParams.toString()}`);
|
||||||
const hasValue = groupName.indexOf(filter) >= 0
|
}
|
||||||
|
|
||||||
if (hasValue){
|
function getParamURL(key) {
|
||||||
const target = $(this).attr("data-bs-target");
|
let url = new URL(location.href)
|
||||||
|
return url.searchParams.get(key)
|
||||||
$(this).show();
|
}
|
||||||
$(`div[id="${target}"] .rule`).show();
|
|
||||||
|
function filterGroupsByName(searchPhrase) {
|
||||||
|
$(".group-heading").each(function () {
|
||||||
|
const groupName = $(this).attr('data-group-name').toLowerCase();
|
||||||
|
const hasValue = groupName.indexOf(searchPhrase) >= 0
|
||||||
|
|
||||||
|
if (!hasValue) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const target = $(this).attr("data-bs-target");
|
||||||
|
$(`div[id="${target}"] .rule`).show();
|
||||||
|
$(this).show();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterRuleByName(){
|
function filterRuleByName(searchPhrase) {
|
||||||
$( ".rule" ).each(function() {
|
$(".rule").each(function () {
|
||||||
const ruleName = $(this).attr("data-rule-name");
|
const ruleName = $(this).attr("data-rule-name").toLowerCase();
|
||||||
const filter = $("#filter").val()
|
const hasValue = ruleName.indexOf(searchPhrase) >= 0
|
||||||
const hasValue = ruleName.indexOf(filter) >= 0
|
if (!hasValue) {
|
||||||
|
|
||||||
if (hasValue){
|
|
||||||
const target = $(this).attr('data-bs-target')
|
|
||||||
|
|
||||||
$(`#rules-${target}`).addClass('show');
|
|
||||||
$(`div[data-bs-target='rules-${target}']`).show();
|
|
||||||
$(this).show();
|
|
||||||
}else{
|
|
||||||
$(this).hide();
|
$(this).hide();
|
||||||
|
return
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
const target = $(this).attr('data-bs-target')
|
||||||
|
$(`#rules-${target}`).addClass('show');
|
||||||
|
$(`div[data-bs-target='rules-${target}']`).show();
|
||||||
|
$(this).show();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterRuleByLabels(){
|
function filterRuleByLabels(searchPhrase) {
|
||||||
$( ".rule" ).each(function() {
|
$(".rule").each(function () {
|
||||||
const filter = $("#filter").val()
|
const matches = $(".label", this).filter(function () {
|
||||||
|
const label = $(this).text().toLowerCase();
|
||||||
const matches = $( ".label", this ).filter(function() {
|
return label.indexOf(searchPhrase) >= 0;
|
||||||
const label = $(this).text();
|
}).length;
|
||||||
const hasValue = label.indexOf(filter) >= 0
|
|
||||||
return hasValue;
|
|
||||||
}).length;
|
|
||||||
|
|
||||||
if (matches > 0){
|
if (matches > 0) {
|
||||||
const target = $(this).attr('data-bs-target')
|
const target = $(this).attr('data-bs-target')
|
||||||
|
|
||||||
$(`#rules-${target}`).addClass('show');
|
$(`#rules-${target}`).addClass('show');
|
||||||
$(`div[data-bs-target='rules-${target}']`).show();
|
$(`div[data-bs-target='rules-${target}']`).show();
|
||||||
$(this).show();
|
$(this).show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
@ -115,6 +133,13 @@ $(document).ready(function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// update search element with value from URL, if any
|
||||||
|
let searchPhrase = getParamURL('search')
|
||||||
|
$("#search").val(searchPhrase)
|
||||||
|
|
||||||
|
// apply filtering by search phrase
|
||||||
|
search()
|
||||||
|
|
||||||
let hash = window.location.hash.substr(1);
|
let hash = window.location.hash.substr(1);
|
||||||
toggleByID(hash);
|
toggleByID(hash);
|
||||||
});
|
});
|
||||||
|
|
|
@ -70,19 +70,23 @@ btn-primary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
%}
|
%}
|
||||||
<a class="btn {%= buttonActive(filter, "") %}" role="button" onclick="window.location = window.location.pathname">All</a>
|
<div class="btn-toolbar mb-3" role="toolbar">
|
||||||
<a class="btn btn-primary" role="button" onclick="collapseAll()">Collapse All</a>
|
<div>
|
||||||
<a class="btn btn-primary" role="button" onclick="expandAll()">Expand All</a>
|
<a class="btn {%= buttonActive(filter, "") %}" role="button" onclick="window.location = window.location.pathname">All</a>
|
||||||
<a class="btn {%= buttonActive(filter, "unhealthy") %}" role="button" onclick="location.href='?filter=unhealthy'" title="Show only rules with errors">Unhealthy</a>
|
<a class="btn btn-primary" role="button" onclick="collapseAll()">Collapse All</a>
|
||||||
<a class="btn {%= buttonActive(filter, "noMatch") %}" role="button" onclick="location.href='?filter=noMatch'" title="Show only rules matching no time series during last evaluation">NoMatch</a>
|
<a class="btn btn-primary" role="button" onclick="expandAll()">Expand All</a>
|
||||||
<div class="pt-2 col-md-4 col-lg-4">
|
<a class="btn {%= buttonActive(filter, "unhealthy") %}" role="button" onclick="location.href='?filter=unhealthy'" title="Show only rules with errors">Unhealthy</a>
|
||||||
<div class="input-group">
|
<a class="btn {%= buttonActive(filter, "noMatch") %}" role="button" onclick="location.href='?filter=noMatch'" title="Show only rules matching no time series during last evaluation">NoMatch</a>
|
||||||
<div class="input-group-prepend">
|
</div>
|
||||||
<span class="input-group-text">
|
<div class="col-md-4 col-lg-5">
|
||||||
<svg fill="#000000" height="25px" width="20px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 490.4 490.4" xml:space="preserve"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <g> <path d="M484.1,454.796l-110.5-110.6c29.8-36.3,47.6-82.8,47.6-133.4c0-116.3-94.3-210.6-210.6-210.6S0,94.496,0,210.796 s94.3,210.6,210.6,210.6c50.8,0,97.4-18,133.8-48l110.5,110.5c12.9,11.8,25,4.2,29.2,0C492.5,475.596,492.5,463.096,484.1,454.796z M41.1,210.796c0-93.6,75.9-169.5,169.5-169.5s169.6,75.9,169.6,169.5s-75.9,169.5-169.5,169.5S41.1,304.396,41.1,210.796z"></path> </g> </g></svg>
|
<div class="px-3 input-group">
|
||||||
</span>
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<svg fill="#000000" height="25px" width="20px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 490.4 490.4" xml:space="preserve"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <g> <path d="M484.1,454.796l-110.5-110.6c29.8-36.3,47.6-82.8,47.6-133.4c0-116.3-94.3-210.6-210.6-210.6S0,94.496,0,210.796 s94.3,210.6,210.6,210.6c50.8,0,97.4-18,133.8-48l110.5,110.5c12.9,11.8,25,4.2,29.2,0C492.5,475.596,492.5,463.096,484.1,454.796z M41.1,210.796c0-93.6,75.9-169.5,169.5-169.5s169.6,75.9,169.6,169.5s-75.9,169.5-169.5,169.5S41.1,304.396,41.1,210.796z"></path> </g> </g></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input id="search" placeholder="Filter by group, rule or labels" type="text" class="form-control"/>
|
||||||
</div>
|
</div>
|
||||||
<input id="filter" placeholder="Filter by group, rule or labels" type="text" class="form-control"/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if len(groups) > 0 %}
|
{% if len(groups) > 0 %}
|
||||||
|
@ -180,11 +184,25 @@ btn-primary
|
||||||
{%code prefix := utils.Prefix(r.URL.Path) %}
|
{%code prefix := utils.Prefix(r.URL.Path) %}
|
||||||
{%= tpl.Header(r, navItems, "Alerts", getLastConfigError()) %}
|
{%= tpl.Header(r, navItems, "Alerts", getLastConfigError()) %}
|
||||||
{% if len(groupAlerts) > 0 %}
|
{% if len(groupAlerts) > 0 %}
|
||||||
<a class="btn btn-primary" role="button" onclick="collapseAll()">Collapse All</a>
|
<div class="btn-toolbar mb-3" role="toolbar">
|
||||||
<a class="btn btn-primary" role="button" onclick="expandAll()">Expand All</a>
|
<div>
|
||||||
|
<a class="btn btn-primary" role="button" onclick="collapseAll()">Collapse All</a>
|
||||||
|
<a class="btn btn-primary" role="button" onclick="expandAll()">Expand All</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 col-lg-5">
|
||||||
|
<div class="px-3 input-group">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<svg fill="#000000" height="25px" width="20px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 490.4 490.4" xml:space="preserve"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <g> <path d="M484.1,454.796l-110.5-110.6c29.8-36.3,47.6-82.8,47.6-133.4c0-116.3-94.3-210.6-210.6-210.6S0,94.496,0,210.796 s94.3,210.6,210.6,210.6c50.8,0,97.4-18,133.8-48l110.5,110.5c12.9,11.8,25,4.2,29.2,0C492.5,475.596,492.5,463.096,484.1,454.796z M41.1,210.796c0-93.6,75.9-169.5,169.5-169.5s169.6,75.9,169.6,169.5s-75.9,169.5-169.5,169.5S41.1,304.396,41.1,210.796z"></path> </g> </g></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input id="search" placeholder="Filter by group, rule or labels" type="text" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% for _, ga := range groupAlerts %}
|
{% for _, ga := range groupAlerts %}
|
||||||
{%code g := ga.Group %}
|
{%code g := ga.Group %}
|
||||||
<div class="group-heading alert-danger" data-bs-target="rules-{%s g.ID %}">
|
<div class="group-heading alert-danger" data-bs-target="rules-{%s g.ID %}" data-group-name="{%s g.Name %}">
|
||||||
<span class="anchor" id="group-{%s g.ID %}"></span>
|
<span class="anchor" id="group-{%s g.ID %}"></span>
|
||||||
<a href="#group-{%s g.ID %}">{%s g.Name %}{% if g.Type != "prometheus" %} ({%s g.Type %}){% endif %}</a>
|
<a href="#group-{%s g.ID %}">{%s g.Name %}{% if g.Type != "prometheus" %} ({%s g.Type %}){% endif %}</a>
|
||||||
<span class="badge bg-danger" title="Number of active alerts">{%d len(ga.Alerts) %}</span>
|
<span class="badge bg-danger" title="Number of active alerts">{%d len(ga.Alerts) %}</span>
|
||||||
|
@ -202,7 +220,7 @@ btn-primary
|
||||||
}
|
}
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
%}
|
%}
|
||||||
<div class="collapse" id="rules-{%s g.ID %}">
|
<div class="collapse rule-table" id="rules-{%s g.ID %}">
|
||||||
{% for _, ruleID := range keys %}
|
{% for _, ruleID := range keys %}
|
||||||
{%code
|
{%code
|
||||||
defaultAR := alertsByRule[ruleID][0]
|
defaultAR := alertsByRule[ruleID][0]
|
||||||
|
@ -213,45 +231,46 @@ btn-primary
|
||||||
sort.Strings(labelKeys)
|
sort.Strings(labelKeys)
|
||||||
%}
|
%}
|
||||||
<br>
|
<br>
|
||||||
<b>alert:</b> {%s defaultAR.Name %} ({%d len(alertsByRule[ruleID]) %})
|
<div class="rule" data-rule-name="{%s defaultAR.Name %}" data-bs-target="{%s g.ID %}">
|
||||||
| <span><a target="_blank" href="{%s defaultAR.SourceLink %}">Source</a></span>
|
<b>alert:</b> {%s defaultAR.Name %} ({%d len(alertsByRule[ruleID]) %})
|
||||||
<br>
|
| <span><a target="_blank" href="{%s defaultAR.SourceLink %}">Source</a></span>
|
||||||
<b>expr:</b><code><pre>{%s defaultAR.Expression %}</pre></code>
|
<br>
|
||||||
<table class="table table-striped table-hover table-sm">
|
<b>expr:</b><code><pre>{%s defaultAR.Expression %}</pre></code>
|
||||||
<thead>
|
<table class="table table-striped table-hover table-sm">
|
||||||
<tr>
|
<thead>
|
||||||
<th scope="col">Labels</th>
|
<tr>
|
||||||
<th scope="col">State</th>
|
<th scope="col">Labels</th>
|
||||||
<th scope="col">Active at</th>
|
<th scope="col">State</th>
|
||||||
<th scope="col">Value</th>
|
<th scope="col">Active at</th>
|
||||||
<th scope="col">Link</th>
|
<th scope="col">Value</th>
|
||||||
</tr>
|
<th scope="col">Link</th>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody>
|
</thead>
|
||||||
{% for _, ar := range alertsByRule[ruleID] %}
|
<tbody>
|
||||||
<tr>
|
{% for _, ar := range alertsByRule[ruleID] %}
|
||||||
<td>
|
<tr>
|
||||||
{% for _, k := range labelKeys %}
|
<td>
|
||||||
<span class="ms-1 badge bg-primary">{%s k %}={%s ar.Labels[k] %}</span>
|
{% for _, k := range labelKeys %}
|
||||||
{% endfor %}
|
<span class="ms-1 badge bg-primary label">{%s k %}={%s ar.Labels[k] %}</span>
|
||||||
</td>
|
{% endfor %}
|
||||||
<td>{%= badgeState(ar.State) %}</td>
|
</td>
|
||||||
<td>
|
<td>{%= badgeState(ar.State) %}</td>
|
||||||
{%s ar.ActiveAt.Format("2006-01-02T15:04:05Z07:00") %}
|
<td>
|
||||||
{% if ar.Restored %}{%= badgeRestored() %}{% endif %}
|
{%s ar.ActiveAt.Format("2006-01-02T15:04:05Z07:00") %}
|
||||||
{% if ar.Stabilizing %}{%= badgeStabilizing() %}{% endif %}
|
{% if ar.Restored %}{%= badgeRestored() %}{% endif %}
|
||||||
</td>
|
{% if ar.Stabilizing %}{%= badgeStabilizing() %}{% endif %}
|
||||||
<td>{%s ar.Value %}</td>
|
</td>
|
||||||
<td>
|
<td>{%s ar.Value %}</td>
|
||||||
<a href="{%s prefix+ar.WebLink() %}">Details</a>
|
<td>
|
||||||
</td>
|
<a href="{%s prefix+ar.WebLink() %}">Details</a>
|
||||||
</tr>
|
</td>
|
||||||
{% endfor %}
|
</tr>
|
||||||
</tbody>
|
{% endfor %}
|
||||||
</table>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<br>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -62,6 +62,7 @@ Released at 2024-02-14
|
||||||
* FEATURE: [dashboards/vmagent](https://grafana.com/grafana/dashboards/12683): add `Targets scraped/s` stat panel showing the number of targets scraped by the vmagent per-second.
|
* FEATURE: [dashboards/vmagent](https://grafana.com/grafana/dashboards/12683): add `Targets scraped/s` stat panel showing the number of targets scraped by the vmagent per-second.
|
||||||
* FEATURE: [dashboards/all](https://grafana.com/orgs/victoriametrics): add new panel `CPU spent on GC`. It should help identifying cases when too much CPU is spent on garbage collection, and advice users on how this can be addressed.
|
* FEATURE: [dashboards/all](https://grafana.com/orgs/victoriametrics): add new panel `CPU spent on GC`. It should help identifying cases when too much CPU is spent on garbage collection, and advice users on how this can be addressed.
|
||||||
* FEATURE: [vmalert](https://docs.victoriametrics.com/#vmalert): support [filtering](https://prometheus.io/docs/prometheus/2.49/querying/api/#rules) for `/api/v1/rules` API. See [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5749) by @victoramsantos.
|
* FEATURE: [vmalert](https://docs.victoriametrics.com/#vmalert): support [filtering](https://prometheus.io/docs/prometheus/2.49/querying/api/#rules) for `/api/v1/rules` API. See [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5749) by @victoramsantos.
|
||||||
|
* FEATURE: [vmalert](https://docs.victoriametrics.com/#vmalert): support filtering by group, rule or labels in [vmalert's UI](https://docs.victoriametrics.com/vmalert/#web) for `/groups` and `/alerts` pages. See [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5791) by @victoramsantos.
|
||||||
* FEATURE: [vmbackup](https://docs.victoriametrics.com/vmbackup.html): support client-side TLS configuration for creating and deleting snapshots via `-snapshot.tls*` cmd-line flags. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5724). Thanks to @khushijain21 for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5738).
|
* FEATURE: [vmbackup](https://docs.victoriametrics.com/vmbackup.html): support client-side TLS configuration for creating and deleting snapshots via `-snapshot.tls*` cmd-line flags. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5724). Thanks to @khushijain21 for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5738).
|
||||||
|
|
||||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): reduce CPU usage when `-promscrape.dropOriginalLabels` command-line flag is set. This issue has been introduced in [v1.96.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.96.0) when addressing [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5389).
|
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): reduce CPU usage when `-promscrape.dropOriginalLabels` command-line flag is set. This issue has been introduced in [v1.96.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.96.0) when addressing [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5389).
|
||||||
|
|
Loading…
Reference in a new issue