mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2026-06-14 14:24:00 +03:00
Compare commits
3 Commits
feature/en
...
docs/file-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60de3bad8e | ||
|
|
05903c8acd | ||
|
|
a9fae230ae |
@@ -462,9 +462,7 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
|
||||
return true
|
||||
case "/prometheus/metric-relabel-debug", "/metric-relabel-debug":
|
||||
promscrapeMetricRelabelDebugRequests.Inc()
|
||||
rwGlobalRelabelConfigs := remotewrite.GetRemoteWriteRelabelConfigString()
|
||||
rwURLRelabelConfigss := remotewrite.GetURLRelabelConfigString()
|
||||
promscrape.WriteMetricRelabelDebug(w, r, rwGlobalRelabelConfigs, rwURLRelabelConfigss)
|
||||
promscrape.WriteMetricRelabelDebug(w, r)
|
||||
return true
|
||||
case "/prometheus/target-relabel-debug", "/target-relabel-debug":
|
||||
promscrapeTargetRelabelDebugRequests.Inc()
|
||||
|
||||
@@ -12,7 +12,6 @@ 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"
|
||||
@@ -83,16 +82,6 @@ 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()
|
||||
@@ -119,24 +108,6 @@ 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() {
|
||||
|
||||
@@ -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, "", nil)
|
||||
promscrape.WriteMetricRelabelDebug(w, r)
|
||||
return true
|
||||
case "/target-relabel-debug":
|
||||
promscrapeTargetRelabelDebugRequests.Inc()
|
||||
|
||||
@@ -6,45 +6,348 @@ build:
|
||||
sitemap:
|
||||
disable: true
|
||||
---
|
||||
**Objective**
|
||||
|
||||
Setup Victoria Metrics Cluster with support of multiple retention periods within one installation.
|
||||
> [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.
|
||||
|
||||
**Enterprise Solution**
|
||||
This guide explains how to set up multiple retentions using an [open-source VictoriaMetrics Cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/).
|
||||
|
||||
[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).
|
||||
## Overview
|
||||
|
||||
**Open Source Solution**
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
## Multi-Retention Architecture
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
The diagram below shows a proposed solution
|
||||
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.
|
||||
|
||||

|
||||
|
||||
**Implementation Details**
|
||||
In the example used throughout this guide, the cluster is divided into three groups:
|
||||
|
||||
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/).
|
||||
- Group A: 3-month retention.
|
||||
- Group B: 1-year retention.
|
||||
- Group C: 3-year retention.
|
||||
|
||||
**Multi-Tenant Setup**
|
||||
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 label‑based 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 sub‑clusters or tenants while still keeping a unified write and read path.
|
||||
|
||||
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.
|
||||
## Implementing Multi-Retention on Kubernetes
|
||||
|
||||
**Additional Enhancements**
|
||||
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.
|
||||
|
||||
You can set up [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/) for routing data to the given vminsert group depending on the needed retention.
|
||||
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.
|
||||
|
||||
@@ -26,8 +26,6 @@ See also [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-rel
|
||||
|
||||
## tip
|
||||
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): enhance metrics relabel debug by adding remote write relabel configs to the relabel configs input. 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)
|
||||
|
||||
@@ -546,7 +546,7 @@ tags at [Docker Hub](https://hub.docker.com/r/victoriametrics/vmalert/tags) and
|
||||
|
||||
## Reading rules from object storage
|
||||
|
||||
[Enterprise version](https://docs.victoriametrics.com/victoriametrics/enterprise/) of `vmalert` may read alerting and recording rules
|
||||
The [Enterprise version](https://docs.victoriametrics.com/victoriametrics/enterprise/) of `vmalert` may read alerting and recording rules
|
||||
from object storage:
|
||||
|
||||
* `./bin/vmalert -rule=s3://bucket/dir/alert.rules` would read rules from the given path at S3 bucket
|
||||
@@ -563,6 +563,8 @@ The following [command-line flags](#flags) can be used for fine-tuning access to
|
||||
* `-s3.customEndpoint` - custom S3 endpoint for use with S3-compatible storages (e.g. MinIO). S3 is used if not set.
|
||||
* `-s3.forcePathStyle` - prefixing endpoint with bucket name when set false, true by default.
|
||||
|
||||
See [providing credentials as a file](https://docs.victoriametrics.com/victoriametrics/vmbackup/#providing-credentials-as-a-file) for details on how to create and use credentials to access S3-compatible buckets and Google Cloud Storage.
|
||||
|
||||
## Topology examples
|
||||
|
||||
The following sections are showing how `vmalert` may be used and configured
|
||||
|
||||
@@ -204,38 +204,80 @@ See [this article](https://medium.com/@valyala/speeding-up-backups-for-big-time-
|
||||
|
||||
### Providing credentials as a file
|
||||
|
||||
Obtaining credentials from a file.
|
||||
`vmbackup`, `vmbackupmanager`, and [`vmalert`](https://docs.victoriametrics.com/victoriametrics/vmalert/) can load credentials from a file via the `-credsFilePath` flag to access remote S3-compatible buckets and Google Cloud Storage.
|
||||
|
||||
Add flag `-credsFilePath=/etc/credentials` with the following content:
|
||||
To use a credential file, add the flag:
|
||||
|
||||
* for S3 (AWS, MinIO or other S3 compatible storages):
|
||||
```sh
|
||||
-credsFilePath=/etc/credentials
|
||||
```
|
||||
|
||||
```sh
|
||||
[default]
|
||||
aws_access_key_id=theaccesskey
|
||||
aws_secret_access_key=thesecretaccesskeyvalue
|
||||
```
|
||||
The argument should point to a file with one of the formats below, depending on the storage provider.
|
||||
|
||||
* for GCP cloud storage:
|
||||
#### S3 (AWS and S3-compatible)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "service_account",
|
||||
"project_id": "project-id",
|
||||
"private_key_id": "key-id",
|
||||
"private_key": "-----BEGIN PRIVATE KEY-----\nprivate-key\n-----END PRIVATE KEY-----\n",
|
||||
"client_email": "service-account-email",
|
||||
"client_id": "client-id",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://accounts.google.com/o/oauth2/token",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/service-account-email"
|
||||
}
|
||||
```
|
||||
1. In AWS, [create an IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html) or role with permissions to read and write the target bucket.
|
||||
2. [Create an access key](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) for that IAM identity and copy the **Access key** and **Secret access key** values
|
||||
3. On the machine running `vmbackup`, create a credentials file with the following content and point `-credsFilePath` to it:
|
||||
|
||||
```ini
|
||||
[default]
|
||||
aws_access_key_id=YOUR_AWS_ACCESS_KEY
|
||||
aws_secret_access_key=YOUR_AWS_SECRET_ACCESS_KEY
|
||||
```
|
||||
|
||||
This format matches the standard shared AWS credentials file used by the [AWS CLI](https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-files.html) and [AWS SDKs](https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html).
|
||||
|
||||
For S3-compatible backends such as [MinIO](https://www.min.io/) or [Ceph](https://ceph.io/), create access keys in the respective
|
||||
system and use the same file format and set a custom endpoint with `-customS3Endpoint`.
|
||||
|
||||
For example:
|
||||
|
||||
```sh
|
||||
vmbackup \
|
||||
-storageDataPath=/data \
|
||||
-snapshot.createURL=http://localhost:8428/snapshot/create \
|
||||
-dst=s3://victoriametrics-backup/backup01 \
|
||||
-customS3Endpoint=http://minio.example.local:9000 \
|
||||
-credsFilePath=/etc/credentials
|
||||
```
|
||||
|
||||
#### Google Cloud Storage (GCS)
|
||||
|
||||
To create an IAM user and download the credential file, follow these steps:
|
||||
|
||||
1. Open the Google Cloud Console and go to **IAM & Admin → Service Accounts**.
|
||||
2. Click **Create service account**.
|
||||
3. Enter a service account name.
|
||||
4. Assign the role the account needs to access Google Cloud Storage. See [IAM permissions for JSON methods](https://docs.cloud.google.com/storage/docs/access-control/iam-json) for more details.
|
||||
5. Open the service account, go to **Keys**, then click **Add key → Create new key**.
|
||||
6. Choose **JSON** as the key type
|
||||
7. Save the downloaded JSON file on the machine running `vmbackup` and point `-credsFilePath` to it. The file contents look similar to:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "service_account",
|
||||
"project_id": "project-id",
|
||||
"private_key_id": "key-id",
|
||||
"private_key": "-----BEGIN PRIVATE KEY-----\nprivate-key\n-----END PRIVATE KEY-----\n",
|
||||
"client_email": "service-account-email",
|
||||
"client_id": "client-id",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://accounts.google.com/o/oauth2/token",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/service-account-email"
|
||||
}
|
||||
```
|
||||
|
||||
This JSON is the standard service account key format defined by [Google Cloud IAM](https://developers.google.com/workspace/guides/create-credentials) and is used by Google client libraries and tools.
|
||||
|
||||
#### Azure Blob Storage
|
||||
|
||||
Azure Blob Storage uses environment variables rather than `-credsFilePath` in `vmbackup`. See [providing credentials via env variables](https://docs.victoriametrics.com/victoriametrics/vmbackup/#providing-credentials-via-env-variables) for details.
|
||||
|
||||
### Providing credentials via env variables
|
||||
|
||||
Obtaining credentials from env variables.
|
||||
Obtaining credentials from environment variables.
|
||||
|
||||
* For AWS S3 compatible storages set env variable `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
|
||||
Also you can set env variable `AWS_SHARED_CREDENTIALS_FILE` with path to credentials file.
|
||||
|
||||
@@ -9,50 +9,47 @@ import (
|
||||
)
|
||||
|
||||
// WriteMetricRelabelDebug writes /metric-relabel-debug page to w with the corresponding args.
|
||||
func WriteMetricRelabelDebug(w io.Writer, targetID, metric, relabelConfigs, rwRelabelConfigs string, rwURLRelabelConfigLength int, format string, err error) {
|
||||
writeRelabelDebug(w, false, targetID, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigLength, format, err)
|
||||
func WriteMetricRelabelDebug(w io.Writer, targetID, metric, relabelConfigs, format string, err error) {
|
||||
writeRelabelDebug(w, false, targetID, metric, relabelConfigs, 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, "", 0, format, err)
|
||||
writeRelabelDebug(w, true, targetID, metric, relabelConfigs, format, err)
|
||||
}
|
||||
|
||||
func writeRelabelDebug(w io.Writer, isTargetRelabel bool, targetID, metric, relabelConfigs, rwRelabelConfigs string, rwURLRelabelConfigLength int, format string, err error) {
|
||||
func writeRelabelDebug(w io.Writer, isTargetRelabel bool, targetID, metric, relabelConfigs, format string, err error) {
|
||||
if metric == "" {
|
||||
metric = "{}"
|
||||
}
|
||||
targetURL := ""
|
||||
if err != nil {
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, 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, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, 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, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
|
||||
return
|
||||
}
|
||||
|
||||
// merge relabel configs
|
||||
fullRelabelConfigs := relabelConfigs + "\n" + rwRelabelConfigs
|
||||
pcs, err := ParseRelabelConfigsData([]byte(fullRelabelConfigs))
|
||||
pcs, err := ParseRelabelConfigsData([]byte(relabelConfigs))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("cannot parse relabel configs: %w", err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, nil, metric, relabelConfigs, err)
|
||||
return
|
||||
}
|
||||
|
||||
dss, targetURL := newDebugRelabelSteps(pcs, labels, isTargetRelabel)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, dss, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, nil)
|
||||
WriteRelabelDebugSteps(w, targetURL, targetID, format, dss, metric, relabelConfigs, nil)
|
||||
}
|
||||
|
||||
func newDebugRelabelSteps(pcs *ParsedConfigs, labels *promutil.Labels, isTargetRelabel bool) ([]DebugStep, string) {
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
|
||||
{% stripspace %}
|
||||
|
||||
{% func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool, err error) %}
|
||||
{% func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, err error) %}
|
||||
{% if format == "json" %}
|
||||
{%= RelabelDebugStepsJSON(targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err) %}
|
||||
{%= RelabelDebugStepsJSON(targetURL, targetID, dss, metric, relabelConfigs, err) %}
|
||||
{% else %}
|
||||
{%= RelabelDebugStepsHTML(targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err) %}
|
||||
{%= RelabelDebugStepsHTML(targetURL, targetID, dss, metric, relabelConfigs, err) %}
|
||||
{% endif %}
|
||||
{% endfunc %}
|
||||
|
||||
{% func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool, err error) %}
|
||||
{% func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, 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, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel) %}
|
||||
{%= relabelDebugFormInputs(metric, relabelConfigs) %}
|
||||
{% if targetID != "" %}
|
||||
<input type="hidden" name="id" value="{%s targetID %}" />
|
||||
{% endif %}
|
||||
@@ -70,34 +70,12 @@ function submitRelabelDebugForm(e) {
|
||||
</html>
|
||||
{% endfunc %}
|
||||
|
||||
{% func relabelDebugFormInputs(metric, relabelConfigs string, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool) %}
|
||||
{% func relabelDebugFormInputs(metric, relabelConfigs string) %}
|
||||
<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">
|
||||
<select name="url_relabel_configs_index" class="form-select form-select-sm w-auto">
|
||||
<option value="">-- select remote write url --</option>
|
||||
{% if rwURLRelabelConfigLength > 0 %}
|
||||
{% for i := range rwURLRelabelConfigLength %}
|
||||
<option value="{%d i %}">remote-write-url-{%d i %}</option>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</select>
|
||||
<input type="submit" name="reload_url_relabel_configs" value="Reload" class="btn btn-secondary btn-sm" />
|
||||
</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>
|
||||
@@ -173,7 +151,7 @@ function submitRelabelDebugForm(e) {
|
||||
{% endif %}
|
||||
{% endfunc %}
|
||||
|
||||
{% func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool, err error) %}
|
||||
{% func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) %}
|
||||
{
|
||||
{% if err != nil %}
|
||||
"status": "error",
|
||||
|
||||
@@ -25,37 +25,37 @@ var (
|
||||
)
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:9
|
||||
func StreamRelabelDebugSteps(qw422016 *qt422016.Writer, targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool, err error) {
|
||||
func StreamRelabelDebugSteps(qw422016 *qt422016.Writer, targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:10
|
||||
if format == "json" {
|
||||
//line lib/promrelabel/debug.qtpl:11
|
||||
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err)
|
||||
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, err)
|
||||
//line lib/promrelabel/debug.qtpl:12
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:13
|
||||
StreamRelabelDebugStepsHTML(qw422016, targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err)
|
||||
StreamRelabelDebugStepsHTML(qw422016, targetURL, targetID, dss, metric, relabelConfigs, 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, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool, err error) {
|
||||
func WriteRelabelDebugSteps(qq422016 qtio422016.Writer, targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, 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, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err)
|
||||
StreamRelabelDebugSteps(qw422016, targetURL, targetID, format, dss, metric, relabelConfigs, 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, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool, err error) string {
|
||||
func RelabelDebugSteps(targetURL, targetID, format string, dss []DebugStep, metric, relabelConfigs string, 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, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err)
|
||||
WriteRelabelDebugSteps(qb422016, targetURL, targetID, format, dss, metric, relabelConfigs, 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, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool, err error) {
|
||||
func StreamRelabelDebugStepsHTML(qw422016 *qt422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, 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, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel)
|
||||
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs)
|
||||
//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, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool, err error) {
|
||||
func WriteRelabelDebugStepsHTML(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, 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, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err)
|
||||
StreamRelabelDebugStepsHTML(qw422016, targetURL, targetID, dss, metric, relabelConfigs, 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, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool, err error) string {
|
||||
func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, 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, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err)
|
||||
WriteRelabelDebugStepsHTML(qb422016, targetURL, targetID, dss, metric, relabelConfigs, err)
|
||||
//line lib/promrelabel/debug.qtpl:71
|
||||
qs422016 := string(qb422016.B)
|
||||
//line lib/promrelabel/debug.qtpl:71
|
||||
@@ -179,358 +179,326 @@ func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric,
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:73
|
||||
func streamrelabelDebugFormInputs(qw422016 *qt422016.Writer, metric, relabelConfigs string, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool) {
|
||||
func streamrelabelDebugFormInputs(qw422016 *qt422016.Writer, metric, relabelConfigs string) {
|
||||
//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>`)
|
||||
//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"><select name="url_relabel_configs_index" class="form-select form-select-sm w-auto"><option value="">-- select remote write url --</option>`)
|
||||
//line lib/promrelabel/debug.qtpl:87
|
||||
if rwURLRelabelConfigLength > 0 {
|
||||
//line lib/promrelabel/debug.qtpl:88
|
||||
for i := range rwURLRelabelConfigLength {
|
||||
//line lib/promrelabel/debug.qtpl:88
|
||||
qw422016.N().S(`<option value="`)
|
||||
//line lib/promrelabel/debug.qtpl:89
|
||||
qw422016.N().D(i)
|
||||
//line lib/promrelabel/debug.qtpl:89
|
||||
qw422016.N().S(`">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
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:91
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:91
|
||||
qw422016.N().S(`</select><input type="submit" name="reload_url_relabel_configs" value="Reload" class="btn btn-secondary btn-sm" /></div></div><textarea name="remote_write_relabel_configs" style="width: 100%; height: 15em; font-family: monospace" class="m-1">`)
|
||||
//line lib/promrelabel/debug.qtpl:96
|
||||
qw422016.E().S(rwRelabelConfigs)
|
||||
//line lib/promrelabel/debug.qtpl:96
|
||||
qw422016.N().S(`</textarea></div>`)
|
||||
//line lib/promrelabel/debug.qtpl:98
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:98
|
||||
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:103
|
||||
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:103
|
||||
//line lib/promrelabel/debug.qtpl:81
|
||||
qw422016.N().S(`</textarea></div>`)
|
||||
//line lib/promrelabel/debug.qtpl:105
|
||||
//line lib/promrelabel/debug.qtpl:83
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:105
|
||||
func writerelabelDebugFormInputs(qq422016 qtio422016.Writer, metric, relabelConfigs string, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool) {
|
||||
//line lib/promrelabel/debug.qtpl:105
|
||||
//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:105
|
||||
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel)
|
||||
//line lib/promrelabel/debug.qtpl:105
|
||||
//line lib/promrelabel/debug.qtpl:83
|
||||
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs)
|
||||
//line lib/promrelabel/debug.qtpl:83
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:105
|
||||
//line lib/promrelabel/debug.qtpl:83
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:105
|
||||
func relabelDebugFormInputs(metric, relabelConfigs string, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool) string {
|
||||
//line lib/promrelabel/debug.qtpl:105
|
||||
//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:105
|
||||
writerelabelDebugFormInputs(qb422016, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel)
|
||||
//line lib/promrelabel/debug.qtpl:105
|
||||
//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:105
|
||||
//line lib/promrelabel/debug.qtpl:83
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line lib/promrelabel/debug.qtpl:105
|
||||
//line lib/promrelabel/debug.qtpl:83
|
||||
return qs422016
|
||||
//line lib/promrelabel/debug.qtpl:105
|
||||
//line lib/promrelabel/debug.qtpl:83
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:107
|
||||
//line lib/promrelabel/debug.qtpl:85
|
||||
func streamrelabelDebugSteps(qw422016 *qt422016.Writer, dss []DebugStep, targetURL, targetID string) {
|
||||
//line lib/promrelabel/debug.qtpl:108
|
||||
//line lib/promrelabel/debug.qtpl:86
|
||||
if len(dss) > 0 {
|
||||
//line lib/promrelabel/debug.qtpl:108
|
||||
//line lib/promrelabel/debug.qtpl:86
|
||||
qw422016.N().S(`<div class="m-3"><b>Original labels:</b> <samp>`)
|
||||
//line lib/promrelabel/debug.qtpl:110
|
||||
//line lib/promrelabel/debug.qtpl:88
|
||||
streammustFormatLabels(qw422016, dss[0].In)
|
||||
//line lib/promrelabel/debug.qtpl:110
|
||||
//line lib/promrelabel/debug.qtpl:88
|
||||
qw422016.N().S(`</samp></div>`)
|
||||
//line lib/promrelabel/debug.qtpl:112
|
||||
//line lib/promrelabel/debug.qtpl:90
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:112
|
||||
//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>`)
|
||||
//line lib/promrelabel/debug.qtpl:123
|
||||
//line lib/promrelabel/debug.qtpl:101
|
||||
for i, ds := range dss {
|
||||
//line lib/promrelabel/debug.qtpl:125
|
||||
//line lib/promrelabel/debug.qtpl:103
|
||||
inLabels, inErr := promutil.NewLabelsFromString(ds.In)
|
||||
outLabels, outErr := promutil.NewLabelsFromString(ds.Out)
|
||||
changedLabels := getChangedLabelNames(inLabels, outLabels)
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:128
|
||||
//line lib/promrelabel/debug.qtpl:106
|
||||
qw422016.N().S(`<tr><td>`)
|
||||
//line lib/promrelabel/debug.qtpl:130
|
||||
//line lib/promrelabel/debug.qtpl:108
|
||||
qw422016.N().D(i)
|
||||
//line lib/promrelabel/debug.qtpl:130
|
||||
//line lib/promrelabel/debug.qtpl:108
|
||||
qw422016.N().S(`</td><td><b><pre class="m-2">`)
|
||||
//line lib/promrelabel/debug.qtpl:131
|
||||
//line lib/promrelabel/debug.qtpl:109
|
||||
qw422016.E().S(ds.Rule)
|
||||
//line lib/promrelabel/debug.qtpl:131
|
||||
//line lib/promrelabel/debug.qtpl:109
|
||||
qw422016.N().S(`</pre></b></td><td>`)
|
||||
//line lib/promrelabel/debug.qtpl:133
|
||||
//line lib/promrelabel/debug.qtpl:111
|
||||
if inErr == nil {
|
||||
//line lib/promrelabel/debug.qtpl:133
|
||||
//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:135
|
||||
//line lib/promrelabel/debug.qtpl:113
|
||||
streamlabelsWithHighlight(qw422016, inLabels, changedLabels, "#D15757")
|
||||
//line lib/promrelabel/debug.qtpl:135
|
||||
//line lib/promrelabel/debug.qtpl:113
|
||||
qw422016.N().S(`</div>`)
|
||||
//line lib/promrelabel/debug.qtpl:137
|
||||
//line lib/promrelabel/debug.qtpl:115
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:137
|
||||
//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:139
|
||||
//line lib/promrelabel/debug.qtpl:117
|
||||
qw422016.E().S(inErr.Error())
|
||||
//line lib/promrelabel/debug.qtpl:139
|
||||
//line lib/promrelabel/debug.qtpl:117
|
||||
qw422016.N().S(`</pre></div>`)
|
||||
//line lib/promrelabel/debug.qtpl:141
|
||||
//line lib/promrelabel/debug.qtpl:119
|
||||
break
|
||||
//line lib/promrelabel/debug.qtpl:142
|
||||
//line lib/promrelabel/debug.qtpl:120
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:142
|
||||
//line lib/promrelabel/debug.qtpl:120
|
||||
qw422016.N().S(`</td><td>`)
|
||||
//line lib/promrelabel/debug.qtpl:145
|
||||
//line lib/promrelabel/debug.qtpl:123
|
||||
if outErr == nil {
|
||||
//line lib/promrelabel/debug.qtpl:145
|
||||
//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:147
|
||||
//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>`)
|
||||
//line lib/promrelabel/debug.qtpl:138
|
||||
if len(dss) > 0 {
|
||||
//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
|
||||
} else {
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:149
|
||||
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:151
|
||||
qw422016.E().S(outErr.Error())
|
||||
//line lib/promrelabel/debug.qtpl:151
|
||||
qw422016.N().S(`</pre></div>`)
|
||||
//line lib/promrelabel/debug.qtpl:153
|
||||
break
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
qw422016.N().S(`</td></tr>`)
|
||||
//line lib/promrelabel/debug.qtpl:157
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:157
|
||||
qw422016.N().S(`</tbody></table>`)
|
||||
//line lib/promrelabel/debug.qtpl:160
|
||||
if len(dss) > 0 {
|
||||
//line lib/promrelabel/debug.qtpl:160
|
||||
qw422016.N().S(`<div class="m-3"><b>Resulting labels:</b> <samp>`)
|
||||
//line lib/promrelabel/debug.qtpl:162
|
||||
streammustFormatLabels(qw422016, dss[len(dss)-1].Out)
|
||||
//line lib/promrelabel/debug.qtpl:162
|
||||
qw422016.N().S(`</samp>`)
|
||||
//line lib/promrelabel/debug.qtpl:163
|
||||
if targetURL != "" {
|
||||
//line lib/promrelabel/debug.qtpl:163
|
||||
qw422016.N().S(`<div><b>Target URL:</b>`)
|
||||
//line lib/promrelabel/debug.qtpl:165
|
||||
qw422016.N().S(` `)
|
||||
//line lib/promrelabel/debug.qtpl:165
|
||||
qw422016.N().S(`<a href="`)
|
||||
//line lib/promrelabel/debug.qtpl:165
|
||||
qw422016.E().S(targetURL)
|
||||
//line lib/promrelabel/debug.qtpl:165
|
||||
qw422016.N().S(`" target="_blank">`)
|
||||
//line lib/promrelabel/debug.qtpl:165
|
||||
qw422016.E().S(targetURL)
|
||||
//line lib/promrelabel/debug.qtpl:165
|
||||
qw422016.N().S(`</a>`)
|
||||
//line lib/promrelabel/debug.qtpl:166
|
||||
if targetID != "" {
|
||||
//line lib/promrelabel/debug.qtpl:167
|
||||
qw422016.N().S(` `)
|
||||
//line lib/promrelabel/debug.qtpl:167
|
||||
qw422016.N().S(`(<a href="target_response?id=`)
|
||||
//line lib/promrelabel/debug.qtpl:168
|
||||
qw422016.E().S(targetID)
|
||||
//line lib/promrelabel/debug.qtpl:168
|
||||
qw422016.N().S(`" target="_blank" title="click to fetch target response on behalf of the scraper">response</a>)`)
|
||||
//line lib/promrelabel/debug.qtpl:169
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:169
|
||||
qw422016.N().S(`</div>`)
|
||||
//line lib/promrelabel/debug.qtpl:171
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:171
|
||||
qw422016.N().S(`</div>`)
|
||||
//line lib/promrelabel/debug.qtpl:173
|
||||
//line lib/promrelabel/debug.qtpl:151
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
func writerelabelDebugSteps(qq422016 qtio422016.Writer, dss []DebugStep, targetURL, targetID string) {
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
streamrelabelDebugSteps(qw422016, dss, targetURL, targetID)
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
func relabelDebugSteps(dss []DebugStep, targetURL, targetID string) string {
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
writerelabelDebugSteps(qb422016, dss, targetURL, targetID)
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
qs422016 := string(qb422016.B)
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
return qs422016
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
//line lib/promrelabel/debug.qtpl:152
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:176
|
||||
func StreamRelabelDebugStepsJSON(qw422016 *qt422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:176
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
func StreamRelabelDebugStepsJSON(qw422016 *qt422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:154
|
||||
qw422016.N().S(`{`)
|
||||
//line lib/promrelabel/debug.qtpl:178
|
||||
//line lib/promrelabel/debug.qtpl:156
|
||||
if err != nil {
|
||||
//line lib/promrelabel/debug.qtpl:178
|
||||
//line lib/promrelabel/debug.qtpl:156
|
||||
qw422016.N().S(`"status": "error","error":`)
|
||||
//line lib/promrelabel/debug.qtpl:180
|
||||
//line lib/promrelabel/debug.qtpl:158
|
||||
qw422016.N().Q(fmt.Sprintf("Error: %s", err))
|
||||
//line lib/promrelabel/debug.qtpl:181
|
||||
//line lib/promrelabel/debug.qtpl:159
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:182
|
||||
//line lib/promrelabel/debug.qtpl:160
|
||||
var hasError bool
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:182
|
||||
//line lib/promrelabel/debug.qtpl:160
|
||||
qw422016.N().S(`"status": "success","steps": [`)
|
||||
//line lib/promrelabel/debug.qtpl:185
|
||||
//line lib/promrelabel/debug.qtpl:163
|
||||
for i, ds := range dss {
|
||||
//line lib/promrelabel/debug.qtpl:187
|
||||
//line lib/promrelabel/debug.qtpl:165
|
||||
inLabels, inErr := promutil.NewLabelsFromString(ds.In)
|
||||
outLabels, outErr := promutil.NewLabelsFromString(ds.Out)
|
||||
changedLabels := getChangedLabelNames(inLabels, outLabels)
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:190
|
||||
//line lib/promrelabel/debug.qtpl:168
|
||||
qw422016.N().S(`{"inLabels":`)
|
||||
//line lib/promrelabel/debug.qtpl:192
|
||||
//line lib/promrelabel/debug.qtpl:170
|
||||
qw422016.N().Q(labelsWithHighlight(inLabels, changedLabels, "#D15757"))
|
||||
//line lib/promrelabel/debug.qtpl:192
|
||||
//line lib/promrelabel/debug.qtpl:170
|
||||
qw422016.N().S(`,"outLabels":`)
|
||||
//line lib/promrelabel/debug.qtpl:193
|
||||
//line lib/promrelabel/debug.qtpl:171
|
||||
qw422016.N().Q(labelsWithHighlight(outLabels, changedLabels, "#4495e0"))
|
||||
//line lib/promrelabel/debug.qtpl:193
|
||||
//line lib/promrelabel/debug.qtpl:171
|
||||
qw422016.N().S(`,"rule":`)
|
||||
//line lib/promrelabel/debug.qtpl:194
|
||||
//line lib/promrelabel/debug.qtpl:172
|
||||
qw422016.N().Q(ds.Rule)
|
||||
//line lib/promrelabel/debug.qtpl:194
|
||||
//line lib/promrelabel/debug.qtpl:172
|
||||
qw422016.N().S(`,"errors": {`)
|
||||
//line lib/promrelabel/debug.qtpl:196
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
if inErr != nil {
|
||||
//line lib/promrelabel/debug.qtpl:196
|
||||
//line lib/promrelabel/debug.qtpl:174
|
||||
qw422016.N().S(`"inLabels":`)
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
//line lib/promrelabel/debug.qtpl:175
|
||||
qw422016.N().Q(`<span style="color: #D15757">` + inErr.Error() + `</span>`)
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
//line lib/promrelabel/debug.qtpl:175
|
||||
if outErr != nil {
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
//line lib/promrelabel/debug.qtpl:175
|
||||
qw422016.N().S(`,`)
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
//line lib/promrelabel/debug.qtpl:175
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:198
|
||||
//line lib/promrelabel/debug.qtpl:176
|
||||
hasError = true
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:199
|
||||
//line lib/promrelabel/debug.qtpl:177
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:200
|
||||
//line lib/promrelabel/debug.qtpl:178
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:201
|
||||
//line lib/promrelabel/debug.qtpl:179
|
||||
if outErr != nil {
|
||||
//line lib/promrelabel/debug.qtpl:201
|
||||
//line lib/promrelabel/debug.qtpl:179
|
||||
qw422016.N().S(`"outLabels":`)
|
||||
//line lib/promrelabel/debug.qtpl:202
|
||||
//line lib/promrelabel/debug.qtpl:180
|
||||
qw422016.N().Q(`<span style="color: #D15757">` + outErr.Error() + `</span>`)
|
||||
//line lib/promrelabel/debug.qtpl:203
|
||||
//line lib/promrelabel/debug.qtpl:181
|
||||
hasError = true
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:204
|
||||
//line lib/promrelabel/debug.qtpl:182
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:204
|
||||
//line lib/promrelabel/debug.qtpl:182
|
||||
qw422016.N().S(`}}`)
|
||||
//line lib/promrelabel/debug.qtpl:207
|
||||
//line lib/promrelabel/debug.qtpl:185
|
||||
if i != len(dss)-1 {
|
||||
//line lib/promrelabel/debug.qtpl:207
|
||||
//line lib/promrelabel/debug.qtpl:185
|
||||
qw422016.N().S(`,`)
|
||||
//line lib/promrelabel/debug.qtpl:207
|
||||
//line lib/promrelabel/debug.qtpl:185
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:208
|
||||
//line lib/promrelabel/debug.qtpl:186
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:208
|
||||
//line lib/promrelabel/debug.qtpl:186
|
||||
qw422016.N().S(`]`)
|
||||
//line lib/promrelabel/debug.qtpl:210
|
||||
//line lib/promrelabel/debug.qtpl:188
|
||||
if len(dss) > 0 && !hasError {
|
||||
//line lib/promrelabel/debug.qtpl:210
|
||||
//line lib/promrelabel/debug.qtpl:188
|
||||
qw422016.N().S(`,"originalLabels":`)
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:190
|
||||
qw422016.N().Q(mustFormatLabels(dss[0].In))
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
//line lib/promrelabel/debug.qtpl:190
|
||||
qw422016.N().S(`,"resultingLabels":`)
|
||||
//line lib/promrelabel/debug.qtpl:213
|
||||
//line lib/promrelabel/debug.qtpl:191
|
||||
qw422016.N().Q(mustFormatLabels(dss[len(dss)-1].Out))
|
||||
//line lib/promrelabel/debug.qtpl:214
|
||||
//line lib/promrelabel/debug.qtpl:192
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:215
|
||||
//line lib/promrelabel/debug.qtpl:193
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:215
|
||||
//line lib/promrelabel/debug.qtpl:193
|
||||
qw422016.N().S(`}`)
|
||||
//line lib/promrelabel/debug.qtpl:217
|
||||
//line lib/promrelabel/debug.qtpl:195
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:217
|
||||
func WriteRelabelDebugStepsJSON(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool, err error) {
|
||||
//line lib/promrelabel/debug.qtpl:217
|
||||
//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:217
|
||||
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err)
|
||||
//line lib/promrelabel/debug.qtpl:217
|
||||
//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:217
|
||||
//line lib/promrelabel/debug.qtpl:195
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:217
|
||||
func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs, rwRelabelConfigs string, rwURLRelabelConfigLength int, isTargetRelabel bool, err error) string {
|
||||
//line lib/promrelabel/debug.qtpl:217
|
||||
//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:217
|
||||
WriteRelabelDebugStepsJSON(qb422016, targetURL, targetID, dss, metric, relabelConfigs, rwRelabelConfigs, rwURLRelabelConfigLength, isTargetRelabel, err)
|
||||
//line lib/promrelabel/debug.qtpl:217
|
||||
//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:217
|
||||
//line lib/promrelabel/debug.qtpl:195
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line lib/promrelabel/debug.qtpl:217
|
||||
//line lib/promrelabel/debug.qtpl:195
|
||||
return qs422016
|
||||
//line lib/promrelabel/debug.qtpl:217
|
||||
//line lib/promrelabel/debug.qtpl:195
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:219
|
||||
//line lib/promrelabel/debug.qtpl:197
|
||||
func streamlabelsWithHighlight(qw422016 *qt422016.Writer, labels *promutil.Labels, highlight map[string]struct{}, color string) {
|
||||
//line lib/promrelabel/debug.qtpl:221
|
||||
//line lib/promrelabel/debug.qtpl:199
|
||||
labelsList := labels.GetLabels()
|
||||
metricName := ""
|
||||
for i, label := range labelsList {
|
||||
@@ -541,153 +509,153 @@ func streamlabelsWithHighlight(qw422016 *qt422016.Writer, labels *promutil.Label
|
||||
}
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:231
|
||||
//line lib/promrelabel/debug.qtpl:209
|
||||
if metricName != "" {
|
||||
//line lib/promrelabel/debug.qtpl:232
|
||||
//line lib/promrelabel/debug.qtpl:210
|
||||
if _, ok := highlight["__name__"]; ok {
|
||||
//line lib/promrelabel/debug.qtpl:232
|
||||
//line lib/promrelabel/debug.qtpl:210
|
||||
qw422016.N().S(`<span style="font-weight:bold;color:`)
|
||||
//line lib/promrelabel/debug.qtpl:233
|
||||
//line lib/promrelabel/debug.qtpl:211
|
||||
qw422016.E().S(color)
|
||||
//line lib/promrelabel/debug.qtpl:233
|
||||
//line lib/promrelabel/debug.qtpl:211
|
||||
qw422016.N().S(`">`)
|
||||
//line lib/promrelabel/debug.qtpl:233
|
||||
//line lib/promrelabel/debug.qtpl:211
|
||||
qw422016.E().S(metricName)
|
||||
//line lib/promrelabel/debug.qtpl:233
|
||||
//line lib/promrelabel/debug.qtpl:211
|
||||
qw422016.N().S(`</span>`)
|
||||
//line lib/promrelabel/debug.qtpl:234
|
||||
//line lib/promrelabel/debug.qtpl:212
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:235
|
||||
//line lib/promrelabel/debug.qtpl:213
|
||||
qw422016.E().S(metricName)
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
//line lib/promrelabel/debug.qtpl:214
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:237
|
||||
//line lib/promrelabel/debug.qtpl:215
|
||||
if len(labelsList) == 0 {
|
||||
//line lib/promrelabel/debug.qtpl:237
|
||||
//line lib/promrelabel/debug.qtpl:215
|
||||
return
|
||||
//line lib/promrelabel/debug.qtpl:237
|
||||
//line lib/promrelabel/debug.qtpl:215
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
//line lib/promrelabel/debug.qtpl:216
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:238
|
||||
//line lib/promrelabel/debug.qtpl:216
|
||||
qw422016.N().S(`{`)
|
||||
//line lib/promrelabel/debug.qtpl:240
|
||||
//line lib/promrelabel/debug.qtpl:218
|
||||
for i, label := range labelsList {
|
||||
//line lib/promrelabel/debug.qtpl:241
|
||||
//line lib/promrelabel/debug.qtpl:219
|
||||
if _, ok := highlight[label.Name]; ok {
|
||||
//line lib/promrelabel/debug.qtpl:241
|
||||
//line lib/promrelabel/debug.qtpl:219
|
||||
qw422016.N().S(`<span style="font-weight:bold;color:`)
|
||||
//line lib/promrelabel/debug.qtpl:242
|
||||
//line lib/promrelabel/debug.qtpl:220
|
||||
qw422016.E().S(color)
|
||||
//line lib/promrelabel/debug.qtpl:242
|
||||
//line lib/promrelabel/debug.qtpl:220
|
||||
qw422016.N().S(`">`)
|
||||
//line lib/promrelabel/debug.qtpl:242
|
||||
//line lib/promrelabel/debug.qtpl:220
|
||||
qw422016.E().S(label.Name)
|
||||
//line lib/promrelabel/debug.qtpl:242
|
||||
//line lib/promrelabel/debug.qtpl:220
|
||||
qw422016.N().S(`=`)
|
||||
//line lib/promrelabel/debug.qtpl:242
|
||||
//line lib/promrelabel/debug.qtpl:220
|
||||
qw422016.E().Q(label.Value)
|
||||
//line lib/promrelabel/debug.qtpl:242
|
||||
//line lib/promrelabel/debug.qtpl:220
|
||||
qw422016.N().S(`</span>`)
|
||||
//line lib/promrelabel/debug.qtpl:243
|
||||
//line lib/promrelabel/debug.qtpl:221
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:244
|
||||
//line lib/promrelabel/debug.qtpl:222
|
||||
qw422016.E().S(label.Name)
|
||||
//line lib/promrelabel/debug.qtpl:244
|
||||
//line lib/promrelabel/debug.qtpl:222
|
||||
qw422016.N().S(`=`)
|
||||
//line lib/promrelabel/debug.qtpl:244
|
||||
//line lib/promrelabel/debug.qtpl:222
|
||||
qw422016.E().Q(label.Value)
|
||||
//line lib/promrelabel/debug.qtpl:245
|
||||
//line lib/promrelabel/debug.qtpl:223
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:246
|
||||
//line lib/promrelabel/debug.qtpl:224
|
||||
if i < len(labelsList)-1 {
|
||||
//line lib/promrelabel/debug.qtpl:246
|
||||
//line lib/promrelabel/debug.qtpl:224
|
||||
qw422016.N().S(`,`)
|
||||
//line lib/promrelabel/debug.qtpl:246
|
||||
//line lib/promrelabel/debug.qtpl:224
|
||||
qw422016.N().S(` `)
|
||||
//line lib/promrelabel/debug.qtpl:246
|
||||
//line lib/promrelabel/debug.qtpl:224
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:247
|
||||
//line lib/promrelabel/debug.qtpl:225
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:247
|
||||
//line lib/promrelabel/debug.qtpl:225
|
||||
qw422016.N().S(`}`)
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
func writelabelsWithHighlight(qq422016 qtio422016.Writer, labels *promutil.Labels, highlight map[string]struct{}, color string) {
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
streamlabelsWithHighlight(qw422016, labels, highlight, color)
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
func labelsWithHighlight(labels *promutil.Labels, highlight map[string]struct{}, color string) string {
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
writelabelsWithHighlight(qb422016, labels, highlight, color)
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
qs422016 := string(qb422016.B)
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
return qs422016
|
||||
//line lib/promrelabel/debug.qtpl:249
|
||||
//line lib/promrelabel/debug.qtpl:227
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:251
|
||||
//line lib/promrelabel/debug.qtpl:229
|
||||
func streammustFormatLabels(qw422016 *qt422016.Writer, s string) {
|
||||
//line lib/promrelabel/debug.qtpl:252
|
||||
//line lib/promrelabel/debug.qtpl:230
|
||||
labels, err := promutil.NewLabelsFromString(s)
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:253
|
||||
//line lib/promrelabel/debug.qtpl:231
|
||||
if err != nil {
|
||||
//line lib/promrelabel/debug.qtpl:253
|
||||
//line lib/promrelabel/debug.qtpl:231
|
||||
qw422016.N().S(`<span style="color: red" title="error parsing labels:`)
|
||||
//line lib/promrelabel/debug.qtpl:254
|
||||
//line lib/promrelabel/debug.qtpl:232
|
||||
qw422016.E().S(err.Error())
|
||||
//line lib/promrelabel/debug.qtpl:254
|
||||
//line lib/promrelabel/debug.qtpl:232
|
||||
qw422016.N().S(`">`)
|
||||
//line lib/promrelabel/debug.qtpl:254
|
||||
//line lib/promrelabel/debug.qtpl:232
|
||||
qw422016.E().S("error parsing labels: " + err.Error())
|
||||
//line lib/promrelabel/debug.qtpl:254
|
||||
//line lib/promrelabel/debug.qtpl:232
|
||||
qw422016.N().S(`</span>`)
|
||||
//line lib/promrelabel/debug.qtpl:255
|
||||
//line lib/promrelabel/debug.qtpl:233
|
||||
} else {
|
||||
//line lib/promrelabel/debug.qtpl:256
|
||||
//line lib/promrelabel/debug.qtpl:234
|
||||
streamlabelsWithHighlight(qw422016, labels, nil, "")
|
||||
//line lib/promrelabel/debug.qtpl:257
|
||||
//line lib/promrelabel/debug.qtpl:235
|
||||
}
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
func writemustFormatLabels(qq422016 qtio422016.Writer, s string) {
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
streammustFormatLabels(qw422016, s)
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
}
|
||||
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
func mustFormatLabels(s string) string {
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
writemustFormatLabels(qb422016, s)
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
qs422016 := string(qb422016.B)
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
return qs422016
|
||||
//line lib/promrelabel/debug.qtpl:258
|
||||
//line lib/promrelabel/debug.qtpl:236
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ func TestWriteRelabelDebugSupportFormats(t *testing.T) {
|
||||
f := func(input, rule, expect string) {
|
||||
// execute
|
||||
outputWriter := bytes.NewBuffer(nil)
|
||||
writeRelabelDebug(outputWriter, false, "", input, rule, "", 0, "json", nil)
|
||||
writeRelabelDebug(outputWriter, false, "", input, rule, "json", nil)
|
||||
|
||||
// the response is in JSON with HTML content, extract the `resultingLabels` in JSON and unescape it.
|
||||
resultingLabels := fastjson.GetString(outputWriter.Bytes(), `resultingLabels`)
|
||||
|
||||
@@ -3,84 +3,34 @@ 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.
|
||||
// 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) {
|
||||
// WriteMetricRelabelDebug serves requests to /metric-relabel-debug page
|
||||
func WriteMetricRelabelDebug(w http.ResponseWriter, r *http.Request) {
|
||||
targetID := r.FormValue("id")
|
||||
metric := r.FormValue("metric")
|
||||
relabelConfigs := r.FormValue("relabel_configs")
|
||||
rwRelabelConfigs := r.FormValue("remote_write_relabel_configs") // global + per-URL configs.
|
||||
|
||||
rwURLRelabelConfigsIdx := 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
|
||||
|
||||
rwURLRelabelConfigsLength := len(rwURLRelabelConfigss)
|
||||
|
||||
// if everything is not set, we should load the initial data for user.
|
||||
if metric == "" && relabelConfigs == "" && rwRelabelConfigs == "" && rwURLRelabelConfigsIdx == "" && reloadRWURLRelabelConfigs == "" && targetID != "" {
|
||||
if metric == "" && relabelConfigs == "" && 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()
|
||||
|
||||
// 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
|
||||
}
|
||||
relabelConfigs = pcs.String()
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
idx, _ := strconv.Atoi(rwURLRelabelConfigsIdx)
|
||||
if idx < 0 || idx >= len(rwURLRelabelConfigss) {
|
||||
idx = 0
|
||||
}
|
||||
rwURLRelabelConfigs = rwURLRelabelConfigss[idx]
|
||||
}
|
||||
|
||||
// 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, rwRelabelConfigs, rwURLRelabelConfigsLength, format, err)
|
||||
promrelabel.WriteMetricRelabelDebug(w, targetID, metric, relabelConfigs, format, err)
|
||||
}
|
||||
|
||||
// WriteTargetRelabelDebug generates response for /target-relabel-debug page
|
||||
|
||||
@@ -14,16 +14,10 @@ func (av *histogramBucketAggrValue) pushSample(_ aggrConfig, sample *pushSample,
|
||||
av.h.Update(sample.value)
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
ctx.appendSeriesWithExtraLabel(key, "histogram_bucket", float64(count), "vmrange", vmrange)
|
||||
})
|
||||
}
|
||||
@@ -32,26 +26,17 @@ func (av *histogramBucketAggrValue) state() any {
|
||||
return av.shared
|
||||
}
|
||||
|
||||
func newHistogramBucketAggrConfig(useSharedState bool) aggrConfig {
|
||||
return &histogramBucketAggrConfig{
|
||||
useSharedState: useSharedState,
|
||||
}
|
||||
func newHistogramBucketAggrConfig() aggrConfig {
|
||||
return &histogramBucketAggrConfig{}
|
||||
}
|
||||
|
||||
type histogramBucketAggrConfig struct {
|
||||
useSharedState bool
|
||||
}
|
||||
type histogramBucketAggrConfig struct{}
|
||||
|
||||
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)
|
||||
}
|
||||
func (*histogramBucketAggrConfig) getValue(s any) aggrValue {
|
||||
if s == nil {
|
||||
s = &metrics.Histogram{}
|
||||
}
|
||||
return &histogramBucketAggrValue{
|
||||
shared: shared,
|
||||
shared: s.(*metrics.Histogram),
|
||||
}
|
||||
}
|
||||
|
||||
109
lib/streamaggr/increase.go
Normal file
109
lib/streamaggr/increase.go
Normal file
@@ -0,0 +1,109 @@
|
||||
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"
|
||||
}
|
||||
@@ -75,6 +75,9 @@ 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
|
||||
@@ -112,6 +115,9 @@ 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()
|
||||
|
||||
@@ -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, useSharedState, ignoreFirstSampleInterval)
|
||||
ac, err := newOutputConfig(ms, outputMetricLabels, output, outputsSeen, 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{}, useSharedState bool, ignoreFirstSampleInterval time.Duration) (aggrConfig, error) {
|
||||
func newOutputConfig(ms *metrics.Set, metricLabels, output string, outputsSeen map[string]struct{}, 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(useSharedState), nil
|
||||
return newHistogramBucketAggrConfig(), nil
|
||||
case "increase":
|
||||
return newTotalAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, true, true), nil
|
||||
return newIncreaseAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, true), nil
|
||||
case "increase_prometheus":
|
||||
return newTotalAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, true, false), nil
|
||||
return newIncreaseAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, 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, false, true), nil
|
||||
return newTotalAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, true), nil
|
||||
case "total_prometheus":
|
||||
return newTotalAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, false, false), nil
|
||||
return newTotalAggrConfig(ms, metricLabels, ignoreFirstSampleIntervalSecs, false), nil
|
||||
case "unique_samples":
|
||||
return newUniqueSamplesAggrConfig(), nil
|
||||
default:
|
||||
|
||||
@@ -53,36 +53,30 @@ 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
|
||||
lvs := av.shared.lastValues
|
||||
for lk, lv := range lvs {
|
||||
for lk, lv := range av.shared.lastValues {
|
||||
if ctx.flushTimestamp > lv.deleteDeadline || isLast {
|
||||
delete(lvs, lk)
|
||||
delete(av.shared.lastValues, lk)
|
||||
}
|
||||
}
|
||||
if ac.resetTotalOnFlush {
|
||||
av.shared.total = 0
|
||||
} else if math.Abs(total) >= (1 << 53) {
|
||||
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, suffix, total)
|
||||
ctx.appendSeries(key, ac.getSuffix(), total)
|
||||
}
|
||||
|
||||
func (av *totalAggrValue) state() any {
|
||||
return av.shared
|
||||
}
|
||||
|
||||
func newTotalAggrConfig(ms *metrics.Set, metricLabels string, ignoreFirstSampleIntervalSecs uint64, resetTotalOnFlush, keepFirstSample bool) aggrConfig {
|
||||
func newTotalAggrConfig(ms *metrics.Set, metricLabels string, ignoreFirstSampleIntervalSecs uint64, 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))
|
||||
@@ -90,8 +84,6 @@ 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
|
||||
|
||||
@@ -117,12 +109,6 @@ 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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user