Compare commits

..

16 Commits

Author SHA1 Message Date
Zhu Jiekun
2843f442da Update docs/victoriametrics/changelog/CHANGELOG.md
Co-authored-by: Max Kotliar <mkotlyar@victoriametrics.com>
Signed-off-by: Zhu Jiekun <jiekun@victoriametrics.com>
2026-06-16 16:01:02 +08:00
Jiekun
9c7196d065 html: hide the reload button if there's no per-URL relabeling rule configured 2026-06-16 15:33:08 +08:00
Jiekun
f8e9bfd62b relabel: add tests for multi relabels 2026-06-16 11:57:31 +08:00
Jiekun
9d960bd6c5 relabel: display currenct selection of relabelconfig correctly 2026-06-16 11:45:15 +08:00
Zhu Jiekun
3b3019fb67 Update lib/promscrape/relabel_debug.go
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Signed-off-by: Zhu Jiekun <jiekun@victoriametrics.com>
2026-06-12 11:29:55 +08:00
Zhu Jiekun
af8720bc86 Merge branch 'master' into feature/enhance-relabel-debug 2026-06-12 11:23:56 +08:00
Jiekun
ecdea28021 revert unnecessary change on WriteURLRelabelConfigData 2026-06-12 11:18:39 +08:00
Jiekun
e0ee9be080 feature: remote write relabel debug 2026-06-12 11:11:40 +08:00
Jiekun
41bf228bb2 feature: remote write relabel debug 2026-06-11 16:48:51 +08:00
Jiekun
b8d60bb716 doc: solve conflict 2026-06-11 11:16:06 +08:00
Jiekun
6db36e244c feature: [relabel debug] remove unnecessary comments 2026-03-08 02:33:25 +08:00
Jiekun
abfd742a0f feature: [relabel debug] remove unnecessary comments 2026-03-08 02:32:47 +08:00
Jiekun
937e3654f3 feature: [relabel debug] simplify the functions 2026-03-08 02:32:11 +08:00
Jiekun
bcbe6d98cc feature: [relabel debug] fix incorrect init 2026-03-08 02:22:51 +08:00
Jiekun
c00ecdde57 feature: [relabel debug] add changelog 2026-03-08 02:16:11 +08:00
Jiekun
ef5174fef3 feature: [relabel debug] add remote write relabel config to debug page 2026-03-08 02:13:52 +08:00
16 changed files with 710 additions and 916 deletions

View File

@@ -462,7 +462,9 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
return true
case "/prometheus/metric-relabel-debug", "/metric-relabel-debug":
promscrapeMetricRelabelDebugRequests.Inc()
promscrape.WriteMetricRelabelDebug(w, r)
rwGlobalRelabelConfigs := remotewrite.GetRemoteWriteRelabelConfigString()
rwURLRelabelConfigss := remotewrite.GetURLRelabelConfigString()
promscrape.WriteMetricRelabelDebug(w, r, rwGlobalRelabelConfigs, rwURLRelabelConfigss)
return true
case "/prometheus/target-relabel-debug", "/target-relabel-debug":
promscrapeTargetRelabelDebugRequests.Inc()

View File

@@ -12,6 +12,7 @@ import (
"github.com/VictoriaMetrics/metrics"
"gopkg.in/yaml.v2"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/flagutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
@@ -82,6 +83,16 @@ func WriteRelabelConfigData(w io.Writer) {
_, _ = w.Write(*p)
}
// GetRemoteWriteRelabelConfigString returns -remoteWrite.relabelConfig contents in string
func GetRemoteWriteRelabelConfigString() string {
var bb bytesutil.ByteBuffer
WriteRelabelConfigData(&bb)
if bb.Len() == 0 {
return ""
}
return string(bb.B)
}
// WriteURLRelabelConfigData writes -remoteWrite.urlRelabelConfig contents to w
func WriteURLRelabelConfigData(w io.Writer) {
p := remoteWriteURLRelabelConfigData.Load()
@@ -108,6 +119,24 @@ func WriteURLRelabelConfigData(w io.Writer) {
_, _ = w.Write(d)
}
// GetURLRelabelConfigString returns -remoteWrite.urlRelabelConfig contents in []string
func GetURLRelabelConfigString() []string {
p := remoteWriteURLRelabelConfigData.Load()
if p == nil {
return nil
}
var ss []string
for i := range *remoteWriteURLs {
cfgData := (*p)[i]
var cfgDataBytes []byte
if cfgData != nil {
cfgDataBytes, _ = yaml.Marshal(cfgData)
}
ss = append(ss, string(cfgDataBytes))
}
return ss
}
func reloadRelabelConfigs() {
rcs := allRelabelConfigs.Load()
if !rcs.isSet() {

View File

@@ -538,7 +538,7 @@ func handleStaticAndSimpleRequests(w http.ResponseWriter, r *http.Request, path
return true
case "/metric-relabel-debug":
promscrapeMetricRelabelDebugRequests.Inc()
promscrape.WriteMetricRelabelDebug(w, r)
promscrape.WriteMetricRelabelDebug(w, r, "", nil)
return true
case "/target-relabel-debug":
promscrapeTargetRelabelDebugRequests.Inc()

View File

@@ -6,348 +6,45 @@ build:
sitemap:
disable: true
---
**Objective**
> [VictoriaMetrics Enterprise](https://docs.victoriametrics.com/victoriametrics/enterprise/) supports specifying multiple retentions for distinct sets of time series and tenants. If you are an Enterprise user, [configure multiple retentions directly through retention filters](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#retention-filters) instead of following this guide.
Setup Victoria Metrics Cluster with support of multiple retention periods within one installation.
This guide explains how to set up multiple retentions using an [open-source VictoriaMetrics Cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/).
**Enterprise Solution**
## Overview
[VictoriaMetrics Enterprise](https://docs.victoriametrics.com/victoriametrics/enterprise/) supports specifying multiple retentions
for distinct sets of time series and [tenants](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#multitenancy)
via [retention filters](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#retention-filters).
VictoriaMetrics retains metrics by default for **1 month**. You can change data retention with the [`-retentionPeriod` command-line flag](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#retention), but this value applies to **all time series stored** on a given `vmstorage` node and cannot be customized per tenant or per metric in the open source version.
**Open Source Solution**
The core idea of this guide is to run **separate logic groups of storages** (or even clusters) with individual `-retentionPeriod` settings, while still providing a single unified write and read path via vmagent and vmselect.
Community version of VictoriaMetrics supports only one retention period per `vmstorage` node via [-retentionPeriod](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#retention) command-line flag.
## Multi-Retention Architecture
A multi-retention setup can be implemented by dividing a [victoriametrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/) into logical groups with different retentions.
To support multiple retentions with the open source version of VictoriaMetrics cluster, you can split the cluster into several logical groups of storage nodes. Each group is configured with a different `-retentionPeriod` and receives only the data that must follow that retention.
Example:
Setup should handle 3 different retention groups 3months, 1year and 3 years.
Solution contains 3 groups of vmstorages + vminserts and one group of vmselects. Routing is done by [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/)
by [splitting data streams](https://docs.victoriametrics.com/victoriametrics/vmagent/#splitting-data-streams-among-multiple-systems).
The [-retentionPeriod](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#retention) sets how long to keep the metrics.
Each storage group is connected to a separate vminsert, while a shared vmselect layer queries across all storage groups so that dashboards and alerts continue to see a single unified VictoriaMetrics backend.
The diagram below shows a proposed solution
![Setup](setup.webp)
In the example used throughout this guide, the cluster is divided into three groups:
**Implementation Details**
- Group A: 3-month retention.
- Group B: 1-year retention.
- Group C: 3-year retention.
1. Groups of vminserts A know about only vmstorages A and this is explicitly specified via `-storageNode` [configuration](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#cluster-setup).
1. Groups of vminserts B know about only vmstorages B and this is explicitly specified via `-storageNode` [configuration](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#cluster-setup).
1. Groups of vminserts C know about only vmstorages C and this is explicitly specified via `-storageNode` [configuration](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#cluster-setup).
1. vmselect reads data from all vmstorage nodes via `-storageNode` [configuration](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#cluster-setup)
with [deduplication](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#deduplication) setting equal to vmagent's scrape interval or minimum interval between collected samples.
1. vmagent routes incoming metrics to the given set of `vminsert` nodes using relabeling rules specified at `-remoteWrite.urlRelabelConfig` [configuration](https://docs.victoriametrics.com/victoriametrics/relabeling/).
Metrics are routed to the appropriate vminsert group by splitting data streams in vmagent, so each time series is sent to exactly one retention group instead of being replicated to all groups. See [Deploying vmagent](https://docs.victoriametrics.com/guides/guide-vmcluster-multiple-retention-setup/#step3) for an example of labelbased routing that implements this split. An optional [vmauth](https://docs.victoriametrics.com/guides/guide-vmcluster-multiple-retention-setup/#additional-enhancements) layer can be added on top to restrict access to specific subclusters or tenants while still keeping a unified write and read path.
**Multi-Tenant Setup**
## Implementing Multi-Retention on Kubernetes
Every group of vmstorages can handle one tenant or multiple one. Different groups can have overlapping tenants. As vmselect reads from all vmstorage nodes, the data is aggregated on its level.
In this section, we'll install and configure the components for a multi-retention deployment of the VictoriaMetrics cluster. See [Kubernetes monitoring with VictoriaMetrics Cluster](https://docs.victoriametrics.com/guides/k8s-monitoring-via-vm-cluster/) for details on running VictoriaMetrics in Kubernetes.
**Additional Enhancements**
Run the following command to add the VictoriaMetrics Helm repository:
```shell
helm repo add vm https://victoriametrics.github.io/helm-charts/
helm repo update
```
### Step 1: Deploying storage groups {#step1}
We'll create three storage groups. Each has a different retention period and disk size. Read [Understand Your Setup Size](https://docs.victoriametrics.com/guides/understand-your-setup-size/) to estimate how much space you will need for each group. The following table is shown as an example:
| Group | Retention Period | Total disk size |
|--------------|------------------|-----------------------|
| `vmcluster-a` | 3 months (`3M`) | 80 Gi |
| `vmcluster-b` | 1 year (`1Y`) | 300 Gi |
| `vmcluster-c` | 3 years (`3Y`) | 900 Gi |
Create a Helm values file for Group A.
```shell
cat <<EOF > vmcluster-a.yaml
vmstorage:
enabled: true
replicaCount: 1
persistence:
size: 80Gi
extraArgs:
retentionPeriod: 3M
podLabels:
retention-group: a
vminsert:
enabled: true
podLabels:
retention-group: a
vmselect:
enabled: false
EOF
```
The values file above creates vminsert and vmstorage services while turning off vmselect, which we'll deploy separately. The `retentionPeriod` flag configures how long data is kept in this group.
Create the values files for Group B and Group C:
```shell
cat <<EOF > vmcluster-b.yaml
vmstorage:
enabled: true
replicaCount: 1
persistence:
size: 300Gi
extraArgs:
retentionPeriod: 1y
podLabels:
retention-group: b
vminsert:
enabled: true
podLabels:
retention-group: b
vmselect:
enabled: false
EOF
cat <<EOF > vmcluster-c.yaml
vmstorage:
enabled: true
replicaCount: 1
persistence:
size: 900Gi
extraArgs:
retentionPeriod: 3y
podLabels:
retention-group: c
vminsert:
enabled: true
podLabels:
retention-group: c
vmselect:
enabled: false
EOF
```
Deploy the three storage groups with:
```shell
helm upgrade --install vmcluster-a vm/victoria-metrics-cluster -f vmcluster-a.yaml
helm upgrade --install vmcluster-b vm/victoria-metrics-cluster -f vmcluster-b.yaml
helm upgrade --install vmcluster-c vm/victoria-metrics-cluster -f vmcluster-c.yaml
# Wait for all storage pods to be ready
kubectl rollout status statefulset -l app.kubernetes.io/instance=vmcluster-a
kubectl rollout status statefulset -l app.kubernetes.io/instance=vmcluster-b
kubectl rollout status statefulset -l app.kubernetes.io/instance=vmcluster-c
```
### Step 2: Deploying vmselect {#step2}
Next, we'll deploy a vmselect service to route queries to the storage groups.
Create a Helm values file with:
```shell
cat <<EOF >vmselect.yaml
vmstorage:
enabled: false
vminsert:
enabled: false
vmselect:
enabled: true
replicaCount: 1
suppressStorageFQDNsRender: true
extraArgs:
# Each list item is a single -storageNode flag. In this example, there is
# one vmstorage pod per retention group, so each entry contains a single host.
# If you run multiple pods per group, list them as comma-separated hosts
# in the same -storageNode value.
#
# The FQDN format is:
# <pod>.<svc>.default.svc
# where pod = <release>-victoria-metrics-cluster-vmstorage-<N>
# and svc = <release>-victoria-metrics-cluster-vmstorage
storageNode:
- "vmcluster-a-victoria-metrics-cluster-vmstorage-0.vmcluster-a-victoria-metrics-cluster-vmstorage.default.svc:8401"
- "vmcluster-b-victoria-metrics-cluster-vmstorage-0.vmcluster-b-victoria-metrics-cluster-vmstorage.default.svc:8401"
- "vmcluster-c-victoria-metrics-cluster-vmstorage-0.vmcluster-c-victoria-metrics-cluster-vmstorage.default.svc:8401"
EOF
```
Let's break down the file above:
- Deploys vmselect as a separate Helm release.
- Disables vminsert and vmstorage as these services were already deployed in Step 1.
- `suppressStorageFQDNsRender: true` turns off automatic FQDN generation for storage nodes. By default, the Helm chart auto-generates `-storageNodes` flags, but since `vmstorage` has been disabled, we need to supply them manually in `extraArgs`.
- In `extraArgs.storageNode:` we define the vmstorage endpoints for queries. On querying, vmselect merges results across all the specified vmstorages to provide a unified view of the data.
Deploy the `vmselect` release with:
```shell
helm upgrade --install vmselect vm/victoria-metrics-cluster -f vmselect.yaml
```
### Step 3: Deploying vmagent {#step3}
We'll use `vmagent` to route incoming metrics to the correct retention group. For example, we can use a `retention` label for mapping metrics to storage groups in the following way:
| `retention` label | Storage Group |
|-------------------|--------------|
| `"3mo"` | `vmcluster-a` |
| `"1yr"` | `vmcluster-b` |
| `"3yr"` | `vmcluster-c` |
Create the values file for vmagent:
```shell
cat <<EOF >vmagent.yaml
service:
enabled: true
remoteWrite:
# Group A: receives metrics with retention="3mo"
- url: http://vmcluster-a-victoria-metrics-cluster-vminsert:8480/insert/0/prometheus/api/v1/write
urlRelabelConfig:
- if: '{retention="3mo"}'
action: keep
# Group B: receives metrics with retention="1yr"
- url: http://vmcluster-b-victoria-metrics-cluster-vminsert:8480/insert/0/prometheus/api/v1/write
urlRelabelConfig:
- if: '{retention="1yr"}'
action: keep
# Group C: receives metrics with retention="3yr"
- url: http://vmcluster-c-victoria-metrics-cluster-vminsert:8480/insert/0/prometheus/api/v1/write
urlRelabelConfig:
- if: '{retention="3yr"}'
action: keep
EOF
```
> Metrics without a matching `retention` label are silently dropped by the `keep` rules. You must ensure that every metric is labeled, or use a different routing configuration.
Now deploy the vmagent release:
```shell
helm upgrade --install vmagent vm/victoria-metrics-agent -f vmagent.yaml
```
Wait for vmagent to become ready:
```shell
kubectl rollout status deploy/vmagent-victoria-metrics-agent
```
### Step 4: Verification
We can send test data to verify that the data is flowing to the correct storage group.
First, port-forward vmagent and vmselect:
```shell
VMAGENT_SVC=$(kubectl get svc -l app.kubernetes.io/instance=vmagent -o jsonpath='{.items[0].metadata.name}')
kubectl port-forward "svc/$VMAGENT_SVC" 8429 &
VMSELECT_SVC=$(kubectl get svc -l app.kubernetes.io/instance=vmselect -o jsonpath='{.items[0].metadata.name}')
kubectl port-forward "svc/$VMSELECT_SVC" 8481 &
```
Send test metrics directly to vmagent's HTTP endpoint to exercise all three retention labels:
```shell
POD=$(kubectl get pod -l app.kubernetes.io/instance=vmagent -o jsonpath='{.items[0].metadata.name}')
for retention in 3mo 1yr 3yr; do
kubectl exec "$POD" -- wget -qO- --post-data="test_routing{retention=\"${retention}\"} 1.0" \
"http://127.0.0.1:8429/api/v1/import/prometheus"
done
```
Query the data back from vmselect (it may take around 30-60 seconds for new data to be available for queries):
```shell
for retention in 3mo 1yr 3yr; do
echo "-> retention=${retention}"
curl -s "http://localhost:8481/select/0/prometheus/api/v1/query" \
--data-urlencode "query=test_routing{retention=\"${retention}\"}"
echo
done
```
You can also check that vmagent is forwarding data to all three groups:
```shell
curl -s http://localhost:8429/metrics | grep vmagent_remotewrite_blocks_sent_total
```
Each `url="N:secret-url"` corresponds to one `remoteWrite` entry (N=1 for Group A, N=2 for Group B, N=3 for Group C). Non-zero values confirm data is flowing.
## Alternative Routing by Existing Labels
The example setup above relies on a synthetic `retention` label to exist in every incoming metric.
If having a `retention` label in every metric isn't practical, you can, as an alternative, rely on existing labels to map data to the correct storage group.
The following example configures vmagent to route metrics based on the `environment` and `team` labels:
```yaml
# vmagent.yaml
remoteWrite:
# send dev and staging data to Group A
- url: "http://vmcluster-a-victoria-metrics-cluster-vminsert:8480/insert/0/prometheus/api/v1/write"
urlRelabelConfig:
- if: {environment=~"dev|staging"}
action: keep
# send prod data to Group B
- url: "http://vmcluster-b-victoria-metrics-cluster-vminsert:8480/insert/0/prometheus/api/v1/write"
urlRelabelConfig:
- if: {environment=~"prod|production"}
action: keep
# send data from Infra and SRE teams to Group C
- url: "http://vmcluster-c-victoria-metrics-cluster-vminsert:8480/insert/0/prometheus/api/v1/write"
urlRelabelConfig:
- if: {team=~"infra|sre"}
action: keep
```
> Metrics that do not match any of the `keep` rules are dropped in the configuration above.
## Additional Enhancements
You can set up [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/) to route data to the specified vminsert group based on the required retention or to restrict which data different users can query.
The following [`-auth.config`](https://docs.victoriametrics.com/victoriametrics/vmauth/#quick-start) example exposes the same vmselect backend via vmauth with two users using basic auth:
- `admin`: can query **all** data across all retention groups.
- `dev`: can query **only** time series that have `team="dev"` label, enforced via the `extra_label` query argument.
```yaml
users:
# User with access to all data across all retention groups
- username: "admin"
password: "foo"
url_map:
- src_paths:
- "/api/v1/query"
- "/api/v1/query_range"
- "/api/v1/series"
- "/api/v1/labels"
- "/api/v1/label/.+/values"
# vmselect service that aggregates all vmstorage groups
url_prefix: "http://vmselect-victoria-metrics-cluster-vmselect:8481/select/0/prometheus"
# User restricted to Dev team data only
- username: "dev"
password: "bar"
url_map:
- src_paths:
- "/api/v1/query"
- "/api/v1/query_range"
- "/api/v1/series"
- "/api/v1/labels"
- "/api/v1/label/.+/values"
# Same vmselect backend, but enforce label filter at query time
# by adding extra_label=team=dev to every proxied request
url_prefix: "http://vmselect-victoria-metrics-cluster-vmselect:8481/select/0/prometheus/?extra_label=team=dev"
```
This is useful for restricting access by team, environment, or tenant without changing the underlying storage topology.
You can set up [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/) for routing data to the given vminsert group depending on the needed retention.

View File

@@ -1712,63 +1712,64 @@ The following versions of VictoriaMetrics receive regular security fixes:
| [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-releases/) | ✅ |
| other releases | ❌ |
### Software Bill of Materials (SBOM)
Every VictoriaMetrics container{{% available_from "v1.137.0" %}} image published to
[Docker Hub](https://hub.docker.com/u/victoriametrics) and [Quay.io](https://quay.io/organization/victoriametrics) include an [SPDX](https://spdx.dev/) SBOM attestation generated automatically by BuildKit during `docker buildx build`.
To inspect the SBOM for an image:
```sh
docker buildx imagetools inspect \
docker.io/victoriametrics/victoria-metrics:latest \
--format "{{ json .SBOM }}"
```
To scan an image using its SBOM attestation with [Trivy](https://github.com/aquasecurity/trivy):
```sh
trivy image --sbom-sources oci \
docker.io/victoriametrics/victoria-metrics:latest
```
### Reporting a Vulnerability
Please report any security issues to <security@victoriametrics.com>
### CVE handling policy
**Source code:** Go dependencies are scanned by [govulncheck](https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck) in CI.
All vulnerabilities must be fixed before the next scheduled release and backported to [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-releases/).
**Docker images:** CVE findings in the [Alpine](https://security.alpinelinux.org/) base image pose minimal risk since VictoriaMetrics binaries are statically compiled with no OS dependencies.
When detected, only the Alpine base tag is updated.
Releases proceed as planned even if upstream fixes are not yet available.
For maximum security, hardened [scratch](https://hub.docker.com/_/scratch)-based images are also provided.
All images are continuously scanned by Docker Hub and verified before release using [grype](https://github.com/anchore/grype).
### General security recommendations:
* All VictoriaMetrics components must run in protected private networks without direct access from untrusted networks such as the Internet.
* All the VictoriaMetrics components must run in protected private networks without direct access from untrusted networks such as Internet.
The exception is [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/) and [vmgateway](https://docs.victoriametrics.com/victoriametrics/vmgateway/),
which are intended for serving public requests and performing authorization with [TLS termination](https://en.wikipedia.org/wiki/TLS_termination_proxy).
* All the requests from untrusted networks to VictoriaMetrics components must go through an auth proxy, such as [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/)
* All the requests from untrusted networks to VictoriaMetrics components must go through auth proxy such as [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/)
or [vmgateway](https://docs.victoriametrics.com/victoriametrics/vmgateway/). The proxy must be set up with proper authentication and authorization.
* Prefer using lists of allowed API endpoints, while disallowing access to other endpoints when configuring [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/)
in front of VictoriaMetrics components.
* Set a reasonable [`Strict-Transport-Security`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) header value on all the components to mitigate [MitM attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack), for example: `max-age=31536000; includeSubDomains`. See `-http.header.hsts` flag.
* Set reasonable [`Strict-Transport-Security`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) header value to all the components to mitigate [MitM attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack), for example: `max-age=31536000; includeSubDomains`. See `-http.header.hsts` flag.
* Set reasonable [`Content-Security-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) header value to mitigate [XSS attacks](https://en.wikipedia.org/wiki/Cross-site_scripting). See `-http.header.csp` flag.
* Set reasonable [`X-Frame-Options`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) header value to mitigate [clickjacking attacks](https://en.wikipedia.org/wiki/Clickjacking), for example `DENY`. See `-http.header.frameOptions` flag.
The following security-related command-line flags are available for all components with HTTP API:
VictoriaMetrics provides the following security-related command-line flags:
* `-tls`, `-tlsCertFile` and `-tlsKeyFile` for switching from HTTP to HTTPS at `-httpListenAddr`.
* `-tls`, `-tlsCertFile` and `-tlsKeyFile` for switching from HTTP to HTTPS at `-httpListenAddr` (TCP port 8428 is listened by default).
[Enterprise version of VictoriaMetrics](https://docs.victoriametrics.com/victoriametrics/enterprise/) supports automatic issuing of TLS certificates.
See [these docs](#automatic-issuing-of-tls-certificates).
* `-mtls` and `-mtlsCAFile` for enabling [mTLS](https://en.wikipedia.org/wiki/Mutual_authentication) for requests to `-httpListenAddr`. See [these docs](#mtls-protection).
* `-httpAuth.username` and `-httpAuth.password` for protecting all the HTTP endpoints
with [HTTP Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication).
* `-http.header.hsts`, `-http.header.csp`, and `-http.header.frameOptions` for serving `Strict-Transport-Security`, `Content-Security-Policy`
and `X-Frame-Options` HTTP response headers.
### Protecting service endpoints
All VictoriaMetrics components expose internal metrics in Prometheus exposition format at the `/metrics` page for [#Monitoring](https://docs.victoriametrics.com/victoriametrics/#monitoring).
Consider limiting access to the `/metrics` page to trusted networks only.
The following service endpoints may require protection:
* `-deleteAuthKey` for protecting the `/api/v1/admin/tsdb/delete_series` endpoint. See [how to delete time series](#how-to-delete-time-series).
* `-snapshotAuthKey` for protecting the `/snapshot*` endpoints. See [how to work with snapshots](#how-to-work-with-snapshots).
* `-forceFlushAuthKey` for protecting the `/internal/force_flush` endpoint. See [force flush docs](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#forced-flush).
* `-forceMergeAuthKey` for protecting the `/internal/force_merge` endpoint. See [force merge docs](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#forced-merge).
* `-search.resetCacheAuthKey` for protecting the `/internal/resetRollupResultCache` endpoint. See [backfilling](#backfilling) for more details.
* `-reloadAuthKey` for protecting the `/-/reload` endpoint, which is used to force reload the [`-promscrape.config`](#how-to-scrape-prometheus-exporters-such-as-node-exporter).
* `-reloadAuthKey` for protecting the `/-/reload` endpoint, which is used for force reloading of [`-promscrape.config`](#how-to-scrape-prometheus-exporters-such-as-node-exporter).
* `-configAuthKey` for protecting the `/config` endpoint, since it may contain sensitive information such as passwords.
* `-flagsAuthKey` for protecting the `/flags` endpoint.
* `-pprofAuthKey` for protecting the `/debug/pprof/*` endpoints, which can be used for [profiling](#profiling).
* `-metricNamesStatsResetAuthKey` for protecting the `/api/v1/admin/status/metric_names_stats/reset` endpoint, used for [Metric Names Tracker](#track-ingested-metrics-usage).
* `-denyQueryTracing` for disallowing [query tracing](#query-tracing).
* `-http.header.hsts`, `-http.header.csp`, and `-http.header.frameOptions` for serving `Strict-Transport-Security`, `Content-Security-Policy`
and `X-Frame-Options` HTTP response headers.
Explicitly set internal network interface for TCP and UDP ports for data ingestion with Graphite and OpenTSDB formats.
For example, substitute `-graphiteListenAddr=:2003` with `-graphiteListenAddr=<internal_iface_ip>:2003`. This protects from unexpected requests from untrusted network interfaces.
@@ -1776,6 +1777,17 @@ For example, substitute `-graphiteListenAddr=:2003` with `-graphiteListenAddr=<i
See also [security recommendation for VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#security)
and [the general security page at VictoriaMetrics website](https://victoriametrics.com/security/).
### CVE handling policy
**Source code:** Go dependencies are scanned by [govulncheck](https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck) in CI.
All vulnerabilities must be fixed before next scheduled release and backported to [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-releases/).
**Docker images:** CVE findings in [Alpine](https://security.alpinelinux.org/) base image pose minimal risk since VictoriaMetrics binaries are statically compiled with no OS dependencies.
When detected, only the Alpine base tag is updated.
Releases proceed as planned even if upstream fixes are not yet available.
For maximum security, hardened [scratch](https://hub.docker.com/_/scratch)-based images are also provided.
All images are continuously scanned by Docker Hub and verified before release using [grype](https://github.com/anchore/grype).
### mTLS protection
By default `VictoriaMetrics` accepts http requests at `8428` port (this port can be changed via `-httpListenAddr` command-line flags).
@@ -1805,39 +1817,19 @@ This functionality can be evaluated for free according to [these docs](https://d
See also [security recommendations](#security).
### Software Bill of Materials (SBOM)
Every VictoriaMetrics container{{% available_from "v1.137.0" %}} image published to
[Docker Hub](https://hub.docker.com/u/victoriametrics) and [Quay.io](https://quay.io/organization/victoriametrics) include an [SPDX](https://spdx.dev/) SBOM attestation generated automatically by BuildKit during `docker buildx build`.
To inspect the SBOM for an image:
```sh
docker buildx imagetools inspect \
docker.io/victoriametrics/victoria-metrics:latest \
--format "{{ json .SBOM }}"
```
To scan an image using its SBOM attestation with [Trivy](https://github.com/aquasecurity/trivy):
```sh
trivy image --sbom-sources oci \
docker.io/victoriametrics/victoria-metrics:latest
```
## Tuning
* No need to tune for VictoriaMetrics - it uses reasonable defaults for command-line flags,
* No need in tuning for VictoriaMetrics - it uses reasonable defaults for command-line flags,
which are automatically adjusted for the available CPU and RAM resources.
* No need to tune for Operating System - VictoriaMetrics is optimized for default OS settings.
* No need in tuning for Operating System - VictoriaMetrics is optimized for default OS settings.
The only option is increasing the limit on [the number of open files in the OS](https://medium.com/@muhammadtriwibowo/set-permanently-ulimit-n-open-files-in-ubuntu-4d61064429a).
The recommendation is not specific to VictoriaMetrics only, but also for any service that handles many HTTP connections and stores data on disk.
* VictoriaMetrics is a write-heavy application, and its performance depends on disk performance. So be careful with other
The recommendation is not specific for VictoriaMetrics only but also for any service which handles many HTTP connections and stores data on disk.
* VictoriaMetrics is a write-heavy application and its performance depends on disk performance. So be careful with other
applications or utilities (like [fstrim](https://manpages.ubuntu.com/manpages/lunar/en/man8/fstrim.8.html))
which could [exhaust disk resources](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1521).
* The recommended filesystem is `ext4`, the recommended persistent storage is [persistent HDD-based disk on GCP](https://cloud.google.com/compute/docs/disks/#pdspecs),
since it is protected from hardware failures via internal replication, and it can be [resized on the fly](https://cloud.google.com/compute/docs/disks/add-persistent-disk#resize_pd).
If you plan to store more than 1TB of data on an `ext4` partition, then the following options are recommended to pass to `mkfs.ext4`:
since it is protected from hardware failures via internal replication and it can be [resized on the fly](https://cloud.google.com/compute/docs/disks/add-persistent-disk#resize_pd).
If you plan to store more than 1TB of data on `ext4` partition, then the following options are recommended to pass to `mkfs.ext4`:
```sh
mkfs.ext4 ... -O 64bit,huge_file,extent -T huge

View File

@@ -26,6 +26,8 @@ See also [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-rel
## tip
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): add support for selecting relabel configurations from `-remoteWrite.relabelConfig` and `-remoteWrite.urlRelabelConfig` in the [metrics relabel debug UI](https://docs.victoriametrics.com/victoriametrics/relabeling/#relabel-debugging). See [#9918](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/9918).
* BUGFIX: [stream aggregation](https://docs.victoriametrics.com/victoriametrics/stream-aggregation/): fix issue with producing aggregated samples with identical timestamps between flushes. See PR [#10808](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10808) for details.
## [v1.145.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.145.0)

View File

@@ -9,47 +9,50 @@ import (
)
// WriteMetricRelabelDebug writes /metric-relabel-debug page to w with the corresponding args.
func WriteMetricRelabelDebug(w io.Writer, targetID, metric, relabelConfigs, format string, err error) {
writeRelabelDebug(w, false, targetID, metric, relabelConfigs, format, err)
func WriteMetricRelabelDebug(w io.Writer, targetID, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, format string, err error) {
writeRelabelDebug(w, false, targetID, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, format, err)
}
// WriteTargetRelabelDebug writes /target-relabel-debug page to w with the corresponding args.
func WriteTargetRelabelDebug(w io.Writer, targetID, metric, relabelConfigs, format string, err error) {
writeRelabelDebug(w, true, targetID, metric, relabelConfigs, format, err)
writeRelabelDebug(w, true, targetID, metric, relabelConfigs, "", 0, 0, format, err)
}
func writeRelabelDebug(w io.Writer, isTargetRelabel bool, targetID, metric, relabelConfigs, format string, err error) {
func writeRelabelDebug(w io.Writer, isTargetRelabel bool, targetID, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, format string, err error) {
if metric == "" {
metric = "{}"
}
targetURL := ""
if err != nil {
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
return
}
metric, err = normalizeInputLabels(metric)
if err != nil {
err = fmt.Errorf("cannot parse metric: %w", err)
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
return
}
labels, err := promutil.NewLabelsFromString(metric)
if err != nil {
err = fmt.Errorf("cannot parse metric: %w", err)
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
return
}
pcs, err := ParseRelabelConfigsData([]byte(relabelConfigs))
// merge relabel configs
fullRelabelConfigs := relabelConfigs + "\n" + rwRelabelConfigs
pcs, err := ParseRelabelConfigsData([]byte(fullRelabelConfigs))
if err != nil {
err = fmt.Errorf("cannot parse relabel configs: %w", err)
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
return
}
dss, targetURL := newDebugRelabelSteps(pcs, labels, isTargetRelabel)
WriteRelabelDebugSteps(w, targetURL, targetID, format, dss, metric, relabelConfigs, nil)
WriteRelabelDebugSteps(w, targetURL, targetID, format, dss, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, nil)
}
func newDebugRelabelSteps(pcs *ParsedConfigs, labels *promutil.Labels, isTargetRelabel bool) ([]DebugStep, string) {

View File

@@ -6,15 +6,15 @@
{% stripspace %}
{% func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, err error) %}
{% func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) %}
{% if format == "json" %}
{%= RelabelDebugStepsJSON(targetURL, targetID, dss, metric, relabelConfigs, err) %}
{%= RelabelDebugStepsJSON(targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err) %}
{% else %}
{%= RelabelDebugStepsHTML(targetURL, targetID, dss, metric, relabelConfigs, err) %}
{%= RelabelDebugStepsHTML(targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err) %}
{% endif %}
{% endfunc %}
{% func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) %}
{% func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) %}
<!DOCTYPE html>
<html lang="en">
<head>
@@ -49,7 +49,7 @@ function submitRelabelDebugForm(e) {
<div class="m-3">
<form method="POST" onsubmit="submitRelabelDebugForm(event)">
{%= relabelDebugFormInputs(metric, relabelConfigs) %}
{%= relabelDebugFormInputs(metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel) %}
{% if targetID != "" %}
<input type="hidden" name="id" value="{%s targetID %}" />
{% endif %}
@@ -70,12 +70,37 @@ function submitRelabelDebugForm(e) {
</html>
{% endfunc %}
{% func relabelDebugFormInputs(metric, relabelConfigs string) %}
{% func relabelDebugFormInputs(metric, relabelConfigs string, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool) %}
<div>
Relabel configs:<br/>
<textarea name="relabel_configs" style="width: 100%; height: 15em; font-family: monospace" class="m-1">{%s relabelConfigs %}</textarea>
</div>
{% if !isTargetRelabel %}
<div>
<div class="m-1">
Remote write relabel configs:
<div class="d-flex align-items-center gap-2 mt-1">
{% if urlRelabelIndexLength > 0 %}
<select name="url_relabel_configs_index" class="form-select form-select-sm w-auto">
{% for i := range urlRelabelIndexLength %}
{% if urlRelabelIndexCurrent == i %}
<option value="{%d i %}" selected="selected">remote-write-url-{%d i %}</option>
{% else %}
<option value="{%d i %}">remote-write-url-{%d i %}</option>
{% endif %}
{% endfor %}
</select>
<input type="submit" name="reload_url_relabel_configs" value="Reload" class="btn btn-secondary btn-sm" />
{% endif %}
</div>
</div>
<textarea name="remote_write_relabel_configs" style="width: 100%; height: 15em; font-family: monospace" class="m-1">{%s rwRelabelConfigs %}</textarea>
</div>
{% endif %}
<div>
Labels:<br/>
<textarea name="metric" style="width: 100%; height: 5em; font-family: monospace" class="m-1">{%s metric %}</textarea>
@@ -151,7 +176,7 @@ function submitRelabelDebugForm(e) {
{% endif %}
{% endfunc %}
{% func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) %}
{% func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) %}
{
{% if err != nil %}
"status": "error",

View File

@@ -25,37 +25,37 @@ var (
)
//line lib/promrelabel/debug.qtpl:9
func StreamRelabelDebugSteps(qw422016 *qt422016.Writer, targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, err error) {
func StreamRelabelDebugSteps(qw422016 *qt422016.Writer, targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) {
//line lib/promrelabel/debug.qtpl:10
if format == "json" {
//line lib/promrelabel/debug.qtpl:11
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, err)
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
//line lib/promrelabel/debug.qtpl:12
} else {
//line lib/promrelabel/debug.qtpl:13
StreamRelabelDebugStepsHTML(qw422016, targetURL, targetID, dss, metric, relabelConfigs, err)
StreamRelabelDebugStepsHTML(qw422016, targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
//line lib/promrelabel/debug.qtpl:14
}
//line lib/promrelabel/debug.qtpl:15
}
//line lib/promrelabel/debug.qtpl:15
func WriteRelabelDebugSteps(qq422016 qtio422016.Writer, targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, err error) {
func WriteRelabelDebugSteps(qq422016 qtio422016.Writer, targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) {
//line lib/promrelabel/debug.qtpl:15
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:15
StreamRelabelDebugSteps(qw422016, targetURL, targetID, format, dss, metric, relabelConfigs, err)
StreamRelabelDebugSteps(qw422016, targetURL, targetID, format, dss, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
//line lib/promrelabel/debug.qtpl:15
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:15
}
//line lib/promrelabel/debug.qtpl:15
func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, err error) string {
func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) string {
//line lib/promrelabel/debug.qtpl:15
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:15
WriteRelabelDebugSteps(qb422016, targetURL, targetID, format, dss, metric, relabelConfigs, err)
WriteRelabelDebugSteps(qb422016, targetURL, targetID, format, dss, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
//line lib/promrelabel/debug.qtpl:15
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:15
@@ -66,7 +66,7 @@ func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metr
}
//line lib/promrelabel/debug.qtpl:17
func StreamRelabelDebugStepsHTML(qw422016 *qt422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
func StreamRelabelDebugStepsHTML(qw422016 *qt422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) {
//line lib/promrelabel/debug.qtpl:17
qw422016.N().S(`<!DOCTYPE html><html lang="en"><head>`)
//line lib/promrelabel/debug.qtpl:21
@@ -120,7 +120,7 @@ func StreamRelabelDebugStepsHTML(qw422016 *qt422016.Writer, targetURL, targetID
//line lib/promrelabel/debug.qtpl:48
qw422016.N().S(`<div class="m-3"><form method="POST" onsubmit="submitRelabelDebugForm(event)">`)
//line lib/promrelabel/debug.qtpl:52
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs)
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel)
//line lib/promrelabel/debug.qtpl:53
if targetID != "" {
//line lib/promrelabel/debug.qtpl:53
@@ -153,22 +153,22 @@ func StreamRelabelDebugStepsHTML(qw422016 *qt422016.Writer, targetURL, targetID
}
//line lib/promrelabel/debug.qtpl:71
func WriteRelabelDebugStepsHTML(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
func WriteRelabelDebugStepsHTML(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) {
//line lib/promrelabel/debug.qtpl:71
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:71
StreamRelabelDebugStepsHTML(qw422016, targetURL, targetID, dss, metric, relabelConfigs, err)
StreamRelabelDebugStepsHTML(qw422016, targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
//line lib/promrelabel/debug.qtpl:71
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:71
}
//line lib/promrelabel/debug.qtpl:71
func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) string {
func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) string {
//line lib/promrelabel/debug.qtpl:71
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:71
WriteRelabelDebugStepsHTML(qb422016, targetURL, targetID, dss, metric, relabelConfigs, err)
WriteRelabelDebugStepsHTML(qb422016, targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
//line lib/promrelabel/debug.qtpl:71
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:71
@@ -179,326 +179,378 @@ func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric,
}
//line lib/promrelabel/debug.qtpl:73
func streamrelabelDebugFormInputs(qw422016 *qt422016.Writer, metric, relabelConfigs string) {
func streamrelabelDebugFormInputs(qw422016 *qt422016.Writer, metric, relabelConfigs string, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool) {
//line lib/promrelabel/debug.qtpl:73
qw422016.N().S(`<div>Relabel configs:<br/><textarea name="relabel_configs" style="width: 100%; height: 15em; font-family: monospace" class="m-1">`)
//line lib/promrelabel/debug.qtpl:76
qw422016.E().S(relabelConfigs)
//line lib/promrelabel/debug.qtpl:76
qw422016.N().S(`</textarea></div><div>Labels:<br/><textarea name="metric" style="width: 100%; height: 5em; font-family: monospace" class="m-1">`)
//line lib/promrelabel/debug.qtpl:81
qw422016.E().S(metric)
//line lib/promrelabel/debug.qtpl:81
qw422016.N().S(`</textarea></div>`)
//line lib/promrelabel/debug.qtpl:83
}
//line lib/promrelabel/debug.qtpl:83
func writerelabelDebugFormInputs(qq422016 qtio422016.Writer, metric, relabelConfigs string) {
//line lib/promrelabel/debug.qtpl:83
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:83
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs)
//line lib/promrelabel/debug.qtpl:83
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:83
}
//line lib/promrelabel/debug.qtpl:83
func relabelDebugFormInputs(metric, relabelConfigs string) string {
//line lib/promrelabel/debug.qtpl:83
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:83
writerelabelDebugFormInputs(qb422016, metric, relabelConfigs)
//line lib/promrelabel/debug.qtpl:83
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:83
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:83
return qs422016
//line lib/promrelabel/debug.qtpl:83
}
//line lib/promrelabel/debug.qtpl:80
if !isTargetRelabel {
//line lib/promrelabel/debug.qtpl:80
qw422016.N().S(`<div><div class="m-1">Remote write relabel configs:<div class="d-flex align-items-center gap-2 mt-1">`)
//line lib/promrelabel/debug.qtpl:85
func streamrelabelDebugSteps(qw422016 *qt422016.Writer, dss []DebugStep, targetURL, targetID string) {
//line lib/promrelabel/debug.qtpl:86
if len(dss) > 0 {
//line lib/promrelabel/debug.qtpl:86
qw422016.N().S(`<div class="m-3"><b>Original labels:</b> <samp>`)
if urlRelabelIndexLength > 0 {
//line lib/promrelabel/debug.qtpl:85
qw422016.N().S(`<select name="url_relabel_configs_index" class="form-select form-select-sm w-auto">`)
//line lib/promrelabel/debug.qtpl:87
for i := range urlRelabelIndexLength {
//line lib/promrelabel/debug.qtpl:88
streammustFormatLabels(qw422016, dss[0].In)
if urlRelabelIndexCurrent == i {
//line lib/promrelabel/debug.qtpl:88
qw422016.N().S(`</samp></div>`)
qw422016.N().S(`<option value="`)
//line lib/promrelabel/debug.qtpl:89
qw422016.N().D(i)
//line lib/promrelabel/debug.qtpl:89
qw422016.N().S(`" selected="selected">remote-write-url-`)
//line lib/promrelabel/debug.qtpl:89
qw422016.N().D(i)
//line lib/promrelabel/debug.qtpl:89
qw422016.N().S(`</option>`)
//line lib/promrelabel/debug.qtpl:90
}
} else {
//line lib/promrelabel/debug.qtpl:90
qw422016.N().S(`<table class="table table-striped table-hover table-bordered table-sm"><thead><tr><th scope="col" style="width: 5%">Step</th><th scope="col" style="width: 25%">Relabeling Rule</th><th scope="col" style="width: 35%">Input Labels</th><th scope="col" stile="width: 35%">Output labels</a></tr></thead><tbody>`)
qw422016.N().S(`<option value="`)
//line lib/promrelabel/debug.qtpl:91
qw422016.N().D(i)
//line lib/promrelabel/debug.qtpl:91
qw422016.N().S(`">remote-write-url-`)
//line lib/promrelabel/debug.qtpl:91
qw422016.N().D(i)
//line lib/promrelabel/debug.qtpl:91
qw422016.N().S(`</option>`)
//line lib/promrelabel/debug.qtpl:92
}
//line lib/promrelabel/debug.qtpl:93
}
//line lib/promrelabel/debug.qtpl:93
qw422016.N().S(`</select><input type="submit" name="reload_url_relabel_configs" value="Reload" class="btn btn-secondary btn-sm" />`)
//line lib/promrelabel/debug.qtpl:96
}
//line lib/promrelabel/debug.qtpl:96
qw422016.N().S(`</div></div><textarea name="remote_write_relabel_configs" style="width: 100%; height: 15em; font-family: monospace" class="m-1">`)
//line lib/promrelabel/debug.qtpl:99
qw422016.E().S(rwRelabelConfigs)
//line lib/promrelabel/debug.qtpl:99
qw422016.N().S(`</textarea></div>`)
//line lib/promrelabel/debug.qtpl:101
}
//line lib/promrelabel/debug.qtpl:101
qw422016.N().S(`<div>Labels:<br/><textarea name="metric" style="width: 100%; height: 5em; font-family: monospace" class="m-1">`)
//line lib/promrelabel/debug.qtpl:106
qw422016.E().S(metric)
//line lib/promrelabel/debug.qtpl:106
qw422016.N().S(`</textarea></div>`)
//line lib/promrelabel/debug.qtpl:108
}
//line lib/promrelabel/debug.qtpl:108
func writerelabelDebugFormInputs(qq422016 qtio422016.Writer, metric, relabelConfigs string, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool) {
//line lib/promrelabel/debug.qtpl:108
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:108
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel)
//line lib/promrelabel/debug.qtpl:108
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:108
}
//line lib/promrelabel/debug.qtpl:108
func relabelDebugFormInputs(metric, relabelConfigs string, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool) string {
//line lib/promrelabel/debug.qtpl:108
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:108
writerelabelDebugFormInputs(qb422016, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel)
//line lib/promrelabel/debug.qtpl:108
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:108
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:108
return qs422016
//line lib/promrelabel/debug.qtpl:108
}
//line lib/promrelabel/debug.qtpl:110
func streamrelabelDebugSteps(qw422016 *qt422016.Writer, dss []DebugStep, targetURL, targetID string) {
//line lib/promrelabel/debug.qtpl:111
if len(dss) > 0 {
//line lib/promrelabel/debug.qtpl:111
qw422016.N().S(`<div class="m-3"><b>Original labels:</b> <samp>`)
//line lib/promrelabel/debug.qtpl:113
streammustFormatLabels(qw422016, dss[0].In)
//line lib/promrelabel/debug.qtpl:113
qw422016.N().S(`</samp></div>`)
//line lib/promrelabel/debug.qtpl:115
}
//line lib/promrelabel/debug.qtpl:115
qw422016.N().S(`<table class="table table-striped table-hover table-bordered table-sm"><thead><tr><th scope="col" style="width: 5%">Step</th><th scope="col" style="width: 25%">Relabeling Rule</th><th scope="col" style="width: 35%">Input Labels</th><th scope="col" stile="width: 35%">Output labels</a></tr></thead><tbody>`)
//line lib/promrelabel/debug.qtpl:126
for i, ds := range dss {
//line lib/promrelabel/debug.qtpl:103
//line lib/promrelabel/debug.qtpl:128
inLabels, inErr := promutil.NewLabelsFromString(ds.In)
outLabels, outErr := promutil.NewLabelsFromString(ds.Out)
changedLabels := getChangedLabelNames(inLabels, outLabels)
//line lib/promrelabel/debug.qtpl:106
qw422016.N().S(`<tr><td>`)
//line lib/promrelabel/debug.qtpl:108
qw422016.N().D(i)
//line lib/promrelabel/debug.qtpl:108
qw422016.N().S(`</td><td><b><pre class="m-2">`)
//line lib/promrelabel/debug.qtpl:109
qw422016.E().S(ds.Rule)
//line lib/promrelabel/debug.qtpl:109
qw422016.N().S(`</pre></b></td><td>`)
//line lib/promrelabel/debug.qtpl:111
if inErr == nil {
//line lib/promrelabel/debug.qtpl:111
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em" title="deleted and updated labels highlighted in red">`)
//line lib/promrelabel/debug.qtpl:113
streamlabelsWithHighlight(qw422016, inLabels, changedLabels, "#D15757")
//line lib/promrelabel/debug.qtpl:113
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:115
} else {
//line lib/promrelabel/debug.qtpl:115
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em; color: red" title="error parsing input labels"><pre>`)
//line lib/promrelabel/debug.qtpl:117
qw422016.E().S(inErr.Error())
//line lib/promrelabel/debug.qtpl:117
qw422016.N().S(`</pre></div>`)
//line lib/promrelabel/debug.qtpl:119
break
//line lib/promrelabel/debug.qtpl:120
}
//line lib/promrelabel/debug.qtpl:120
qw422016.N().S(`</td><td>`)
//line lib/promrelabel/debug.qtpl:123
if outErr == nil {
//line lib/promrelabel/debug.qtpl:123
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em" title="added and updated labels highlighted in blue">`)
//line lib/promrelabel/debug.qtpl:125
streamlabelsWithHighlight(qw422016, outLabels, changedLabels, "#4495e0")
//line lib/promrelabel/debug.qtpl:125
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:127
} else {
//line lib/promrelabel/debug.qtpl:127
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em; color: red" title="error parsing output labels"><pre>`)
//line lib/promrelabel/debug.qtpl:129
qw422016.E().S(outErr.Error())
//line lib/promrelabel/debug.qtpl:129
qw422016.N().S(`</pre></div>`)
//line lib/promrelabel/debug.qtpl:131
break
//line lib/promrelabel/debug.qtpl:132
}
//line lib/promrelabel/debug.qtpl:132
qw422016.N().S(`</td></tr>`)
//line lib/promrelabel/debug.qtpl:135
}
//line lib/promrelabel/debug.qtpl:135
qw422016.N().S(`</tbody></table>`)
qw422016.N().S(`<tr><td>`)
//line lib/promrelabel/debug.qtpl:133
qw422016.N().D(i)
//line lib/promrelabel/debug.qtpl:133
qw422016.N().S(`</td><td><b><pre class="m-2">`)
//line lib/promrelabel/debug.qtpl:134
qw422016.E().S(ds.Rule)
//line lib/promrelabel/debug.qtpl:134
qw422016.N().S(`</pre></b></td><td>`)
//line lib/promrelabel/debug.qtpl:136
if inErr == nil {
//line lib/promrelabel/debug.qtpl:136
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em" title="deleted and updated labels highlighted in red">`)
//line lib/promrelabel/debug.qtpl:138
if len(dss) > 0 {
streamlabelsWithHighlight(qw422016, inLabels, changedLabels, "#D15757")
//line lib/promrelabel/debug.qtpl:138
qw422016.N().S(`<div class="m-3"><b>Resulting labels:</b> <samp>`)
//line lib/promrelabel/debug.qtpl:140
streammustFormatLabels(qw422016, dss[len(dss)-1].Out)
//line lib/promrelabel/debug.qtpl:140
qw422016.N().S(`</samp>`)
//line lib/promrelabel/debug.qtpl:141
if targetURL != "" {
//line lib/promrelabel/debug.qtpl:141
qw422016.N().S(`<div><b>Target URL:</b>`)
//line lib/promrelabel/debug.qtpl:143
qw422016.N().S(` `)
//line lib/promrelabel/debug.qtpl:143
qw422016.N().S(`<a href="`)
//line lib/promrelabel/debug.qtpl:143
qw422016.E().S(targetURL)
//line lib/promrelabel/debug.qtpl:143
qw422016.N().S(`" target="_blank">`)
//line lib/promrelabel/debug.qtpl:143
qw422016.E().S(targetURL)
//line lib/promrelabel/debug.qtpl:143
qw422016.N().S(`</a>`)
//line lib/promrelabel/debug.qtpl:144
if targetID != "" {
//line lib/promrelabel/debug.qtpl:145
qw422016.N().S(` `)
//line lib/promrelabel/debug.qtpl:145
qw422016.N().S(`(<a href="target_response?id=`)
//line lib/promrelabel/debug.qtpl:146
qw422016.E().S(targetID)
//line lib/promrelabel/debug.qtpl:146
qw422016.N().S(`" target="_blank" title="click to fetch target response on behalf of the scraper">response</a>)`)
//line lib/promrelabel/debug.qtpl:147
}
//line lib/promrelabel/debug.qtpl:147
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:149
//line lib/promrelabel/debug.qtpl:140
} else {
//line lib/promrelabel/debug.qtpl:140
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em; color: red" title="error parsing input labels"><pre>`)
//line lib/promrelabel/debug.qtpl:142
qw422016.E().S(inErr.Error())
//line lib/promrelabel/debug.qtpl:142
qw422016.N().S(`</pre></div>`)
//line lib/promrelabel/debug.qtpl:144
break
//line lib/promrelabel/debug.qtpl:145
}
//line lib/promrelabel/debug.qtpl:149
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:151
}
//line lib/promrelabel/debug.qtpl:145
qw422016.N().S(`</td><td>`)
//line lib/promrelabel/debug.qtpl:148
if outErr == nil {
//line lib/promrelabel/debug.qtpl:148
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em" title="added and updated labels highlighted in blue">`)
//line lib/promrelabel/debug.qtpl:150
streamlabelsWithHighlight(qw422016, outLabels, changedLabels, "#4495e0")
//line lib/promrelabel/debug.qtpl:150
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:152
}
} else {
//line lib/promrelabel/debug.qtpl:152
func writerelabelDebugSteps(qq422016 qtio422016.Writer, dss []DebugStep, targetURL, targetID string) {
//line lib/promrelabel/debug.qtpl:152
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:152
streamrelabelDebugSteps(qw422016, dss, targetURL, targetID)
//line lib/promrelabel/debug.qtpl:152
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:152
}
//line lib/promrelabel/debug.qtpl:152
func relabelDebugSteps(dss []DebugStep, targetURL, targetID string) string {
//line lib/promrelabel/debug.qtpl:152
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:152
writerelabelDebugSteps(qb422016, dss, targetURL, targetID)
//line lib/promrelabel/debug.qtpl:152
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:152
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:152
return qs422016
//line lib/promrelabel/debug.qtpl:152
}
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em; color: red" title="error parsing output labels"><pre>`)
//line lib/promrelabel/debug.qtpl:154
func StreamRelabelDebugStepsJSON(qw422016 *qt422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
qw422016.E().S(outErr.Error())
//line lib/promrelabel/debug.qtpl:154
qw422016.N().S(`{`)
qw422016.N().S(`</pre></div>`)
//line lib/promrelabel/debug.qtpl:156
if err != nil {
//line lib/promrelabel/debug.qtpl:156
qw422016.N().S(`"status": "error","error":`)
//line lib/promrelabel/debug.qtpl:158
qw422016.N().Q(fmt.Sprintf("Error: %s", err))
//line lib/promrelabel/debug.qtpl:159
} else {
break
//line lib/promrelabel/debug.qtpl:157
}
//line lib/promrelabel/debug.qtpl:157
qw422016.N().S(`</td></tr>`)
//line lib/promrelabel/debug.qtpl:160
}
//line lib/promrelabel/debug.qtpl:160
qw422016.N().S(`</tbody></table>`)
//line lib/promrelabel/debug.qtpl:163
if len(dss) > 0 {
//line lib/promrelabel/debug.qtpl:163
qw422016.N().S(`<div class="m-3"><b>Resulting labels:</b> <samp>`)
//line lib/promrelabel/debug.qtpl:165
streammustFormatLabels(qw422016, dss[len(dss)-1].Out)
//line lib/promrelabel/debug.qtpl:165
qw422016.N().S(`</samp>`)
//line lib/promrelabel/debug.qtpl:166
if targetURL != "" {
//line lib/promrelabel/debug.qtpl:166
qw422016.N().S(`<div><b>Target URL:</b>`)
//line lib/promrelabel/debug.qtpl:168
qw422016.N().S(` `)
//line lib/promrelabel/debug.qtpl:168
qw422016.N().S(`<a href="`)
//line lib/promrelabel/debug.qtpl:168
qw422016.E().S(targetURL)
//line lib/promrelabel/debug.qtpl:168
qw422016.N().S(`" target="_blank">`)
//line lib/promrelabel/debug.qtpl:168
qw422016.E().S(targetURL)
//line lib/promrelabel/debug.qtpl:168
qw422016.N().S(`</a>`)
//line lib/promrelabel/debug.qtpl:169
if targetID != "" {
//line lib/promrelabel/debug.qtpl:170
qw422016.N().S(` `)
//line lib/promrelabel/debug.qtpl:170
qw422016.N().S(`(<a href="target_response?id=`)
//line lib/promrelabel/debug.qtpl:171
qw422016.E().S(targetID)
//line lib/promrelabel/debug.qtpl:171
qw422016.N().S(`" target="_blank" title="click to fetch target response on behalf of the scraper">response</a>)`)
//line lib/promrelabel/debug.qtpl:172
}
//line lib/promrelabel/debug.qtpl:172
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:174
}
//line lib/promrelabel/debug.qtpl:174
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:176
}
//line lib/promrelabel/debug.qtpl:177
}
//line lib/promrelabel/debug.qtpl:177
func writerelabelDebugSteps(qq422016 qtio422016.Writer, dss []DebugStep, targetURL, targetID string) {
//line lib/promrelabel/debug.qtpl:177
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:177
streamrelabelDebugSteps(qw422016, dss, targetURL, targetID)
//line lib/promrelabel/debug.qtpl:177
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:177
}
//line lib/promrelabel/debug.qtpl:177
func relabelDebugSteps(dss []DebugStep, targetURL, targetID string) string {
//line lib/promrelabel/debug.qtpl:177
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:177
writerelabelDebugSteps(qb422016, dss, targetURL, targetID)
//line lib/promrelabel/debug.qtpl:177
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:177
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:177
return qs422016
//line lib/promrelabel/debug.qtpl:177
}
//line lib/promrelabel/debug.qtpl:179
func StreamRelabelDebugStepsJSON(qw422016 *qt422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) {
//line lib/promrelabel/debug.qtpl:179
qw422016.N().S(`{`)
//line lib/promrelabel/debug.qtpl:181
if err != nil {
//line lib/promrelabel/debug.qtpl:181
qw422016.N().S(`"status": "error","error":`)
//line lib/promrelabel/debug.qtpl:183
qw422016.N().Q(fmt.Sprintf("Error: %s", err))
//line lib/promrelabel/debug.qtpl:184
} else {
//line lib/promrelabel/debug.qtpl:185
var hasError bool
//line lib/promrelabel/debug.qtpl:160
//line lib/promrelabel/debug.qtpl:185
qw422016.N().S(`"status": "success","steps": [`)
//line lib/promrelabel/debug.qtpl:163
//line lib/promrelabel/debug.qtpl:188
for i, ds := range dss {
//line lib/promrelabel/debug.qtpl:165
//line lib/promrelabel/debug.qtpl:190
inLabels, inErr := promutil.NewLabelsFromString(ds.In)
outLabels, outErr := promutil.NewLabelsFromString(ds.Out)
changedLabels := getChangedLabelNames(inLabels, outLabels)
//line lib/promrelabel/debug.qtpl:168
//line lib/promrelabel/debug.qtpl:193
qw422016.N().S(`{"inLabels":`)
//line lib/promrelabel/debug.qtpl:170
//line lib/promrelabel/debug.qtpl:195
qw422016.N().Q(labelsWithHighlight(inLabels, changedLabels, "#D15757"))
//line lib/promrelabel/debug.qtpl:170
//line lib/promrelabel/debug.qtpl:195
qw422016.N().S(`,"outLabels":`)
//line lib/promrelabel/debug.qtpl:171
//line lib/promrelabel/debug.qtpl:196
qw422016.N().Q(labelsWithHighlight(outLabels, changedLabels, "#4495e0"))
//line lib/promrelabel/debug.qtpl:171
//line lib/promrelabel/debug.qtpl:196
qw422016.N().S(`,"rule":`)
//line lib/promrelabel/debug.qtpl:172
qw422016.N().Q(ds.Rule)
//line lib/promrelabel/debug.qtpl:172
qw422016.N().S(`,"errors": {`)
//line lib/promrelabel/debug.qtpl:174
if inErr != nil {
//line lib/promrelabel/debug.qtpl:174
qw422016.N().S(`"inLabels":`)
//line lib/promrelabel/debug.qtpl:175
qw422016.N().Q(`<span style="color: #D15757">` + inErr.Error() + `</span>`)
//line lib/promrelabel/debug.qtpl:175
if outErr != nil {
//line lib/promrelabel/debug.qtpl:175
qw422016.N().S(`,`)
//line lib/promrelabel/debug.qtpl:175
}
//line lib/promrelabel/debug.qtpl:176
hasError = true
//line lib/promrelabel/debug.qtpl:177
} else {
//line lib/promrelabel/debug.qtpl:178
}
//line lib/promrelabel/debug.qtpl:179
if outErr != nil {
//line lib/promrelabel/debug.qtpl:179
qw422016.N().S(`"outLabels":`)
//line lib/promrelabel/debug.qtpl:180
qw422016.N().Q(`<span style="color: #D15757">` + outErr.Error() + `</span>`)
//line lib/promrelabel/debug.qtpl:181
hasError = true
//line lib/promrelabel/debug.qtpl:182
}
//line lib/promrelabel/debug.qtpl:182
qw422016.N().S(`}}`)
//line lib/promrelabel/debug.qtpl:185
if i != len(dss)-1 {
//line lib/promrelabel/debug.qtpl:185
qw422016.N().S(`,`)
//line lib/promrelabel/debug.qtpl:185
}
//line lib/promrelabel/debug.qtpl:186
}
//line lib/promrelabel/debug.qtpl:186
qw422016.N().S(`]`)
//line lib/promrelabel/debug.qtpl:188
if len(dss) > 0 && !hasError {
//line lib/promrelabel/debug.qtpl:188
qw422016.N().S(`,"originalLabels":`)
//line lib/promrelabel/debug.qtpl:190
qw422016.N().Q(mustFormatLabels(dss[0].In))
//line lib/promrelabel/debug.qtpl:190
qw422016.N().S(`,"resultingLabels":`)
//line lib/promrelabel/debug.qtpl:191
qw422016.N().Q(mustFormatLabels(dss[len(dss)-1].Out))
//line lib/promrelabel/debug.qtpl:192
}
//line lib/promrelabel/debug.qtpl:193
}
//line lib/promrelabel/debug.qtpl:193
qw422016.N().S(`}`)
//line lib/promrelabel/debug.qtpl:195
}
//line lib/promrelabel/debug.qtpl:195
func WriteRelabelDebugStepsJSON(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
//line lib/promrelabel/debug.qtpl:195
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:195
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, err)
//line lib/promrelabel/debug.qtpl:195
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:195
}
//line lib/promrelabel/debug.qtpl:195
func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) string {
//line lib/promrelabel/debug.qtpl:195
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:195
WriteRelabelDebugStepsJSON(qb422016, targetURL, targetID, dss, metric, relabelConfigs, err)
//line lib/promrelabel/debug.qtpl:195
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:195
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:195
return qs422016
//line lib/promrelabel/debug.qtpl:195
}
//line lib/promrelabel/debug.qtpl:197
func streamlabelsWithHighlight(qw422016 *qt422016.Writer, labels *promutil.Labels, highlight map[string]struct{}, color string) {
qw422016.N().Q(ds.Rule)
//line lib/promrelabel/debug.qtpl:197
qw422016.N().S(`,"errors": {`)
//line lib/promrelabel/debug.qtpl:199
if inErr != nil {
//line lib/promrelabel/debug.qtpl:199
qw422016.N().S(`"inLabels":`)
//line lib/promrelabel/debug.qtpl:200
qw422016.N().Q(`<span style="color: #D15757">` + inErr.Error() + `</span>`)
//line lib/promrelabel/debug.qtpl:200
if outErr != nil {
//line lib/promrelabel/debug.qtpl:200
qw422016.N().S(`,`)
//line lib/promrelabel/debug.qtpl:200
}
//line lib/promrelabel/debug.qtpl:201
hasError = true
//line lib/promrelabel/debug.qtpl:202
} else {
//line lib/promrelabel/debug.qtpl:203
}
//line lib/promrelabel/debug.qtpl:204
if outErr != nil {
//line lib/promrelabel/debug.qtpl:204
qw422016.N().S(`"outLabels":`)
//line lib/promrelabel/debug.qtpl:205
qw422016.N().Q(`<span style="color: #D15757">` + outErr.Error() + `</span>`)
//line lib/promrelabel/debug.qtpl:206
hasError = true
//line lib/promrelabel/debug.qtpl:207
}
//line lib/promrelabel/debug.qtpl:207
qw422016.N().S(`}}`)
//line lib/promrelabel/debug.qtpl:210
if i != len(dss)-1 {
//line lib/promrelabel/debug.qtpl:210
qw422016.N().S(`,`)
//line lib/promrelabel/debug.qtpl:210
}
//line lib/promrelabel/debug.qtpl:211
}
//line lib/promrelabel/debug.qtpl:211
qw422016.N().S(`]`)
//line lib/promrelabel/debug.qtpl:213
if len(dss) > 0 && !hasError {
//line lib/promrelabel/debug.qtpl:213
qw422016.N().S(`,"originalLabels":`)
//line lib/promrelabel/debug.qtpl:215
qw422016.N().Q(mustFormatLabels(dss[0].In))
//line lib/promrelabel/debug.qtpl:215
qw422016.N().S(`,"resultingLabels":`)
//line lib/promrelabel/debug.qtpl:216
qw422016.N().Q(mustFormatLabels(dss[len(dss)-1].Out))
//line lib/promrelabel/debug.qtpl:217
}
//line lib/promrelabel/debug.qtpl:218
}
//line lib/promrelabel/debug.qtpl:218
qw422016.N().S(`}`)
//line lib/promrelabel/debug.qtpl:220
}
//line lib/promrelabel/debug.qtpl:220
func WriteRelabelDebugStepsJSON(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) {
//line lib/promrelabel/debug.qtpl:220
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:220
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
//line lib/promrelabel/debug.qtpl:220
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:220
}
//line lib/promrelabel/debug.qtpl:220
func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, urlRelabelIndexLength, urlRelabelIndexCurrent int, isTargetRelabel bool, err error) string {
//line lib/promrelabel/debug.qtpl:220
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:220
WriteRelabelDebugStepsJSON(qb422016, targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, urlRelabelIndexLength, urlRelabelIndexCurrent, isTargetRelabel, err)
//line lib/promrelabel/debug.qtpl:220
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:220
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:220
return qs422016
//line lib/promrelabel/debug.qtpl:220
}
//line lib/promrelabel/debug.qtpl:222
func streamlabelsWithHighlight(qw422016 *qt422016.Writer, labels *promutil.Labels, highlight map[string]struct{}, color string) {
//line lib/promrelabel/debug.qtpl:224
labelsList := labels.GetLabels()
metricName := ""
for i, label := range labelsList {
@@ -509,153 +561,153 @@ func streamlabelsWithHighlight(qw422016 *qt422016.Writer, labels *promutil.Label
}
}
//line lib/promrelabel/debug.qtpl:209
//line lib/promrelabel/debug.qtpl:234
if metricName != "" {
//line lib/promrelabel/debug.qtpl:210
//line lib/promrelabel/debug.qtpl:235
if _, ok := highlight["__name__"]; ok {
//line lib/promrelabel/debug.qtpl:210
//line lib/promrelabel/debug.qtpl:235
qw422016.N().S(`<span style="font-weight:bold;color:`)
//line lib/promrelabel/debug.qtpl:211
//line lib/promrelabel/debug.qtpl:236
qw422016.E().S(color)
//line lib/promrelabel/debug.qtpl:211
//line lib/promrelabel/debug.qtpl:236
qw422016.N().S(`">`)
//line lib/promrelabel/debug.qtpl:211
//line lib/promrelabel/debug.qtpl:236
qw422016.E().S(metricName)
//line lib/promrelabel/debug.qtpl:211
//line lib/promrelabel/debug.qtpl:236
qw422016.N().S(`</span>`)
//line lib/promrelabel/debug.qtpl:212
//line lib/promrelabel/debug.qtpl:237
} else {
//line lib/promrelabel/debug.qtpl:213
//line lib/promrelabel/debug.qtpl:238
qw422016.E().S(metricName)
//line lib/promrelabel/debug.qtpl:214
//line lib/promrelabel/debug.qtpl:239
}
//line lib/promrelabel/debug.qtpl:215
//line lib/promrelabel/debug.qtpl:240
if len(labelsList) == 0 {
//line lib/promrelabel/debug.qtpl:215
//line lib/promrelabel/debug.qtpl:240
return
//line lib/promrelabel/debug.qtpl:215
//line lib/promrelabel/debug.qtpl:240
}
//line lib/promrelabel/debug.qtpl:216
//line lib/promrelabel/debug.qtpl:241
}
//line lib/promrelabel/debug.qtpl:216
//line lib/promrelabel/debug.qtpl:241
qw422016.N().S(`{`)
//line lib/promrelabel/debug.qtpl:218
//line lib/promrelabel/debug.qtpl:243
for i, label := range labelsList {
//line lib/promrelabel/debug.qtpl:219
//line lib/promrelabel/debug.qtpl:244
if _, ok := highlight[label.Name]; ok {
//line lib/promrelabel/debug.qtpl:219
//line lib/promrelabel/debug.qtpl:244
qw422016.N().S(`<span style="font-weight:bold;color:`)
//line lib/promrelabel/debug.qtpl:220
//line lib/promrelabel/debug.qtpl:245
qw422016.E().S(color)
//line lib/promrelabel/debug.qtpl:220
//line lib/promrelabel/debug.qtpl:245
qw422016.N().S(`">`)
//line lib/promrelabel/debug.qtpl:220
//line lib/promrelabel/debug.qtpl:245
qw422016.E().S(label.Name)
//line lib/promrelabel/debug.qtpl:220
//line lib/promrelabel/debug.qtpl:245
qw422016.N().S(`=`)
//line lib/promrelabel/debug.qtpl:220
//line lib/promrelabel/debug.qtpl:245
qw422016.E().Q(label.Value)
//line lib/promrelabel/debug.qtpl:220
//line lib/promrelabel/debug.qtpl:245
qw422016.N().S(`</span>`)
//line lib/promrelabel/debug.qtpl:221
//line lib/promrelabel/debug.qtpl:246
} else {
//line lib/promrelabel/debug.qtpl:222
//line lib/promrelabel/debug.qtpl:247
qw422016.E().S(label.Name)
//line lib/promrelabel/debug.qtpl:222
//line lib/promrelabel/debug.qtpl:247
qw422016.N().S(`=`)
//line lib/promrelabel/debug.qtpl:222
//line lib/promrelabel/debug.qtpl:247
qw422016.E().Q(label.Value)
//line lib/promrelabel/debug.qtpl:223
//line lib/promrelabel/debug.qtpl:248
}
//line lib/promrelabel/debug.qtpl:224
//line lib/promrelabel/debug.qtpl:249
if i < len(labelsList)-1 {
//line lib/promrelabel/debug.qtpl:224
//line lib/promrelabel/debug.qtpl:249
qw422016.N().S(`,`)
//line lib/promrelabel/debug.qtpl:224
//line lib/promrelabel/debug.qtpl:249
qw422016.N().S(` `)
//line lib/promrelabel/debug.qtpl:224
//line lib/promrelabel/debug.qtpl:249
}
//line lib/promrelabel/debug.qtpl:225
//line lib/promrelabel/debug.qtpl:250
}
//line lib/promrelabel/debug.qtpl:225
//line lib/promrelabel/debug.qtpl:250
qw422016.N().S(`}`)
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
}
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
func writelabelsWithHighlight(qq422016 qtio422016.Writer, labels *promutil.Labels, highlight map[string]struct{}, color string) {
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
streamlabelsWithHighlight(qw422016, labels, highlight, color)
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
}
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
func labelsWithHighlight(labels *promutil.Labels, highlight map[string]struct{}, color string) string {
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
writelabelsWithHighlight(qb422016, labels, highlight, color)
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
return qs422016
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:252
}
//line lib/promrelabel/debug.qtpl:229
//line lib/promrelabel/debug.qtpl:254
func streammustFormatLabels(qw422016 *qt422016.Writer, s string) {
//line lib/promrelabel/debug.qtpl:230
//line lib/promrelabel/debug.qtpl:255
labels, err := promutil.NewLabelsFromString(s)
//line lib/promrelabel/debug.qtpl:231
//line lib/promrelabel/debug.qtpl:256
if err != nil {
//line lib/promrelabel/debug.qtpl:231
//line lib/promrelabel/debug.qtpl:256
qw422016.N().S(`<span style="color: red" title="error parsing labels:`)
//line lib/promrelabel/debug.qtpl:232
//line lib/promrelabel/debug.qtpl:257
qw422016.E().S(err.Error())
//line lib/promrelabel/debug.qtpl:232
//line lib/promrelabel/debug.qtpl:257
qw422016.N().S(`">`)
//line lib/promrelabel/debug.qtpl:232
//line lib/promrelabel/debug.qtpl:257
qw422016.E().S("error parsing labels: " + err.Error())
//line lib/promrelabel/debug.qtpl:232
//line lib/promrelabel/debug.qtpl:257
qw422016.N().S(`</span>`)
//line lib/promrelabel/debug.qtpl:233
//line lib/promrelabel/debug.qtpl:258
} else {
//line lib/promrelabel/debug.qtpl:234
//line lib/promrelabel/debug.qtpl:259
streamlabelsWithHighlight(qw422016, labels, nil, "")
//line lib/promrelabel/debug.qtpl:235
//line lib/promrelabel/debug.qtpl:260
}
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
}
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
func writemustFormatLabels(qq422016 qtio422016.Writer, s string) {
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
streammustFormatLabels(qw422016, s)
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
}
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
func mustFormatLabels(s string) string {
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
writemustFormatLabels(qb422016, s)
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
return qs422016
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:261
}

View File

@@ -10,10 +10,10 @@ import (
// TestWriteRelabelDebugSupportFormats verifies the relabeling debug input, rules and output.
func TestWriteRelabelDebugSupportFormats(t *testing.T) {
f := func(input, rule, expect string) {
f := func(input, relabelRules, globalRemoteWriteRelabelRules, urlRelabelRules, expect string) {
// execute
outputWriter := bytes.NewBuffer(nil)
writeRelabelDebug(outputWriter, false, "", input, rule, "json", nil)
writeRelabelDebug(outputWriter, false, "", input, relabelRules, globalRemoteWriteRelabelRules+urlRelabelRules, 0, 0, "json", nil)
// the response is in JSON with HTML content, extract the `resultingLabels` in JSON and unescape it.
resultingLabels := fastjson.GetString(outputWriter.Bytes(), `resultingLabels`)
@@ -31,14 +31,30 @@ func TestWriteRelabelDebugSupportFormats(t *testing.T) {
- action: labeldrop
regex: "a_not_exist_label"
`
f(`metric_name`, ruleTestParsing, `metric_name`)
f(`metric_name{label1="value1"}`, ruleTestParsing, `metric_name{label1="value1"}`)
f(`{__name__="metric_name", label1="value1"}`, ruleTestParsing, `metric_name{label1="value1"}`)
f(`__name__="metric_name", label1="value1"`, ruleTestParsing, `metric_name{label1="value1"}`)
f(`_name__="metric_name"`, ruleTestParsing, `{_name__="metric_name"}`)
f(`metric_name`, ruleTestParsing, "", "", `metric_name`)
f(`metric_name{label1="value1"}`, ruleTestParsing, "", "", `metric_name{label1="value1"}`)
f(`{__name__="metric_name", label1="value1"}`, ruleTestParsing, "", "", `metric_name{label1="value1"}`)
f(`__name__="metric_name", label1="value1"`, ruleTestParsing, "", "", `metric_name{label1="value1"}`)
f(`_name__="metric_name"`, ruleTestParsing, "", "", `{_name__="metric_name"}`)
// special case: incorrect input format
f(`{_name__="metric_name"`, ruleTestParsing, ``)
f(`_name__="metric_name}"`, ruleTestParsing, ``)
f(`metrics_name}"`, ruleTestParsing, ``)
f(`{_name__="metric_name"`, ruleTestParsing, "", "", ``)
f(`_name__="metric_name}"`, ruleTestParsing, "", "", ``)
f(`metrics_name}"`, ruleTestParsing, "", "", ``)
// test multiple rules including remote writes
// drop all labels and add one in URL relabeling
rule1 := `
- action: labeldrop
regex: "drop_me_metrics_relabel"
`
rule2 := `
- action: labeldrop
regex: "drop_me_remote_write_relabel"
`
rule3 := `
- target_label: add_me_url_relabel
replacement: added
`
f(`{__name__="metric_name", drop_me_metrics_relabel="1", drop_me_remote_write_relabel="2"}`, rule1, rule2, rule3, `metric_name{add_me_url_relabel="added"}`)
}

View File

@@ -3,34 +3,96 @@ package promscrape
import (
"fmt"
"net/http"
"strconv"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
)
// WriteMetricRelabelDebug serves requests to /metric-relabel-debug page
func WriteMetricRelabelDebug(w http.ResponseWriter, r *http.Request) {
// WriteMetricRelabelDebug serves requests to /metric-relabel-debug page.
// remotewrite-related relabel configs could be empty as vmsingle doesn't provide remote write feature.
func WriteMetricRelabelDebug(w http.ResponseWriter, r *http.Request, rwGlobalRelabelConfigs string, rwURLRelabelConfigss []string) {
targetID := r.FormValue("id")
metric := r.FormValue("metric")
relabelConfigs := r.FormValue("relabel_configs")
rwRelabelConfigs := r.FormValue("remote_write_relabel_configs") // global + per-URL configs.
rwURLRelabelConfigsIdxStr := r.FormValue("url_relabel_configs_index") // only for per-URL configs and has to be set with reload_url_relabel_configs.
reloadRWURLRelabelConfigs := r.FormValue("reload_url_relabel_configs") // if set, it will reset the whole remote_write_relabel_configs.
format := r.FormValue("format")
var err error
if metric == "" && relabelConfigs == "" && targetID != "" {
// if all per-URL config is empty, it means no per-URL rule is configured.
// set it to 0 so the user do not see the options in debug page.
rwURLRelabelConfigsLength := 0
for _, urlRelabelConfig := range rwURLRelabelConfigss {
if urlRelabelConfig != "" {
rwURLRelabelConfigsLength = len(rwURLRelabelConfigss)
break
}
}
rwURLRelabelConfigsIdx, idxErr := strconv.Atoi(rwURLRelabelConfigsIdxStr)
if idxErr != nil {
rwURLRelabelConfigsIdx = -1
}
// if everything is not set, we should load the initial data for user.
if metric == "" && relabelConfigs == "" && rwRelabelConfigs == "" && rwURLRelabelConfigsIdxStr == "" && reloadRWURLRelabelConfigs == "" && targetID != "" {
pcs, labels, ok := getMetricRelabelContextByTargetID(targetID)
if !ok {
err = fmt.Errorf("cannot find target for id=%s", targetID)
targetID = ""
} else {
metric = labels.String()
relabelConfigs = pcs.String()
relabelConfigs += pcs.String()
// by default use the first per-URL remote write relabel config, if exists.
rwURLRelabelConfigs := ""
if len(rwURLRelabelConfigss) > 0 {
rwURLRelabelConfigs = rwURLRelabelConfigss[0]
}
if rwGlobalRelabelConfigs != "" {
rwRelabelConfigs += "\n# -remoteWrite.relabelConfig"
rwRelabelConfigs += "\n" + rwGlobalRelabelConfigs
}
if rwURLRelabelConfigs != "" {
rwRelabelConfigs += "\n# -remoteWrite.urlRelabelConfig"
rwRelabelConfigs += "\n" + rwURLRelabelConfigs
}
}
}
// if reloadRWURLRelabelConfigs is set, it means user clicked the button and want to reload the rwRelabelConfigs by rwURLRelabelConfigsIdx
if reloadRWURLRelabelConfigs != "" {
// set the per-URL remote write relabel according to index, any error will fall back the index to 0.
rwURLRelabelConfigs := ""
if len(rwURLRelabelConfigss) > 0 {
// ignore the error if the input is invalid or exceed the length, and fallback to 0.
if rwURLRelabelConfigsIdx < 0 || rwURLRelabelConfigsIdx >= len(rwURLRelabelConfigss) {
rwURLRelabelConfigsIdx = 0
}
rwURLRelabelConfigs = rwURLRelabelConfigss[rwURLRelabelConfigsIdx]
}
// reload will remove the existing content
if rwGlobalRelabelConfigs != "" {
rwRelabelConfigs = "\n# -remoteWrite.relabelConfig"
rwRelabelConfigs += "\n" + rwGlobalRelabelConfigs
}
if rwURLRelabelConfigs != "" {
rwRelabelConfigs += "\n# -remoteWrite.urlRelabelConfig"
rwRelabelConfigs += "\n" + rwURLRelabelConfigs
}
}
if format == "json" {
httpserver.EnableCORS(w, r)
w.Header().Set("Content-Type", "application/json")
}
promrelabel.WriteMetricRelabelDebug(w, targetID, metric, relabelConfigs, format, err)
promrelabel.WriteMetricRelabelDebug(w, targetID, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigsLength, rwURLRelabelConfigsIdx, format, err)
}
// WriteTargetRelabelDebug generates response for /target-relabel-debug page

View File

@@ -14,10 +14,16 @@ func (av *histogramBucketAggrValue) pushSample(_ aggrConfig, sample *pushSample,
av.h.Update(sample.value)
}
func (av *histogramBucketAggrValue) flush(_ aggrConfig, ctx *flushCtx, key string, _ bool) {
av.shared.Merge(&av.h)
av.h.Reset()
av.shared.VisitNonZeroBuckets(func(vmrange string, count uint64) {
func (av *histogramBucketAggrValue) flush(c aggrConfig, ctx *flushCtx, key string, _ bool) {
ac := c.(*histogramBucketAggrConfig)
shared := av.shared
if ac.useSharedState {
shared.Merge(&av.h)
av.h.Reset()
} else {
shared = &av.h
}
shared.VisitNonZeroBuckets(func(vmrange string, count uint64) {
ctx.appendSeriesWithExtraLabel(key, "histogram_bucket", float64(count), "vmrange", vmrange)
})
}
@@ -26,17 +32,26 @@ func (av *histogramBucketAggrValue) state() any {
return av.shared
}
func newHistogramBucketAggrConfig() aggrConfig {
return &histogramBucketAggrConfig{}
func newHistogramBucketAggrConfig(useSharedState bool) aggrConfig {
return &histogramBucketAggrConfig{
useSharedState: useSharedState,
}
}
type histogramBucketAggrConfig struct{}
type histogramBucketAggrConfig struct {
useSharedState bool
}
func (*histogramBucketAggrConfig) getValue(s any) aggrValue {
if s == nil {
s = &metrics.Histogram{}
func (ac *histogramBucketAggrConfig) getValue(s any) aggrValue {
var shared *metrics.Histogram
if ac.useSharedState {
if s == nil {
shared = &metrics.Histogram{}
} else {
shared = s.(*metrics.Histogram)
}
}
return &histogramBucketAggrValue{
shared: s.(*metrics.Histogram),
shared: shared,
}
}

View File

@@ -1,109 +0,0 @@
package streamaggr
import (
"fmt"
"github.com/VictoriaMetrics/metrics"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
)
type increaseLastValue struct {
value float64
timestamp int64
deleteDeadline int64
}
type increaseAggrConfig struct {
keepFirstSample bool
// The first sample per each new series is ignored until this unix timestamp deadline in seconds even if keepFirstSample is set.
// This allows avoiding an initial spike of the output values at startup when new time series
// cannot be distinguished from already existing series. This is tracked with ignoreFirstSampleDeadline.
ignoreFirstSampleDeadline uint64
counterResetsTotal *metrics.Counter
}
type increaseAggrValue struct {
total *float64
shared map[string]increaseLastValue
}
func (av *increaseAggrValue) pushSample(c aggrConfig, sample *pushSample, key string, deleteDeadline int64) {
ac := c.(*increaseAggrConfig)
currentTime := fasttime.UnixTimestamp()
keepFirstSample := ac.keepFirstSample && currentTime >= ac.ignoreFirstSampleDeadline
lv, ok := av.shared[key]
if av.total == nil {
av.total = new(float64)
}
if ok {
if sample.timestamp < lv.timestamp {
// Skip out of order sample
return
}
if sample.value >= lv.value {
*av.total += sample.value - lv.value
} else {
// counter reset
*av.total += sample.value
ac.counterResetsTotal.Inc()
}
} else if keepFirstSample {
*av.total += sample.value
}
lv.value = sample.value
lv.timestamp = sample.timestamp
lv.deleteDeadline = deleteDeadline
key = bytesutil.InternString(key)
av.shared[key] = lv
}
func (av *increaseAggrValue) flush(c aggrConfig, ctx *flushCtx, key string, isLast bool) {
ac := c.(*increaseAggrConfig)
for lk, lv := range av.shared {
if ctx.flushTimestamp > lv.deleteDeadline || isLast {
delete(av.shared, lk)
}
}
if av.total == nil {
return
}
total := *av.total
av.total = nil
ctx.appendSeries(key, ac.getSuffix(), total)
}
func (av *increaseAggrValue) state() any {
return av.shared
}
func newIncreaseAggrConfig(ms *metrics.Set, metricLabels string, ignoreFirstSampleIntervalSecs uint64, keepFirstSample bool) aggrConfig {
ignoreFirstSampleDeadline := fasttime.UnixTimestamp() + ignoreFirstSampleIntervalSecs
cfg := &increaseAggrConfig{
keepFirstSample: keepFirstSample,
ignoreFirstSampleDeadline: ignoreFirstSampleDeadline,
}
cfg.counterResetsTotal = ms.NewCounter(fmt.Sprintf(`vm_streamaggr_counter_resets_total{%s}`, metricLabels))
return cfg
}
func (*increaseAggrConfig) getValue(s any) aggrValue {
var shared map[string]increaseLastValue
if s == nil {
shared = make(map[string]increaseLastValue)
} else {
shared = s.(map[string]increaseLastValue)
}
return &increaseAggrValue{
shared: shared,
}
}
func (ac *increaseAggrConfig) getSuffix() string {
if ac.keepFirstSample {
return "increase"
}
return "increase_prometheus"
}

View File

@@ -75,9 +75,6 @@ func (ao *aggrOutputs) pushSamples(samples []pushSample, deleteDeadline int64, i
outputs = av.blue
}
for idx, o := range outputs {
if o == nil {
o = av.blue[idx]
}
o.pushSample(ao.configs[idx], sample, inputKey, deleteDeadline)
}
av.deleteDeadline = deleteDeadline
@@ -115,9 +112,6 @@ func (ao *aggrOutputs) flushState(ctx *flushCtx) {
outputs = av.blue
}
for i, o := range outputs {
if o == nil {
o = av.blue[i]
}
o.flush(ao.configs[i], ctx, outputKey, ctx.isLast)
}
av.mu.Unlock()

View File

@@ -609,7 +609,7 @@ func newAggregator(cfg *Config, path string, pushFunc PushFunc, ms *metrics.Set,
outputsSeen := make(map[string]struct{}, len(cfg.Outputs))
for i, output := range cfg.Outputs {
outputMetricLabels := fmt.Sprintf(`output=%q,name=%q,path=%q,url=%q,position="%d"`, output, name, path, alias, aggrID)
ac, err := newOutputConfig(ms, outputMetricLabels, output, outputsSeen, ignoreFirstSampleInterval)
ac, err := newOutputConfig(ms, outputMetricLabels, output, outputsSeen, useSharedState, ignoreFirstSampleInterval)
if err != nil {
return nil, err
}
@@ -716,7 +716,7 @@ func newAggregator(cfg *Config, path string, pushFunc PushFunc, ms *metrics.Set,
return a, nil
}
func newOutputConfig(ms *metrics.Set, metricLabels, output string, outputsSeen map[string]struct{}, ignoreFirstSampleInterval time.Duration) (aggrConfig, error) {
func newOutputConfig(ms *metrics.Set, metricLabels, output string, outputsSeen map[string]struct{}, useSharedState bool, ignoreFirstSampleInterval time.Duration) (aggrConfig, error) {
// check for duplicated output
if _, ok := outputsSeen[output]; ok {
return nil, fmt.Errorf("`outputs` list contains duplicate aggregation function: %s", output)
@@ -760,11 +760,11 @@ func newOutputConfig(ms *metrics.Set, metricLabels, output string, outputsSeen m
case "count_series":
return newCountSeriesAggrConfig(), nil
case "histogram_bucket":
return newHistogramBucketAggrConfig(), nil
return newHistogramBucketAggrConfig(useSharedState), nil
case "increase":
return newIncreaseAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, true), nil
return newTotalAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, true, true), nil
case "increase_prometheus":
return newIncreaseAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, false), nil
return newTotalAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, true, false), nil
case "last":
return newLastAggrConfig(), nil
case "max":
@@ -782,9 +782,9 @@ func newOutputConfig(ms *metrics.Set, metricLabels, output string, outputsSeen m
case "sum_samples":
return newSumSamplesAggrConfig(), nil
case "total":
return newTotalAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, true), nil
return newTotalAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, false, true), nil
case "total_prometheus":
return newTotalAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, false), nil
return newTotalAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, false, false), nil
case "unique_samples":
return newUniqueSamplesAggrConfig(), nil
default:

View File

@@ -53,30 +53,36 @@ func (av *totalAggrValue) pushSample(c aggrConfig, sample *pushSample, key strin
func (av *totalAggrValue) flush(c aggrConfig, ctx *flushCtx, key string, isLast bool) {
ac := c.(*totalAggrConfig)
suffix := ac.getSuffix()
// check for stale entries
total := av.shared.total + av.total
av.total = 0
for lk, lv := range av.shared.lastValues {
lvs := av.shared.lastValues
for lk, lv := range lvs {
if ctx.flushTimestamp > lv.deleteDeadline || isLast {
delete(av.shared.lastValues, lk)
delete(lvs, lk)
}
}
if math.Abs(total) >= (1 << 53) {
if ac.resetTotalOnFlush {
av.shared.total = 0
} else if math.Abs(total) >= (1 << 53) {
// It is time to reset the entry, since it starts losing float64 precision
av.shared.total = 0
} else {
av.shared.total = total
}
ctx.appendSeries(key, ac.getSuffix(), total)
ctx.appendSeries(key, suffix, total)
}
func (av *totalAggrValue) state() any {
return av.shared
}
func newTotalAggrConfig(ms *metrics.Set, metricLabels string, ignoreFirstSampleIntervalSecs uint64, keepFirstSample bool) aggrConfig {
func newTotalAggrConfig(ms *metrics.Set, metricLabels string, ignoreFirstSampleIntervalSecs uint64, resetTotalOnFlush, keepFirstSample bool) aggrConfig {
ignoreFirstSampleDeadline := fasttime.UnixTimestamp() + ignoreFirstSampleIntervalSecs
cfg := &totalAggrConfig{
keepFirstSample: keepFirstSample,
resetTotalOnFlush: resetTotalOnFlush,
ignoreFirstSampleDeadline: ignoreFirstSampleDeadline,
}
cfg.counterResetsTotal = ms.NewCounter(fmt.Sprintf(`vm_streamaggr_counter_resets_total{%s}`, metricLabels))
@@ -84,6 +90,8 @@ func newTotalAggrConfig(ms *metrics.Set, metricLabels string, ignoreFirstSampleI
}
type totalAggrConfig struct {
resetTotalOnFlush bool
// Whether to take into account the first sample in new time series when calculating the output value.
keepFirstSample bool
@@ -109,6 +117,12 @@ func (*totalAggrConfig) getValue(s any) aggrValue {
}
func (ac *totalAggrConfig) getSuffix() string {
if ac.resetTotalOnFlush {
if ac.keepFirstSample {
return "increase"
}
return "increase_prometheus"
}
if ac.keepFirstSample {
return "total"
}