Compare commits

..

8 Commits

Author SHA1 Message Date
f41gh7
24ac567a9f fixes tests after dce8193c16 2026-06-24 17:13:04 +02:00
Aliaksandr Valialkin
dce8193c16 vendor: update github.com/VictoriaMetrics/VictoriaLogs from v1.121.1-0.20260616132739-c901a1e31cb3 to v1.51.1-0.20260624061259-dc94972a8708 2026-06-24 09:40:23 +02:00
Immanuel Tikhonov
e196479fb2 apptest: fix invalid go test command in README (#1484)
apptest/README.md points to `go test ./app/apptest`, but that dir is not
in this repo. Fresh clone, instant fail, kinda rough.

This switches it to `go test ./apptest/...` and adds a short note that
`go test ./...` needs the test binaries built first.

How to repro:
1. Fresh clone the repo.
2. Run `go test ./app/apptest`.
3. See `stat .../app/apptest: directory not found`.

---------

Signed-off-by: immanuwell <pchpr.00@list.ru>
Signed-off-by: Immanuel Tikhonov <122638311+immanuwell@users.noreply.github.com>
Co-authored-by: Phuong Le <39565248+func25@users.noreply.github.com>
2026-06-24 08:09:16 +02:00
Hui Wang
1c774564a2 stream aggregation: improve rate_xx results with out of order samples (#11140)
Previously, there could be an unexpected increase in `rate` if an
out-of-order sample was ingested after the previous flush.
Before:
<img width="1503" height="835" alt="image"
src="https://github.com/user-attachments/assets/3fb05f3e-51af-4c5c-989a-ec3da089fc23"
/>
After:
<img width="2546" height="924" alt="image"
src="https://github.com/user-attachments/assets/db8d8b0e-bbc0-4927-947a-713fc1fb4c5f"
/>

---------

Signed-off-by: hagen1778 <roman@victoriametrics.com>
Co-authored-by: hagen1778 <roman@victoriametrics.com>
2026-06-23 12:46:50 +02:00
Max Kotliar
e1c554d4a6 docs/changelog: follow-up on 3419328f
Follow-up on
3419328f1c
2026-06-23 11:48:23 +03:00
Zhu Jiekun
3419328f1c app/vmselect: propagate cache reset operation to selectNode (#11118)
Fixes https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11112
PR https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11118

Propagate cache reset operation to `-selectNode` when `/internal/resetRollupResultCache` is called. Previously, the propagation only happened when `delete_series` API was called.

To avoid an infinite loop, the propagation happens only when `propagate=1` query arg is set.

Note that if `-search.resetCacheAuthKey` is configured. It will be used as `authKey` query arg while propagating requests to other select instances.

---------

Signed-off-by: Zhu Jiekun <jiekun@victoriametrics.com>
Co-authored-by: Max Kotliar <mkotlyar@victoriametrics.com>
2026-06-23 11:17:02 +03:00
Max Kotliar
e841e45877 dashboards: fix typo "Sey major ..." -> "See major ..."
The credit goes to @marco-m who proposed the change in
https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11126.
2026-06-23 11:01:20 +03:00
Max Kotliar
d53d8849e7 lib/promrelabel: hide relabel debug cross-link when no target id is present (#11143)
The cross-link between `/metric-relabel-debug` and `/target-relabel-debug` pages has always been rendered, regardless of whether a target `id` query param was present or not. The target ID is responsible for preloading either metrics or the target relabeling config.

The commit hides the cross-link when no target ID is present. Without the ID, pages are essentially the same, so there is no need to link them. It should reduce user confusion.
2026-06-23 10:53:44 +03:00
67 changed files with 961 additions and 653 deletions

View File

@@ -54,14 +54,14 @@ jobs:
restore-keys: go-artifacts-${{ runner.os }}-codeql-analyze-${{ steps.go.outputs.go-version }}-
- name: Initialize CodeQL
uses: github/codeql-action/init@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
uses: github/codeql-action/init@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
with:
languages: go
- name: Autobuild
uses: github/codeql-action/autobuild@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
uses: github/codeql-action/autobuild@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
uses: github/codeql-action/analyze@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
with:
category: 'language:go'

View File

@@ -457,12 +457,10 @@ func TestSetIntervalAsTimeFilter(t *testing.T) {
f(`* | count()`, "vlogs", true)
f(`error OR _time:5m | count()`, "vlogs", true)
f(`(_time: 5m AND error) OR (_time: 5m AND warn) | count()`, "vlogs", true)
f(`* | error OR _time:5m | count()`, "vlogs", true)
f(`_time:5m | count()`, "vlogs", false)
f(`_time:2023-04-25T22:45:59Z | count()`, "vlogs", false)
f(`error AND _time:5m | count()`, "vlogs", false)
f(`* | error AND _time:5m | count()`, "vlogs", false)
}
func TestRecordingRuleExec_Partial(t *testing.T) {

View File

@@ -4,7 +4,7 @@ The `apptest` package contains the integration tests for the VictoriaMetrics
applications (such as vmstorage, vminsert, and vmselect).
An integration test aims at verifying the behavior of an application as a whole,
as apposed to a unit test that verifies the behavior of a building block of an
as opposed to a unit test that verifies the behavior of a building block of an
application.
To achieve that an integration test starts an application in a separate process
@@ -19,10 +19,10 @@ work together as a system.
The package provides a collection of helpers to start applications and make
queries to them:
- `app.go` - contains the generic code for staring an application and should
- `app.go` - contains the generic code for starting an application and should
not be used by integration tests directly.
- `{vmstorage,vminsert,etc}.go` - build on top of `app.go` and provide the
code for staring a specific application.
code for starting a specific application.
- `client.go` - provides helper functions for sending HTTP requests to
applications.
@@ -36,7 +36,7 @@ the application binary files to be built and put into the `bin` directory. The
build rule used for running integration tests, `make apptest`,
accounts for that, it builds all application binaries before running the tests.
But if you want to run the tests without `make`, i.e. by executing
`go test ./app/apptest`, you will need to build the binaries first (for example,
`go test ./apptest/tests`, you will need to build the binaries first (for example,
by executing `make all`).
Not all binaries can be built from `master` branch, cluster binaries can be built

View File

@@ -2083,7 +2083,7 @@
"type": "prometheus",
"uid": "$ds"
},
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSeу major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSee major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"fieldConfig": {
"defaults": {
"color": {

View File

@@ -2388,7 +2388,7 @@
"type": "prometheus",
"uid": "$ds"
},
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSeу major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSee major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"fieldConfig": {
"defaults": {
"color": {

View File

@@ -2084,7 +2084,7 @@
"type": "victoriametrics-metrics-datasource",
"uid": "$ds"
},
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSeу major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSee major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"fieldConfig": {
"defaults": {
"color": {

View File

@@ -2389,7 +2389,7 @@
"type": "victoriametrics-metrics-datasource",
"uid": "$ds"
},
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSeу major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSee major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"fieldConfig": {
"defaults": {
"color": {

View File

@@ -2165,7 +2165,7 @@
"type": "victoriametrics-metrics-datasource",
"uid": "$ds"
},
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSeу major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSee major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"fieldConfig": {
"defaults": {
"color": {

View File

@@ -1840,7 +1840,7 @@
"type": "victoriametrics-metrics-datasource",
"uid": "$ds"
},
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSeу major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSee major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"fieldConfig": {
"defaults": {
"color": {

View File

@@ -2164,7 +2164,7 @@
"type": "prometheus",
"uid": "$ds"
},
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSeу major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSee major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"fieldConfig": {
"defaults": {
"color": {

View File

@@ -1839,7 +1839,7 @@
"type": "prometheus",
"uid": "$ds"
},
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSeу major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"description": "Shows memory pressure based on [Pressure Stall Information](https://docs.kernel.org/accounting/psi.html).\n\n**Lower is better.**\n\nPressure is measured as amount of time within 1sec time window the process was:\n- waiting: at least one thread was blocked on memory.\n- stalled: every thread was blocked on memory (severe pressure).\n\nElevated memory pressure can slowdown the process performance by utilizing more disk IO. Consider increasing amount of available RAM limit or decreasing the load on the process.\n\nSee major page faults rate panel in Troubleshooting section if this metric continued to be high.",
"fieldConfig": {
"defaults": {
"color": {

View File

@@ -26,6 +26,9 @@ See also [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-rel
## tip
* BUGFIX: `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): propagate cache reset operation to `selectNode` when `/internal/resetRollupResultCache` is called. Previously, the propagation only happened when the `delete_series` API was called. See [#11112](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11112).
* BUGFIX: [stream aggregation](https://docs.victoriametrics.com/victoriametrics/stream-aggregation/): fix possible unexpected increases in `rate_avg` and `rate_sum` if an out-of-order sample is ingested after the previous flush. See [#11140](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11140).
## [v1.146.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.146.0)
Released at 2026-06-22
@@ -45,11 +48,11 @@ Released at 2026-06-22
* BUGFIX: [stream aggregation](https://docs.victoriametrics.com/victoriametrics/stream-aggregation/): fix issue with producing aggregated samples with identical timestamps between flushes. See [#10808](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10808).
* BUGFIX: [vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/): fix potential corruption of remote-write metadata `Unit` values. See [#11120](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11120). Thanks for @fxrlv for the contribution.
* BUGFIX: [vmalert](https://docs.victoriametrics.com/victoriametrics/vmalert/),[vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/),[vmagent](https://docs.victoriametrics.com/victoriametrics/vmagent/) and [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/): fix rare unbounded shutdown delay when config reload takes longer than `-configCheckInterval`. See [#11107](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11107). Thanks to @PleasingFungus for contribution.
* BUGFIX: [vmbackup](https://docs.victoriametrics.com/vmbackup/), [vmbackupmanager](https://docs.victoriametrics.com/victoriametrics/vmbackupmanager/): do not fail backup list if directory is absent while using `fs://` destination to align with other protocols. See [6c3c548d](https://github.com/VictoriaMetrics/VictoriaMetrics/commit/6c3c548ddb0385b749e731f52276f130e2a4e4a8).
* BUGFIX: [vmctl](https://docs.victoriametrics.com/victoriametrics/vmctl/): push metrics to configured `-pushmetrics.url` on shutdown when migration fails. Previously, metrics were not pushed if vmctl exited with an error. See [#11081](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11081). Thanks to @zasdaym for contribution.
* BUGFIX: [vmrestore](https://docs.victoriametrics.com/victoriametrics/vmrestore/): disallow restoring parts outside the configured `-storageDataPath` directory. See [710c920d](https://github.com/VictoriaMetrics/VictoriaMetrics/commit/710c920d6083327042a309e449fae4383617d817).
* BUGFIX: `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): correctly apply long tenant filters. Previously, such filters could be truncated, causing tenants to be matched incorrectly. See [#11096](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11096). Thanks for @fxrlv for the contribution.
* BUGFIX: `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): fix corrupted metrics metadata when a response contains multiple rows. See [#11115](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11115). Thanks for @fxrlv for the contribution.
* BUGFIX: [vmbackup](https://docs.victoriametrics.com/vmbackup/), [vmbackupmanager](https://docs.victoriametrics.com/victoriametrics/vmbackupmanager/): do not fail backup list if directory is absent while using `fs://` destination to align with other protocols. See [6c3c548](https://github.com/VictoriaMetrics/VictoriaMetrics/commit/6c3c548ddb0385b749e731f52276f130e2a4e4a8)
* BUGFIX: `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): don't cache empty responses for tenant IDs discovery during [multitenant queries](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html#multitenant-reads). This problem was visible during integration tests when multitenant queries were executed before the first ingestion happened. See [#10982](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/10982)
* BUGFIX: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) and `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): properly escape `metricFamilyName` at metrics metadata response. See [#11129](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/11129). Thanks for @fxrlv for the contribution.
* BUGFIX: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) and `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): prevent more cases of panic during directory deletion on `NFS`-based mounts. See [#11060](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11060).

View File

@@ -622,11 +622,13 @@ curl -Is http://localhost:8428/internal/resetRollupResultCache
Cluster version of VictoriaMetrics:
```sh
curl -Is http://<vmselect>:8481/internal/resetRollupResultCache
curl -Is http://<vmselect>:8481/internal/resetRollupResultCache?propagate=1
```
vmselect will propagate this call to the rest of the vmselects listed in its `-selectNode` cmd-line flag. If this
flag isn't set, then cache need to be purged from each vmselect individually.
vmselect will propagate this call to the rest of the vmselects listed in its `-selectNode` cmd-line flag when `propagate=1` argument is set.
If this flag or the `propagate` argument isn't set, then cache need to be purged from each vmselect individually.
If `-search.resetCacheAuthKey` is set, it will be attached to the propagation request as query argument.
### TCP and UDP

2
go.mod
View File

@@ -7,7 +7,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.8.0
github.com/VictoriaMetrics/VictoriaLogs v1.121.1-0.20260616132739-c901a1e31cb3
github.com/VictoriaMetrics/VictoriaLogs v1.51.1-0.20260624061259-dc94972a8708
github.com/VictoriaMetrics/easyproto v1.2.0
github.com/VictoriaMetrics/fastcache v1.13.3
github.com/VictoriaMetrics/metrics v1.44.0

4
go.sum
View File

@@ -52,8 +52,8 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapp
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0/go.mod h1:YqwkQPrWSC7+byyc1VlKbWLBF5JsW5IoL6xUkemYSXk=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/VictoriaMetrics/VictoriaLogs v1.121.1-0.20260616132739-c901a1e31cb3 h1:3eP8RRZitbga5EYiQ3IANrMPxpBwMAX4VA6akDaXwpU=
github.com/VictoriaMetrics/VictoriaLogs v1.121.1-0.20260616132739-c901a1e31cb3/go.mod h1:H4sDxcvk6OmC6zOt++IlDyrwfbn4F1eSLwMpR+kpRt8=
github.com/VictoriaMetrics/VictoriaLogs v1.51.1-0.20260624061259-dc94972a8708 h1:D9/Jzlm3B8PBnrWxg4ft8KYZdG607dV3lpBfPCoiJD8=
github.com/VictoriaMetrics/VictoriaLogs v1.51.1-0.20260624061259-dc94972a8708/go.mod h1:H4sDxcvk6OmC6zOt++IlDyrwfbn4F1eSLwMpR+kpRt8=
github.com/VictoriaMetrics/easyproto v1.2.0 h1:FJT9uNXA2isppFuJErbLqD306KoFlehl7Wn2dg/6oIE=
github.com/VictoriaMetrics/easyproto v1.2.0/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710=
github.com/VictoriaMetrics/fastcache v1.13.3 h1:rBabE0iIxcqKEMCwUmwHZ9dgEqXerg8FRbRDUvC7OVc=

View File

@@ -36,10 +36,12 @@ function submitRelabelDebugForm(e) {
<div class="container-fluid">
<a href="https://docs.victoriametrics.com/victoriametrics/relabeling/" target="_blank">Relabeling docs</a>{% space %}
{% if targetURL != "" %}
<a href="metric-relabel-debug{% if targetID != "" %}?id={%s targetID %}{% endif %}">Metric relabel debug</a>
{% else %}
<a href="target-relabel-debug{% if targetID != "" %}?id={%s targetID %}{% endif %}">Target relabel debug</a>
{% if targetID != "" %}
{% if targetURL != "" %}
<a href="metric-relabel-debug?id={%s targetID %}">Metric relabel debug</a>
{% else %}
<a href="target-relabel-debug?id={%s targetID %}">Target relabel debug</a>
{% endif %}
{% endif %}
<br>

View File

@@ -80,425 +80,417 @@ func StreamRelabelDebugStepsHTML(qw422016 *qt422016.Writer, targetURL, targetID
//line lib/promrelabel/debug.qtpl:37
qw422016.N().S(` `)
//line lib/promrelabel/debug.qtpl:39
if targetURL != "" {
//line lib/promrelabel/debug.qtpl:39
qw422016.N().S(`<a href="metric-relabel-debug`)
if targetID != "" {
//line lib/promrelabel/debug.qtpl:40
if targetID != "" {
if targetURL != "" {
//line lib/promrelabel/debug.qtpl:40
qw422016.N().S(`?id=`)
//line lib/promrelabel/debug.qtpl:40
qw422016.E().S(targetID)
//line lib/promrelabel/debug.qtpl:40
}
//line lib/promrelabel/debug.qtpl:40
qw422016.N().S(`">Metric relabel debug</a>`)
qw422016.N().S(`<a href="metric-relabel-debug?id=`)
//line lib/promrelabel/debug.qtpl:41
} else {
//line lib/promrelabel/debug.qtpl:41
qw422016.N().S(`<a href="target-relabel-debug`)
//line lib/promrelabel/debug.qtpl:42
if targetID != "" {
//line lib/promrelabel/debug.qtpl:42
qw422016.N().S(`?id=`)
//line lib/promrelabel/debug.qtpl:42
qw422016.E().S(targetID)
//line lib/promrelabel/debug.qtpl:41
qw422016.N().S(`">Metric relabel debug</a>`)
//line lib/promrelabel/debug.qtpl:42
}
} else {
//line lib/promrelabel/debug.qtpl:42
qw422016.N().S(`">Target relabel debug</a>`)
qw422016.N().S(`<a href="target-relabel-debug?id=`)
//line lib/promrelabel/debug.qtpl:43
qw422016.E().S(targetID)
//line lib/promrelabel/debug.qtpl:43
qw422016.N().S(`">Target relabel debug</a>`)
//line lib/promrelabel/debug.qtpl:44
}
//line lib/promrelabel/debug.qtpl:45
}
//line lib/promrelabel/debug.qtpl:43
//line lib/promrelabel/debug.qtpl:45
qw422016.N().S(`<br>`)
//line lib/promrelabel/debug.qtpl:46
//line lib/promrelabel/debug.qtpl:48
if err != nil {
//line lib/promrelabel/debug.qtpl:47
//line lib/promrelabel/debug.qtpl:49
htmlcomponents.StreamErrorNotification(qw422016, err)
//line lib/promrelabel/debug.qtpl:48
//line lib/promrelabel/debug.qtpl:50
}
//line lib/promrelabel/debug.qtpl:48
//line lib/promrelabel/debug.qtpl:50
qw422016.N().S(`<div class="m-3"><form method="POST" onsubmit="submitRelabelDebugForm(event)">`)
//line lib/promrelabel/debug.qtpl:52
//line lib/promrelabel/debug.qtpl:54
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs)
//line lib/promrelabel/debug.qtpl:53
//line lib/promrelabel/debug.qtpl:55
if targetID != "" {
//line lib/promrelabel/debug.qtpl:53
//line lib/promrelabel/debug.qtpl:55
qw422016.N().S(`<input type="hidden" name="id" value="`)
//line lib/promrelabel/debug.qtpl:54
//line lib/promrelabel/debug.qtpl:56
qw422016.E().S(targetID)
//line lib/promrelabel/debug.qtpl:54
//line lib/promrelabel/debug.qtpl:56
qw422016.N().S(`" />`)
//line lib/promrelabel/debug.qtpl:55
//line lib/promrelabel/debug.qtpl:57
}
//line lib/promrelabel/debug.qtpl:55
//line lib/promrelabel/debug.qtpl:57
qw422016.N().S(`<input type="submit" value="Submit" class="btn btn-primary m-1" />`)
//line lib/promrelabel/debug.qtpl:57
//line lib/promrelabel/debug.qtpl:59
if targetID != "" {
//line lib/promrelabel/debug.qtpl:57
//line lib/promrelabel/debug.qtpl:59
qw422016.N().S(`<button type="button" onclick="location.href='?id=`)
//line lib/promrelabel/debug.qtpl:58
//line lib/promrelabel/debug.qtpl:60
qw422016.E().S(targetID)
//line lib/promrelabel/debug.qtpl:58
//line lib/promrelabel/debug.qtpl:60
qw422016.N().S(`'" class="btn btn-secondary m-1">Reset</button>`)
//line lib/promrelabel/debug.qtpl:59
//line lib/promrelabel/debug.qtpl:61
}
//line lib/promrelabel/debug.qtpl:59
//line lib/promrelabel/debug.qtpl:61
qw422016.N().S(`</form></div><div class="row"><main class="col-12">`)
//line lib/promrelabel/debug.qtpl:65
//line lib/promrelabel/debug.qtpl:67
streamrelabelDebugSteps(qw422016, dss, targetURL, targetID)
//line lib/promrelabel/debug.qtpl:65
//line lib/promrelabel/debug.qtpl:67
qw422016.N().S(`</main></div></div></body></html>`)
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
}
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
func WriteRelabelDebugStepsHTML(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
StreamRelabelDebugStepsHTML(qw422016, targetURL, targetID, dss, metric, relabelConfigs, err)
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
}
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
func RelabelDebugStepsHTML(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) string {
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
WriteRelabelDebugStepsHTML(qb422016, targetURL, targetID, dss, metric, relabelConfigs, err)
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
return qs422016
//line lib/promrelabel/debug.qtpl:71
//line lib/promrelabel/debug.qtpl:73
}
//line lib/promrelabel/debug.qtpl:73
//line lib/promrelabel/debug.qtpl:75
func streamrelabelDebugFormInputs(qw422016 *qt422016.Writer, metric, relabelConfigs string) {
//line lib/promrelabel/debug.qtpl:73
//line lib/promrelabel/debug.qtpl:75
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
//line lib/promrelabel/debug.qtpl:78
qw422016.E().S(relabelConfigs)
//line lib/promrelabel/debug.qtpl:76
//line lib/promrelabel/debug.qtpl:78
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
//line lib/promrelabel/debug.qtpl:83
qw422016.E().S(metric)
//line lib/promrelabel/debug.qtpl:81
//line lib/promrelabel/debug.qtpl:83
qw422016.N().S(`</textarea></div>`)
//line lib/promrelabel/debug.qtpl:83
}
//line lib/promrelabel/debug.qtpl:83
func writerelabelDebugFormInputs(qq422016 qtio422016.Writer, metric, relabelConfigs string) {
//line lib/promrelabel/debug.qtpl:83
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:83
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs)
//line lib/promrelabel/debug.qtpl:83
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:83
}
//line lib/promrelabel/debug.qtpl:83
func relabelDebugFormInputs(metric, relabelConfigs string) string {
//line lib/promrelabel/debug.qtpl:83
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:83
writerelabelDebugFormInputs(qb422016, metric, relabelConfigs)
//line lib/promrelabel/debug.qtpl:83
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:83
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:83
return qs422016
//line lib/promrelabel/debug.qtpl:83
//line lib/promrelabel/debug.qtpl:85
}
//line lib/promrelabel/debug.qtpl:85
func writerelabelDebugFormInputs(qq422016 qtio422016.Writer, metric, relabelConfigs string) {
//line lib/promrelabel/debug.qtpl:85
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:85
streamrelabelDebugFormInputs(qw422016, metric, relabelConfigs)
//line lib/promrelabel/debug.qtpl:85
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:85
}
//line lib/promrelabel/debug.qtpl:85
func relabelDebugFormInputs(metric, relabelConfigs string) string {
//line lib/promrelabel/debug.qtpl:85
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:85
writerelabelDebugFormInputs(qb422016, metric, relabelConfigs)
//line lib/promrelabel/debug.qtpl:85
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:85
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:85
return qs422016
//line lib/promrelabel/debug.qtpl:85
}
//line lib/promrelabel/debug.qtpl:87
func streamrelabelDebugSteps(qw422016 *qt422016.Writer, dss []DebugStep, targetURL, targetID string) {
//line lib/promrelabel/debug.qtpl:86
//line lib/promrelabel/debug.qtpl:88
if len(dss) > 0 {
//line lib/promrelabel/debug.qtpl:86
//line lib/promrelabel/debug.qtpl:88
qw422016.N().S(`<div class="m-3"><b>Original labels:</b> <samp>`)
//line lib/promrelabel/debug.qtpl:88
//line lib/promrelabel/debug.qtpl:90
streammustFormatLabels(qw422016, dss[0].In)
//line lib/promrelabel/debug.qtpl:88
//line lib/promrelabel/debug.qtpl:90
qw422016.N().S(`</samp></div>`)
//line lib/promrelabel/debug.qtpl:90
//line lib/promrelabel/debug.qtpl:92
}
//line lib/promrelabel/debug.qtpl:90
//line lib/promrelabel/debug.qtpl:92
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:101
for i, ds := range dss {
//line lib/promrelabel/debug.qtpl:103
for i, ds := range dss {
//line lib/promrelabel/debug.qtpl:105
inLabels, inErr := promutil.NewLabelsFromString(ds.In)
outLabels, outErr := promutil.NewLabelsFromString(ds.Out)
changedLabels := getChangedLabelNames(inLabels, outLabels)
//line lib/promrelabel/debug.qtpl:106
//line lib/promrelabel/debug.qtpl:108
qw422016.N().S(`<tr><td>`)
//line lib/promrelabel/debug.qtpl:108
//line lib/promrelabel/debug.qtpl:110
qw422016.N().D(i)
//line lib/promrelabel/debug.qtpl:108
//line lib/promrelabel/debug.qtpl:110
qw422016.N().S(`</td><td><b><pre class="m-2">`)
//line lib/promrelabel/debug.qtpl:109
//line lib/promrelabel/debug.qtpl:111
qw422016.E().S(ds.Rule)
//line lib/promrelabel/debug.qtpl:109
//line lib/promrelabel/debug.qtpl:111
qw422016.N().S(`</pre></b></td><td>`)
//line lib/promrelabel/debug.qtpl:111
//line lib/promrelabel/debug.qtpl:113
if inErr == nil {
//line lib/promrelabel/debug.qtpl:111
//line lib/promrelabel/debug.qtpl:113
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em" title="deleted and updated labels highlighted in red">`)
//line lib/promrelabel/debug.qtpl:113
//line lib/promrelabel/debug.qtpl:115
streamlabelsWithHighlight(qw422016, inLabels, changedLabels, "#D15757")
//line lib/promrelabel/debug.qtpl:113
//line lib/promrelabel/debug.qtpl:115
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:115
//line lib/promrelabel/debug.qtpl:117
} else {
//line lib/promrelabel/debug.qtpl:115
//line lib/promrelabel/debug.qtpl:117
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em; color: red" title="error parsing input labels"><pre>`)
//line lib/promrelabel/debug.qtpl:117
qw422016.E().S(inErr.Error())
//line lib/promrelabel/debug.qtpl:117
qw422016.N().S(`</pre></div>`)
//line lib/promrelabel/debug.qtpl:119
break
//line lib/promrelabel/debug.qtpl:120
}
//line lib/promrelabel/debug.qtpl:120
qw422016.N().S(`</td><td>`)
//line lib/promrelabel/debug.qtpl:123
if outErr == nil {
//line lib/promrelabel/debug.qtpl:123
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em" title="added and updated labels highlighted in blue">`)
//line lib/promrelabel/debug.qtpl:125
streamlabelsWithHighlight(qw422016, outLabels, changedLabels, "#4495e0")
//line lib/promrelabel/debug.qtpl:125
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:127
} else {
//line lib/promrelabel/debug.qtpl:127
qw422016.N().S(`<div class="m-2" style="font-size: 0.9em; color: red" title="error parsing output labels"><pre>`)
//line lib/promrelabel/debug.qtpl:129
qw422016.E().S(outErr.Error())
//line lib/promrelabel/debug.qtpl:129
qw422016.E().S(inErr.Error())
//line lib/promrelabel/debug.qtpl:119
qw422016.N().S(`</pre></div>`)
//line lib/promrelabel/debug.qtpl:131
//line lib/promrelabel/debug.qtpl:121
break
//line lib/promrelabel/debug.qtpl:132
//line lib/promrelabel/debug.qtpl:122
}
//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
//line lib/promrelabel/debug.qtpl:122
qw422016.N().S(`</td><td>`)
//line lib/promrelabel/debug.qtpl:125
if outErr == nil {
//line lib/promrelabel/debug.qtpl:125
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:127
streamlabelsWithHighlight(qw422016, outLabels, changedLabels, "#4495e0")
//line lib/promrelabel/debug.qtpl:127
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:149
//line lib/promrelabel/debug.qtpl:129
} else {
//line lib/promrelabel/debug.qtpl:129
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:131
qw422016.E().S(outErr.Error())
//line lib/promrelabel/debug.qtpl:131
qw422016.N().S(`</pre></div>`)
//line lib/promrelabel/debug.qtpl:133
break
//line lib/promrelabel/debug.qtpl:134
}
//line lib/promrelabel/debug.qtpl:149
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:151
//line lib/promrelabel/debug.qtpl:134
qw422016.N().S(`</td></tr>`)
//line lib/promrelabel/debug.qtpl:137
}
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:137
qw422016.N().S(`</tbody></table>`)
//line lib/promrelabel/debug.qtpl:140
if len(dss) > 0 {
//line lib/promrelabel/debug.qtpl:140
qw422016.N().S(`<div class="m-3"><b>Resulting labels:</b> <samp>`)
//line lib/promrelabel/debug.qtpl:142
streammustFormatLabels(qw422016, dss[len(dss)-1].Out)
//line lib/promrelabel/debug.qtpl:142
qw422016.N().S(`</samp>`)
//line lib/promrelabel/debug.qtpl:143
if targetURL != "" {
//line lib/promrelabel/debug.qtpl:143
qw422016.N().S(`<div><b>Target URL:</b>`)
//line lib/promrelabel/debug.qtpl:145
qw422016.N().S(` `)
//line lib/promrelabel/debug.qtpl:145
qw422016.N().S(`<a href="`)
//line lib/promrelabel/debug.qtpl:145
qw422016.E().S(targetURL)
//line lib/promrelabel/debug.qtpl:145
qw422016.N().S(`" target="_blank">`)
//line lib/promrelabel/debug.qtpl:145
qw422016.E().S(targetURL)
//line lib/promrelabel/debug.qtpl:145
qw422016.N().S(`</a>`)
//line lib/promrelabel/debug.qtpl:146
if targetID != "" {
//line lib/promrelabel/debug.qtpl:147
qw422016.N().S(` `)
//line lib/promrelabel/debug.qtpl:147
qw422016.N().S(`(<a href="target_response?id=`)
//line lib/promrelabel/debug.qtpl:148
qw422016.E().S(targetID)
//line lib/promrelabel/debug.qtpl:148
qw422016.N().S(`" target="_blank" title="click to fetch target response on behalf of the scraper">response</a>)`)
//line lib/promrelabel/debug.qtpl:149
}
//line lib/promrelabel/debug.qtpl:149
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:151
}
//line lib/promrelabel/debug.qtpl:151
qw422016.N().S(`</div>`)
//line lib/promrelabel/debug.qtpl:153
}
//line lib/promrelabel/debug.qtpl:154
}
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:154
func writerelabelDebugSteps(qq422016 qtio422016.Writer, dss []DebugStep, targetURL, targetID string) {
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:154
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:154
streamrelabelDebugSteps(qw422016, dss, targetURL, targetID)
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:154
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:154
}
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:154
func relabelDebugSteps(dss []DebugStep, targetURL, targetID string) string {
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:154
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:154
writerelabelDebugSteps(qb422016, dss, targetURL, targetID)
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:154
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:154
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:154
return qs422016
//line lib/promrelabel/debug.qtpl:152
//line lib/promrelabel/debug.qtpl:154
}
//line lib/promrelabel/debug.qtpl:154
//line lib/promrelabel/debug.qtpl:156
func StreamRelabelDebugStepsJSON(qw422016 *qt422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
//line lib/promrelabel/debug.qtpl:154
//line lib/promrelabel/debug.qtpl:156
qw422016.N().S(`{`)
//line lib/promrelabel/debug.qtpl:156
if err != nil {
//line lib/promrelabel/debug.qtpl:156
qw422016.N().S(`"status": "error","error":`)
//line lib/promrelabel/debug.qtpl:158
qw422016.N().Q(fmt.Sprintf("Error: %s", err))
//line lib/promrelabel/debug.qtpl:159
} else {
if err != nil {
//line lib/promrelabel/debug.qtpl:158
qw422016.N().S(`"status": "error","error":`)
//line lib/promrelabel/debug.qtpl:160
qw422016.N().Q(fmt.Sprintf("Error: %s", err))
//line lib/promrelabel/debug.qtpl:161
} else {
//line lib/promrelabel/debug.qtpl:162
var hasError bool
//line lib/promrelabel/debug.qtpl:160
//line lib/promrelabel/debug.qtpl:162
qw422016.N().S(`"status": "success","steps": [`)
//line lib/promrelabel/debug.qtpl:163
for i, ds := range dss {
//line lib/promrelabel/debug.qtpl:165
for i, ds := range dss {
//line lib/promrelabel/debug.qtpl:167
inLabels, inErr := promutil.NewLabelsFromString(ds.In)
outLabels, outErr := promutil.NewLabelsFromString(ds.Out)
changedLabels := getChangedLabelNames(inLabels, outLabels)
//line lib/promrelabel/debug.qtpl:168
//line lib/promrelabel/debug.qtpl:170
qw422016.N().S(`{"inLabels":`)
//line lib/promrelabel/debug.qtpl:170
//line lib/promrelabel/debug.qtpl:172
qw422016.N().Q(labelsWithHighlight(inLabels, changedLabels, "#D15757"))
//line lib/promrelabel/debug.qtpl:170
//line lib/promrelabel/debug.qtpl:172
qw422016.N().S(`,"outLabels":`)
//line lib/promrelabel/debug.qtpl:171
//line lib/promrelabel/debug.qtpl:173
qw422016.N().Q(labelsWithHighlight(outLabels, changedLabels, "#4495e0"))
//line lib/promrelabel/debug.qtpl:171
//line lib/promrelabel/debug.qtpl:173
qw422016.N().S(`,"rule":`)
//line lib/promrelabel/debug.qtpl:172
//line lib/promrelabel/debug.qtpl:174
qw422016.N().Q(ds.Rule)
//line lib/promrelabel/debug.qtpl:172
//line lib/promrelabel/debug.qtpl:174
qw422016.N().S(`,"errors": {`)
//line lib/promrelabel/debug.qtpl:174
if inErr != nil {
//line lib/promrelabel/debug.qtpl:174
qw422016.N().S(`"inLabels":`)
//line lib/promrelabel/debug.qtpl:175
qw422016.N().Q(`<span style="color: #D15757">` + inErr.Error() + `</span>`)
//line lib/promrelabel/debug.qtpl:175
if outErr != nil {
//line lib/promrelabel/debug.qtpl:175
qw422016.N().S(`,`)
//line lib/promrelabel/debug.qtpl:175
}
//line lib/promrelabel/debug.qtpl:176
hasError = true
if inErr != nil {
//line lib/promrelabel/debug.qtpl:176
qw422016.N().S(`"inLabels":`)
//line lib/promrelabel/debug.qtpl:177
} else {
qw422016.N().Q(`<span style="color: #D15757">` + inErr.Error() + `</span>`)
//line lib/promrelabel/debug.qtpl:177
if outErr != nil {
//line lib/promrelabel/debug.qtpl:177
qw422016.N().S(`,`)
//line lib/promrelabel/debug.qtpl:177
}
//line lib/promrelabel/debug.qtpl:178
}
//line lib/promrelabel/debug.qtpl:179
if outErr != nil {
//line lib/promrelabel/debug.qtpl:179
qw422016.N().S(`"outLabels":`)
//line lib/promrelabel/debug.qtpl:180
qw422016.N().Q(`<span style="color: #D15757">` + outErr.Error() + `</span>`)
//line lib/promrelabel/debug.qtpl:181
hasError = true
//line lib/promrelabel/debug.qtpl:182
//line lib/promrelabel/debug.qtpl:179
} else {
//line lib/promrelabel/debug.qtpl:180
}
//line lib/promrelabel/debug.qtpl:181
if outErr != nil {
//line lib/promrelabel/debug.qtpl:181
qw422016.N().S(`"outLabels":`)
//line lib/promrelabel/debug.qtpl:182
qw422016.N().Q(`<span style="color: #D15757">` + outErr.Error() + `</span>`)
//line lib/promrelabel/debug.qtpl:183
hasError = true
//line lib/promrelabel/debug.qtpl:184
}
//line lib/promrelabel/debug.qtpl:184
qw422016.N().S(`}}`)
//line lib/promrelabel/debug.qtpl:185
//line lib/promrelabel/debug.qtpl:187
if i != len(dss)-1 {
//line lib/promrelabel/debug.qtpl:185
//line lib/promrelabel/debug.qtpl:187
qw422016.N().S(`,`)
//line lib/promrelabel/debug.qtpl:185
//line lib/promrelabel/debug.qtpl:187
}
//line lib/promrelabel/debug.qtpl:186
//line lib/promrelabel/debug.qtpl:188
}
//line lib/promrelabel/debug.qtpl:186
//line lib/promrelabel/debug.qtpl:188
qw422016.N().S(`]`)
//line lib/promrelabel/debug.qtpl:188
//line lib/promrelabel/debug.qtpl:190
if len(dss) > 0 && !hasError {
//line lib/promrelabel/debug.qtpl:188
//line lib/promrelabel/debug.qtpl:190
qw422016.N().S(`,"originalLabels":`)
//line lib/promrelabel/debug.qtpl:190
qw422016.N().Q(mustFormatLabels(dss[0].In))
//line lib/promrelabel/debug.qtpl:190
qw422016.N().S(`,"resultingLabels":`)
//line lib/promrelabel/debug.qtpl:191
qw422016.N().Q(mustFormatLabels(dss[len(dss)-1].Out))
//line lib/promrelabel/debug.qtpl:192
qw422016.N().Q(mustFormatLabels(dss[0].In))
//line lib/promrelabel/debug.qtpl:192
qw422016.N().S(`,"resultingLabels":`)
//line lib/promrelabel/debug.qtpl:193
qw422016.N().Q(mustFormatLabels(dss[len(dss)-1].Out))
//line lib/promrelabel/debug.qtpl:194
}
//line lib/promrelabel/debug.qtpl:193
//line lib/promrelabel/debug.qtpl:195
}
//line lib/promrelabel/debug.qtpl:193
//line lib/promrelabel/debug.qtpl:195
qw422016.N().S(`}`)
//line lib/promrelabel/debug.qtpl:195
}
//line lib/promrelabel/debug.qtpl:195
func WriteRelabelDebugStepsJSON(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
//line lib/promrelabel/debug.qtpl:195
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:195
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, err)
//line lib/promrelabel/debug.qtpl:195
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:195
}
//line lib/promrelabel/debug.qtpl:195
func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) string {
//line lib/promrelabel/debug.qtpl:195
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:195
WriteRelabelDebugStepsJSON(qb422016, targetURL, targetID, dss, metric, relabelConfigs, err)
//line lib/promrelabel/debug.qtpl:195
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:195
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:195
return qs422016
//line lib/promrelabel/debug.qtpl:195
//line lib/promrelabel/debug.qtpl:197
}
//line lib/promrelabel/debug.qtpl:197
func streamlabelsWithHighlight(qw422016 *qt422016.Writer, labels *promutil.Labels, highlight map[string]struct{}, color string) {
func WriteRelabelDebugStepsJSON(qq422016 qtio422016.Writer, targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) {
//line lib/promrelabel/debug.qtpl:197
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:197
StreamRelabelDebugStepsJSON(qw422016, targetURL, targetID, dss, metric, relabelConfigs, err)
//line lib/promrelabel/debug.qtpl:197
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:197
}
//line lib/promrelabel/debug.qtpl:197
func RelabelDebugStepsJSON(targetURL, targetID string, dss []DebugStep, metric, relabelConfigs string, err error) string {
//line lib/promrelabel/debug.qtpl:197
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:197
WriteRelabelDebugStepsJSON(qb422016, targetURL, targetID, dss, metric, relabelConfigs, err)
//line lib/promrelabel/debug.qtpl:197
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:197
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:197
return qs422016
//line lib/promrelabel/debug.qtpl:197
}
//line lib/promrelabel/debug.qtpl:199
func streamlabelsWithHighlight(qw422016 *qt422016.Writer, labels *promutil.Labels, highlight map[string]struct{}, color string) {
//line lib/promrelabel/debug.qtpl:201
labelsList := labels.GetLabels()
metricName := ""
for i, label := range labelsList {
@@ -509,153 +501,153 @@ func streamlabelsWithHighlight(qw422016 *qt422016.Writer, labels *promutil.Label
}
}
//line lib/promrelabel/debug.qtpl:209
//line lib/promrelabel/debug.qtpl:211
if metricName != "" {
//line lib/promrelabel/debug.qtpl:210
if _, ok := highlight["__name__"]; ok {
//line lib/promrelabel/debug.qtpl:210
qw422016.N().S(`<span style="font-weight:bold;color:`)
//line lib/promrelabel/debug.qtpl:211
qw422016.E().S(color)
//line lib/promrelabel/debug.qtpl:211
qw422016.N().S(`">`)
//line lib/promrelabel/debug.qtpl:211
qw422016.E().S(metricName)
//line lib/promrelabel/debug.qtpl:211
qw422016.N().S(`</span>`)
//line lib/promrelabel/debug.qtpl:212
} else {
if _, ok := highlight["__name__"]; ok {
//line lib/promrelabel/debug.qtpl:212
qw422016.N().S(`<span style="font-weight:bold;color:`)
//line lib/promrelabel/debug.qtpl:213
qw422016.E().S(color)
//line lib/promrelabel/debug.qtpl:213
qw422016.N().S(`">`)
//line lib/promrelabel/debug.qtpl:213
qw422016.E().S(metricName)
//line lib/promrelabel/debug.qtpl:214
}
//line lib/promrelabel/debug.qtpl:215
if len(labelsList) == 0 {
//line lib/promrelabel/debug.qtpl:215
return
//line lib/promrelabel/debug.qtpl:215
}
//line lib/promrelabel/debug.qtpl:216
}
//line lib/promrelabel/debug.qtpl:216
qw422016.N().S(`{`)
//line lib/promrelabel/debug.qtpl:218
for i, label := range labelsList {
//line lib/promrelabel/debug.qtpl:219
if _, ok := highlight[label.Name]; ok {
//line lib/promrelabel/debug.qtpl:219
qw422016.N().S(`<span style="font-weight:bold;color:`)
//line lib/promrelabel/debug.qtpl:220
qw422016.E().S(color)
//line lib/promrelabel/debug.qtpl:220
qw422016.N().S(`">`)
//line lib/promrelabel/debug.qtpl:220
qw422016.E().S(label.Name)
//line lib/promrelabel/debug.qtpl:220
qw422016.N().S(`=`)
//line lib/promrelabel/debug.qtpl:220
qw422016.E().Q(label.Value)
//line lib/promrelabel/debug.qtpl:220
//line lib/promrelabel/debug.qtpl:213
qw422016.N().S(`</span>`)
//line lib/promrelabel/debug.qtpl:221
//line lib/promrelabel/debug.qtpl:214
} else {
//line lib/promrelabel/debug.qtpl:215
qw422016.E().S(metricName)
//line lib/promrelabel/debug.qtpl:216
}
//line lib/promrelabel/debug.qtpl:217
if len(labelsList) == 0 {
//line lib/promrelabel/debug.qtpl:217
return
//line lib/promrelabel/debug.qtpl:217
}
//line lib/promrelabel/debug.qtpl:218
}
//line lib/promrelabel/debug.qtpl:218
qw422016.N().S(`{`)
//line lib/promrelabel/debug.qtpl:220
for i, label := range labelsList {
//line lib/promrelabel/debug.qtpl:221
if _, ok := highlight[label.Name]; ok {
//line lib/promrelabel/debug.qtpl:221
qw422016.N().S(`<span style="font-weight:bold;color:`)
//line lib/promrelabel/debug.qtpl:222
qw422016.E().S(color)
//line lib/promrelabel/debug.qtpl:222
qw422016.N().S(`">`)
//line lib/promrelabel/debug.qtpl:222
qw422016.E().S(label.Name)
//line lib/promrelabel/debug.qtpl:222
qw422016.N().S(`=`)
//line lib/promrelabel/debug.qtpl:222
qw422016.E().Q(label.Value)
//line lib/promrelabel/debug.qtpl:222
qw422016.N().S(`</span>`)
//line lib/promrelabel/debug.qtpl:223
}
} else {
//line lib/promrelabel/debug.qtpl:224
qw422016.E().S(label.Name)
//line lib/promrelabel/debug.qtpl:224
qw422016.N().S(`=`)
//line lib/promrelabel/debug.qtpl:224
qw422016.E().Q(label.Value)
//line lib/promrelabel/debug.qtpl:225
}
//line lib/promrelabel/debug.qtpl:226
if i < len(labelsList)-1 {
//line lib/promrelabel/debug.qtpl:224
//line lib/promrelabel/debug.qtpl:226
qw422016.N().S(`,`)
//line lib/promrelabel/debug.qtpl:224
//line lib/promrelabel/debug.qtpl:226
qw422016.N().S(` `)
//line lib/promrelabel/debug.qtpl:224
//line lib/promrelabel/debug.qtpl:226
}
//line lib/promrelabel/debug.qtpl:225
//line lib/promrelabel/debug.qtpl:227
}
//line lib/promrelabel/debug.qtpl:225
//line lib/promrelabel/debug.qtpl:227
qw422016.N().S(`}`)
//line lib/promrelabel/debug.qtpl:227
}
//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:227
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:227
streamlabelsWithHighlight(qw422016, labels, highlight, color)
//line lib/promrelabel/debug.qtpl:227
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:227
}
//line lib/promrelabel/debug.qtpl:227
func labelsWithHighlight(labels *promutil.Labels, highlight map[string]struct{}, color string) string {
//line lib/promrelabel/debug.qtpl:227
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:227
writelabelsWithHighlight(qb422016, labels, highlight, color)
//line lib/promrelabel/debug.qtpl:227
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:227
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:227
return qs422016
//line lib/promrelabel/debug.qtpl:227
//line lib/promrelabel/debug.qtpl:229
}
//line lib/promrelabel/debug.qtpl:229
func writelabelsWithHighlight(qq422016 qtio422016.Writer, labels *promutil.Labels, highlight map[string]struct{}, color string) {
//line lib/promrelabel/debug.qtpl:229
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:229
streamlabelsWithHighlight(qw422016, labels, highlight, color)
//line lib/promrelabel/debug.qtpl:229
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:229
}
//line lib/promrelabel/debug.qtpl:229
func labelsWithHighlight(labels *promutil.Labels, highlight map[string]struct{}, color string) string {
//line lib/promrelabel/debug.qtpl:229
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:229
writelabelsWithHighlight(qb422016, labels, highlight, color)
//line lib/promrelabel/debug.qtpl:229
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:229
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:229
return qs422016
//line lib/promrelabel/debug.qtpl:229
}
//line lib/promrelabel/debug.qtpl:231
func streammustFormatLabels(qw422016 *qt422016.Writer, s string) {
//line lib/promrelabel/debug.qtpl:230
//line lib/promrelabel/debug.qtpl:232
labels, err := promutil.NewLabelsFromString(s)
//line lib/promrelabel/debug.qtpl:231
if err != nil {
//line lib/promrelabel/debug.qtpl:231
qw422016.N().S(`<span style="color: red" title="error parsing labels:`)
//line lib/promrelabel/debug.qtpl:232
qw422016.E().S(err.Error())
//line lib/promrelabel/debug.qtpl:232
qw422016.N().S(`">`)
//line lib/promrelabel/debug.qtpl:232
qw422016.E().S("error parsing labels: " + err.Error())
//line lib/promrelabel/debug.qtpl:232
qw422016.N().S(`</span>`)
//line lib/promrelabel/debug.qtpl:233
} else {
if err != nil {
//line lib/promrelabel/debug.qtpl:233
qw422016.N().S(`<span style="color: red" title="error parsing labels:`)
//line lib/promrelabel/debug.qtpl:234
streamlabelsWithHighlight(qw422016, labels, nil, "")
qw422016.E().S(err.Error())
//line lib/promrelabel/debug.qtpl:234
qw422016.N().S(`">`)
//line lib/promrelabel/debug.qtpl:234
qw422016.E().S("error parsing labels: " + err.Error())
//line lib/promrelabel/debug.qtpl:234
qw422016.N().S(`</span>`)
//line lib/promrelabel/debug.qtpl:235
} else {
//line lib/promrelabel/debug.qtpl:236
streamlabelsWithHighlight(qw422016, labels, nil, "")
//line lib/promrelabel/debug.qtpl:237
}
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
}
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
func writemustFormatLabels(qq422016 qtio422016.Writer, s string) {
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
qw422016 := qt422016.AcquireWriter(qq422016)
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
streammustFormatLabels(qw422016, s)
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
qt422016.ReleaseWriter(qw422016)
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
}
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
func mustFormatLabels(s string) string {
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
qb422016 := qt422016.AcquireByteBuffer()
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
writemustFormatLabels(qb422016, s)
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
qs422016 := string(qb422016.B)
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
qt422016.ReleaseByteBuffer(qb422016)
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
return qs422016
//line lib/promrelabel/debug.qtpl:236
//line lib/promrelabel/debug.qtpl:238
}

View File

@@ -108,7 +108,7 @@ func (av *rateAggrValue) pushSample(c aggrConfig, sample *pushSample, key string
}
if ok {
state = sv.getState(av.isGreen)
if sample.timestamp < state.timestamp {
if sample.timestamp < state.timestamp || sample.timestamp < sv.prevTimestamp {
// Skip out of order sample
return
}
@@ -143,9 +143,6 @@ func (av *rateAggrValue) flush(c aggrConfig, ctx *flushCtx, key string, isLast b
putRateAggrSharedValue(sv)
continue
}
if sv.prevTimestamp == 0 {
continue
}
state = sv.getState(av.isGreen)
if state.timestamp > 0 {
d := float64(state.timestamp-sv.prevTimestamp) / 1000

View File

@@ -789,6 +789,24 @@ foo:1m_by_cde_rate_sum{cde="1"} 0.125
outputs: [rate_sum, rate_avg]
`, "11111")
// test rate_sum with out of order samples
f([]string{`
foo 1
`, `
foo 61
`, `
foo 31 -70
foo 91
`, `
foo 121
`}, time.Minute, `foo:1m_rate_sum 1
foo:1m_rate_sum 0.5
foo:1m_rate_sum 0.5
`, `
- interval: 1m
outputs: [rate_sum]
`, "11111")
// test rate_sum and rate_avg with different staleness intervals
f([]string{`
foo{abc="123", cde="1"} 1

View File

@@ -30,7 +30,7 @@ func parseIfFilter(lex *lexer) (*ifFilter, error) {
return newIfFilter(newFilterNoop()), nil
}
f, err := parseFilter(lex, true)
f, err := parseFilter(lex)
if err != nil {
return nil, fmt.Errorf("cannot parse 'if' filter: %w", err)
}

View File

@@ -301,38 +301,43 @@ func (lr *LogRows) NeedFlush() bool {
// MustAddInsertRow adds r to lr.
func (lr *LogRows) MustAddInsertRow(r *InsertRow) {
// verify r.StreamTagsCanonical
if err := verifyStreamTagsCanonical(r.StreamTagsCanonical, r.Fields); err != nil {
st := GetStreamTags()
streamTagsCanonical := r.StreamTagsCanonical
if err := parseStreamTagsCanonical(st, streamTagsCanonical); err != nil {
line := MarshalFieldsToJSON(nil, r.Fields)
invalidStreamTagsLogger.Warnf("cannot unmarshal streamTagsCanonical: %s; skipping the log entry; log entry: %s", err, line)
PutStreamTags(st)
return
}
if st.normalize(r.Fields) {
bLen := len(lr.a.b)
lr.a.b = st.MarshalCanonical(lr.a.b)
streamTagsCanonical = bytesutil.ToUnsafeString(lr.a.b[bLen:])
}
PutStreamTags(st)
// Calculate the id for the StreamTags
var sid streamID
sid.tenantID = r.TenantID
streamTagsCanonical := bytesutil.ToUnsafeBytes(r.StreamTagsCanonical)
sid.id = hash128(streamTagsCanonical)
sid.id = hash128(bytesutil.ToUnsafeBytes(streamTagsCanonical))
// Store the row
lr.mustAddInternal(sid, r.Timestamp, r.Fields, r.StreamTagsCanonical)
lr.mustAddInternal(sid, r.Timestamp, r.Fields, streamTagsCanonical)
}
var invalidStreamTagsLogger = logger.WithThrottler("invalid_stream_tags", 5*time.Second)
func verifyStreamTagsCanonical(streamTagsCanonical string, fields []Field) error {
st := GetStreamTags()
defer PutStreamTags(st)
func parseStreamTagsCanonical(dst *StreamTags, streamTagsCanonical string) error {
src := bytesutil.ToUnsafeBytes(streamTagsCanonical)
tail, err := st.UnmarshalCanonicalInplace(src)
tail, err := dst.UnmarshalCanonicalInplace(src)
if err != nil {
return fmt.Errorf("cannot unmarshal streamTagsCanonical: %w", err)
}
if len(tail) > 0 {
return fmt.Errorf("unexpected tail left after unmarshaling streamTagsCanonical; len(tail)=%d; streamTags: %s", len(tail), st)
return fmt.Errorf("unexpected tail left after unmarshaling streamTagsCanonical; len(tail)=%d; streamTags: %s", len(tail), dst)
}
return st.verifyCanonicalFieldValues(fields)
return nil
}
func (lr *LogRows) mustAdd(tenantID TenantID, timestamp int64, fields []Field) {
@@ -394,11 +399,7 @@ func (lr *LogRows) MustAdd(tenantID TenantID, timestamp int64, fields []Field, s
invalidStreamTagsLogger.Warnf("cannot parse _stream=%s: %s; skipping the log entry; log entry: %s", f.Value, err, line)
return
}
if err := st.verifyCanonicalFieldValues(fields); err != nil {
line := MarshalFieldsToJSON(nil, fields)
invalidStreamTagsLogger.Warnf("invalid _stream=%s: %s; skipping the log entry; log entry: %s", f.Value, err, line)
return
}
st.normalize(fields)
// Remove _stream field, since it is re-generated from st below.
f.Value = ""
case "_stream_id":

View File

@@ -1928,7 +1928,7 @@ func parseQuery(lex *lexer) (*Query, error) {
lex.pushQueryOptions(&q.opts)
defer lex.popQueryOptions()
f, err := parseFilter(lex, true)
f, err := parseFilter(lex)
if err != nil {
return nil, fmt.Errorf("%w; context: [%s]", err, lex.context())
}
@@ -2113,20 +2113,11 @@ func parseQueryOptions(dstOpts *queryOptions, lex *lexer) error {
}
}
func parseFilter(lex *lexer, allowPipeKeywords bool) (filter, error) {
func parseFilter(lex *lexer) (filter, error) {
if lex.isQueryPartTrailer() {
return nil, fmt.Errorf("missing query")
}
if !allowPipeKeywords {
// Verify the first token in the filter doesn't match pipe names.
firstToken := strings.ToLower(lex.rawToken)
if firstToken == "by" || isPipeName(firstToken) || isStatsFuncName(firstToken) {
return nil, fmt.Errorf("query filter cannot start with pipe keyword %q; see https://docs.victoriametrics.com/victorialogs/logsql/#query-syntax; "+
"please put the first word of the filter into quotes", firstToken)
}
}
fo, err := parseFilterOr(lex, "")
if err != nil {
return nil, err
@@ -3875,7 +3866,7 @@ func quoteFieldFilterIfNeeded(s string) string {
if wildcard == "" || !needQuoteToken(wildcard) {
return s
}
return strconv.Quote(s)
return strconv.Quote(wildcard) + "*"
}
func quoteTokenIfNeeded(s string) string {
@@ -3912,7 +3903,7 @@ func isNumberPrefix(s string) bool {
}
func needQuoteToken(s string) bool {
if s == "." {
if !isWord(s) {
return true
}
@@ -3920,14 +3911,10 @@ func needQuoteToken(s string) bool {
if _, ok := reservedKeywords[sLower]; ok {
return true
}
if isPipeName(sLower) || isStatsFuncName(sLower) {
if isPipeName(sLower) || isStatsFuncName(sLower) || isMathFuncName(sLower) {
return true
}
for _, r := range s {
if !isTokenRune(r) && r != '.' {
return true
}
}
return false
}
@@ -4008,6 +3995,9 @@ var reservedKeywords = func() map[string]struct{} {
// 'as' is used in various pipes such as `format ... as ...`
"as",
// 'from' is used in various pipes such as `split ... from ...` and `unpack_json from ...`
"from",
}
m := make(map[string]struct{}, len(kws))
for _, kw := range kws {
@@ -4057,12 +4047,12 @@ func toFieldsFilters(pf *prefixfilter.Filter) string {
denyFilters := pf.GetDenyFilters()
if len(denyFilters) > 0 {
qStr += " | delete " + fieldNamesString(denyFilters)
qStr += " | delete " + fieldFiltersString(denyFilters)
}
allowFilters := pf.GetAllowFilters()
if len(allowFilters) > 0 && !prefixfilter.MatchAll(allowFilters) {
qStr += " | fields " + fieldNamesString(allowFilters)
qStr += " | fields " + fieldFiltersString(allowFilters)
}
return qStr

View File

@@ -0,0 +1,123 @@
package logstorage
import (
"fmt"
"strings"
"github.com/VictoriaMetrics/VictoriaLogs/lib/prefixfilter"
)
func parseFieldNamesInParens(lex *lexer) ([]string, error) {
fieldNames, err := parseFieldFiltersInParens(lex)
if err != nil {
return nil, err
}
for _, fieldName := range fieldNames {
if prefixfilter.IsWildcardFilter(fieldName) {
return nil, fmt.Errorf("the field name %q cannot end with '*'", fieldName)
}
}
return fieldNames, nil
}
func parseFieldFiltersInParens(lex *lexer) ([]string, error) {
if !lex.isKeyword("(") {
return nil, fmt.Errorf("missing `(`")
}
var fields []string
for {
lex.nextToken()
if lex.isKeyword(")") {
lex.nextToken()
return fields, nil
}
if lex.isKeyword(",") {
return nil, fmt.Errorf("unexpected `,`")
}
field, err := parseFieldFilter(lex)
if err != nil {
return nil, err
}
fields = append(fields, field)
switch {
case lex.isKeyword(")"):
lex.nextToken()
return fields, nil
case lex.isKeyword(","):
default:
return nil, fmt.Errorf("unexpected token: %q; expecting ',' or ')'", lex.token)
}
}
}
func parseCommaSeparatedFieldNames(lex *lexer) ([]string, error) {
fieldNames, err := parseCommaSeparatedFieldFilters(lex)
if err != nil {
return nil, err
}
for _, fieldName := range fieldNames {
if prefixfilter.IsWildcardFilter(fieldName) {
return nil, fmt.Errorf("the field name %q cannot end with '*'", fieldName)
}
}
return fieldNames, nil
}
func parseCommaSeparatedFieldFilters(lex *lexer) ([]string, error) {
var fields []string
for {
field, err := parseFieldFilter(lex)
if err != nil {
return nil, fmt.Errorf("cannot parse field name: %w", err)
}
fields = append(fields, field)
if !lex.isKeyword(",") {
return fields, nil
}
lex.nextToken()
}
}
func parseFieldName(lex *lexer) (string, error) {
fieldName, err := lex.nextCompoundToken()
if err != nil {
return "", err
}
fieldName = getCanonicalColumnName(fieldName)
return fieldName, nil
}
func parseFieldFilter(lex *lexer) (string, error) {
if lex.isKeyword("*") {
lex.nextToken()
return "*", nil
}
fieldName, err := lex.nextCompoundToken()
if err != nil {
return "", err
}
fieldName = getCanonicalColumnName(fieldName)
if !lex.isSkippedSpace && lex.isKeyword("*") {
lex.nextToken()
fieldName += "*"
}
return fieldName, nil
}
func fieldNamesString(fieldNames []string) string {
a := make([]string, len(fieldNames))
for i, f := range fieldNames {
a[i] = quoteTokenIfNeeded(f)
}
return strings.Join(a, ", ")
}
func fieldFiltersString(fieldFilters []string) string {
a := make([]string, len(fieldFilters))
for i, f := range fieldFilters {
a[i] = quoteFieldFilterIfNeeded(f)
}
return strings.Join(a, ", ")
}

View File

@@ -146,23 +146,54 @@ func parsePipe(lex *lexer) (pipe, error) {
return p, nil
}
lexState := lex.backupState()
// Try parsing stats pipe without 'stats' keyword
ps, err := parsePipeStatsNoStatsKeyword(lex)
if err == nil {
if isLikelyStatsPipe(lex) {
// Try parsing stats pipe without 'stats' keyword
ps, err := parsePipeStatsNoStatsKeyword(lex)
if err != nil {
return nil, fmt.Errorf("cannot parse 'stats' pipe: %w", err)
}
return ps, nil
}
lex.restoreState(lexState)
// Try parsing filter pipe without 'filter' keyword
pf, err := parsePipeFilterNoFilterKeyword(lex)
if err == nil {
if isLikelyFilterPipe(lex) {
// Try parsing filter pipe without 'filter' keyword
pf, err := parsePipeFilterNoFilterKeyword(lex)
if err != nil {
return nil, fmt.Errorf("cannot parse 'filter' pipe: %w", err)
}
return pf, nil
}
lex.restoreState(lexState)
return nil, fmt.Errorf("unexpected pipe %q", lex.token)
return nil, fmt.Errorf("unexpected pipe name %q; probably, 'filter' is missing in front of %q; "+
"see https://docs.victoriametrics.com/victorialogs/logsql/#filter-pipe", lex.token, lex.token)
}
func isLikelyStatsPipe(lex *lexer) bool {
return isStatsFuncName(lex.rawToken) || lex.isKeyword("by", "(")
}
func isLikelyFilterPipe(lex *lexer) bool {
if lex.isQuotedToken() {
return true
}
if !isWord(lex.token) {
// Any token that isn't a word cannot clash with a pipe name,
// since all pipe names are words. So treat it as a filter.
return true
}
if lex.isKeyword("not") {
// 'not' is a logical filter operator rather than a pipe name.
return true
}
lexState := lex.backupState()
defer lex.restoreState(lexState)
stopTokens := []string{":"}
if _, err := lex.nextCompoundTokenExt(stopTokens); err != nil {
return false
}
return lex.isKeyword(":")
}
var pipeParsers map[string]pipeParseFunc
@@ -201,6 +232,7 @@ func initPipeParsers() {
"generate_sequence": parsePipeGenerateSequence,
"hash": parsePipeHash,
"join": parsePipeJoin,
"json_array_concat": parsePipeJSONArrayConcat,
"json_array_len": parsePipeJSONArrayLen,
"head": parsePipeLimit,
"keep": parsePipeFields,

View File

@@ -23,7 +23,7 @@ func (pc *pipeCoalesce) String() string {
logger.Panicf("BUG: pipeCoalesce must contain at least one srcField")
}
s := "coalesce(" + fieldNamesString(pc.srcFieldFilters) + ")"
s := "coalesce(" + fieldFiltersString(pc.srcFieldFilters) + ")"
if pc.defaultValue != "" {
s += " default " + quoteTokenIfNeeded(pc.defaultValue)
}

View File

@@ -21,7 +21,7 @@ func (pd *pipeDelete) String() string {
logger.Panicf("BUG: pipeDelete must contain at least a single field")
}
return "delete " + fieldNamesString(pd.fieldFilters)
return "delete " + fieldFiltersString(pd.fieldFilters)
}
func (pd *pipeDelete) splitToRemoteAndLocal(_ int64) (pipe, []pipe) {
@@ -87,7 +87,7 @@ func parsePipeDelete(lex *lexer) (pipe, error) {
}
lex.nextToken()
fieldFilters, err := parseCommaSeparatedFields(lex)
fieldFilters, err := parseCommaSeparatedFieldFilters(lex)
if err != nil {
return nil, err
}

View File

@@ -20,7 +20,7 @@ func (pf *pipeFields) String() string {
if len(pf.fieldFilters) == 0 {
logger.Panicf("BUG: pipeFields must contain at least a single field filter")
}
return "fields " + fieldNamesString(pf.fieldFilters)
return "fields " + fieldFiltersString(pf.fieldFilters)
}
func (pf *pipeFields) splitToRemoteAndLocal(_ int64) (pipe, []pipe) {
@@ -100,7 +100,7 @@ func parsePipeFields(lex *lexer) (pipe, error) {
}
lex.nextToken()
fieldFilters, err := parseCommaSeparatedFields(lex)
fieldFilters, err := parseCommaSeparatedFieldFilters(lex)
if err != nil {
return nil, err
}
@@ -109,18 +109,3 @@ func parsePipeFields(lex *lexer) (pipe, error) {
}
return pf, nil
}
func parseCommaSeparatedFields(lex *lexer) ([]string, error) {
var fields []string
for {
field, err := parseFieldFilter(lex)
if err != nil {
return nil, fmt.Errorf("cannot parse field name: %w", err)
}
fields = append(fields, field)
if !lex.isKeyword(",") {
return fields, nil
}
lex.nextToken()
}
}

View File

@@ -124,7 +124,7 @@ func parsePipeFilterExt(lex *lexer, needFilterKeyword bool) (pipe, error) {
lex.nextToken()
}
f, err := parseFilter(lex, needFilterKeyword)
f, err := parseFilter(lex)
if err != nil {
return nil, fmt.Errorf("cannot parse 'filter': %w", err)
}

View File

@@ -192,7 +192,6 @@ func (pjp *pipeJoinProcessor) writeBlock(workerID uint, br *blockResult) {
for i := range cs {
name := cs[i].name
byValuesIdxs[i] = slices.Index(pj.byFields, name)
}
for rowIdx := range br.rowsLen {

View File

@@ -0,0 +1,197 @@
package logstorage
import (
"fmt"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/atomicutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
"github.com/VictoriaMetrics/VictoriaLogs/lib/prefixfilter"
)
// pipeJSONArrayConcat processes '| json_array_concat ...' pipe.
//
// See https://docs.victoriametrics.com/victorialogs/logsql/#json_array_concat-pipe
type pipeJSONArrayConcat struct {
delimiter string
fromField string
resultField string
}
func (pc *pipeJSONArrayConcat) String() string {
s := "json_array_concat"
if pc.delimiter != "" {
s += " " + quoteTokenIfNeeded(pc.delimiter)
}
if !isMsgFieldName(pc.fromField) {
s += " from " + quoteTokenIfNeeded(pc.fromField)
}
if pc.resultField != pc.fromField {
s += " as " + quoteTokenIfNeeded(pc.resultField)
}
return s
}
func (pc *pipeJSONArrayConcat) splitToRemoteAndLocal(_ int64) (pipe, []pipe) {
return pc, nil
}
func (pc *pipeJSONArrayConcat) canLiveTail() bool {
return true
}
func (pc *pipeJSONArrayConcat) canReturnLastNResults() bool {
return pc.resultField != "_time"
}
func (pc *pipeJSONArrayConcat) isFixedOutputFieldsOrder() bool {
return false
}
func (pc *pipeJSONArrayConcat) updateNeededFields(pf *prefixfilter.Filter) {
if pf.MatchString(pc.resultField) {
pf.AddDenyFilter(pc.resultField)
pf.AddAllowFilter(pc.fromField)
}
}
func (pc *pipeJSONArrayConcat) hasFilterInWithQuery() bool {
return false
}
func (pc *pipeJSONArrayConcat) initFilterInValues(_ *inValuesCache, _ getFieldValuesFunc) (pipe, error) {
return pc, nil
}
func (pc *pipeJSONArrayConcat) visitSubqueries(_ func(q *Query)) {
// nothing to do
}
func (pc *pipeJSONArrayConcat) newPipeProcessor(_ int, _ <-chan struct{}, _ func(), ppNext pipeProcessor) pipeProcessor {
pcp := &pipeJSONArrayConcatProcessor{
pc: pc,
ppNext: ppNext,
}
pcp.shards.Init = func(shard *pipeJSONArrayConcatProcessorShard) {
shard.reset()
}
return pcp
}
type pipeJSONArrayConcatProcessor struct {
pc *pipeJSONArrayConcat
ppNext pipeProcessor
shards atomicutil.Slice[pipeJSONArrayConcatProcessorShard]
}
func (pcp *pipeJSONArrayConcatProcessor) writeBlock(workerID uint, br *blockResult) {
if br.rowsLen == 0 {
return
}
shard := pcp.shards.Get(workerID)
shard.rc.name = pcp.pc.resultField
c := br.getColumnByName(pcp.pc.fromField)
delimiter := pcp.pc.delimiter
if c.isConst {
// Fast path for const column
v := c.valuesEncoded[0]
out := shard.concat(v, delimiter)
shard.rc.addValue(out)
br.addResultColumnConst(shard.rc)
} else {
// Slow path for other columns
values := c.getValues(br)
prevOut := ""
for rowIdx := range values {
if rowIdx == 0 || values[rowIdx] != values[rowIdx-1] {
prevOut = shard.concat(values[rowIdx], delimiter)
}
shard.rc.addValue(prevOut)
}
br.addResultColumn(shard.rc)
}
pcp.ppNext.writeBlock(workerID, br)
shard.reset()
}
type pipeJSONArrayConcatProcessorShard struct {
a arena
rc resultColumn
tmpValues []string
}
func (shard *pipeJSONArrayConcatProcessorShard) reset() {
shard.a.reset()
shard.rc.reset()
shard.tmpValues = shard.tmpValues[:0]
}
func (shard *pipeJSONArrayConcatProcessorShard) concat(arrayStr, delimiter string) string {
shard.tmpValues = unpackJSONArray(shard.tmpValues[:0], &shard.a, arrayStr)
bLen := len(shard.a.b)
for i, v := range shard.tmpValues {
if i > 0 {
shard.a.b = append(shard.a.b, delimiter...)
}
shard.a.b = append(shard.a.b, v...)
}
return bytesutil.ToUnsafeString(shard.a.b[bLen:])
}
func (pcp *pipeJSONArrayConcatProcessor) flush() error {
return nil
}
func parsePipeJSONArrayConcat(lex *lexer) (pipe, error) {
if !lex.isKeyword("json_array_concat") {
return nil, fmt.Errorf("unexpected token: %q; want %q", lex.token, "json_array_concat")
}
lex.nextToken()
delimiter := ""
if !lex.isQueryPartTrailer() && !lex.isKeyword("from", "as") {
s, err := lex.nextCompoundToken()
if err != nil {
return nil, fmt.Errorf("cannot parse delimiter for 'json_array_concat': %w", err)
}
delimiter = s
}
fromField := "_msg"
if !lex.isQueryPartTrailer() && !lex.isKeyword("as") {
if lex.isKeyword("from") {
lex.nextToken()
}
f, err := parseFieldName(lex)
if err != nil {
return nil, fmt.Errorf("cannot parse 'from' field for 'json_array_concat': %w", err)
}
fromField = f
}
resultField := fromField
if !lex.isQueryPartTrailer() {
if lex.isKeyword("as") {
lex.nextToken()
}
f, err := parseFieldName(lex)
if err != nil {
return nil, fmt.Errorf("cannot parse result field for 'json_array_concat': %w", err)
}
resultField = f
}
return &pipeJSONArrayConcat{
delimiter: delimiter,
fromField: fromField,
resultField: resultField,
}, nil
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"math"
"strings"
"sync"
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/atomicutil"
@@ -567,32 +568,51 @@ func parseMathExprInParens(lex *lexer) (*mathExpr, error) {
return me, nil
}
type mathFuncParser func(lex *lexer) (*mathExpr, error)
var mathFuncParsers map[string]mathFuncParser
var mathFuncParsersOnce sync.Once
func getMathFuncParsers() map[string]mathFuncParser {
mathFuncParsersOnce.Do(initMathFuncParsers)
return mathFuncParsers
}
func initMathFuncParsers() {
mathFuncParsers = map[string]mathFuncParser{
"abs": parseMathExprAbs,
"ceil": parseMathExprCeil,
"exp": parseMathExprExp,
"floor": parseMathExprFloor,
"ln": parseMathExprLn,
"max": parseMathExprMax,
"min": parseMathExprMin,
"now": parseMathExprNow,
"rand": parseMathExprRand,
"round": parseMathExprRound,
}
}
func isMathFuncName(s string) bool {
mps := getMathFuncParsers()
sLower := strings.ToLower(s)
return mps[sLower] != nil
}
func parseMathExprOperand(lex *lexer) (*mathExpr, error) {
if lex.isKeyword("(") {
return parseMathExprInParens(lex)
}
// A quoted token (e.g. "abs") isn't a keyword, so isKeyword() returns false for it
// and it falls through to parseMathExprFieldName below as a field name.
for funcName, parseFunc := range getMathFuncParsers() {
if lex.isKeyword(funcName) {
return parseFunc(lex)
}
}
switch {
case lex.isKeyword("abs"):
return parseMathExprAbs(lex)
case lex.isKeyword("exp"):
return parseMathExprExp(lex)
case lex.isKeyword("ln"):
return parseMathExprLn(lex)
case lex.isKeyword("max"):
return parseMathExprMax(lex)
case lex.isKeyword("min"):
return parseMathExprMin(lex)
case lex.isKeyword("now"):
return parseMathExprNow(lex)
case lex.isKeyword("rand"):
return parseMathExprRand(lex)
case lex.isKeyword("round"):
return parseMathExprRound(lex)
case lex.isKeyword("ceil"):
return parseMathExprCeil(lex)
case lex.isKeyword("floor"):
return parseMathExprFloor(lex)
case lex.isKeyword("-"):
return parseMathExprUnaryMinus(lex)
case lex.isKeyword("+"):

View File

@@ -20,7 +20,7 @@ type pipePackJSON struct {
func (pp *pipePackJSON) String() string {
s := "pack_json"
if len(pp.fieldFilters) > 0 {
s += " fields (" + fieldNamesString(pp.fieldFilters) + ")"
s += " fields (" + fieldFiltersString(pp.fieldFilters) + ")"
}
if !isMsgFieldName(pp.resultField) {
s += " as " + quoteTokenIfNeeded(pp.resultField)

View File

@@ -20,7 +20,7 @@ type pipePackLogfmt struct {
func (pp *pipePackLogfmt) String() string {
s := "pack_logfmt"
if len(pp.fieldFilters) > 0 {
s += " fields (" + fieldNamesString(pp.fieldFilters) + ")"
s += " fields (" + fieldFiltersString(pp.fieldFilters) + ")"
}
if !isMsgFieldName(pp.resultField) {
s += " as " + quoteTokenIfNeeded(pp.resultField)

View File

@@ -92,7 +92,6 @@ func (pr *pipeReplaceRegexp) newPipeProcessor(_ int, _ <-chan struct{}, _ func()
}
return newPipeUpdateProcessor(updateFunc, ppNext, pr.field, pr.iff)
}
func parsePipeReplaceRegexp(lex *lexer) (pipe, error) {

View File

@@ -25,7 +25,7 @@ func (ps *pipeSetStreamFields) String() string {
if ps.iff != nil {
s += " " + ps.iff.String()
}
s += " " + fieldNamesString(ps.streamFieldFilters)
s += " " + fieldFiltersString(ps.streamFieldFilters)
return s
}
@@ -186,7 +186,7 @@ func parsePipeSetStreamFields(lex *lexer) (pipe, error) {
}
// Parse stream fields
streamFieldFilters, err := parseCommaSeparatedFields(lex)
streamFieldFilters, err := parseCommaSeparatedFieldFilters(lex)
if err != nil {
return nil, err
}

View File

@@ -1395,7 +1395,7 @@ func parsePipeStatsExt(lex *lexer, needStatsKeyword bool) (pipe, error) {
for {
e, err := parseStatsEntry(lex)
if err != nil {
return nil, fmt.Errorf("cannot parse 'stats' entry: %w", err)
return nil, err
}
ps.entries = append(ps.entries, e)
@@ -1779,85 +1779,6 @@ func tryParseBucketSize(s string) (float64, bool) {
return 0, false
}
func parseFieldNamesInParens(lex *lexer) ([]string, error) {
fieldNames, err := parseFieldFiltersInParens(lex)
if err != nil {
return nil, err
}
for _, fieldName := range fieldNames {
if prefixfilter.IsWildcardFilter(fieldName) {
return nil, fmt.Errorf("the field name %q cannot end with '*'", fieldName)
}
}
return fieldNames, nil
}
func parseFieldFiltersInParens(lex *lexer) ([]string, error) {
if !lex.isKeyword("(") {
return nil, fmt.Errorf("missing `(`")
}
var fields []string
for {
lex.nextToken()
if lex.isKeyword(")") {
lex.nextToken()
return fields, nil
}
if lex.isKeyword(",") {
return nil, fmt.Errorf("unexpected `,`")
}
field, err := parseFieldFilter(lex)
if err != nil {
return nil, err
}
fields = append(fields, field)
switch {
case lex.isKeyword(")"):
lex.nextToken()
return fields, nil
case lex.isKeyword(","):
default:
return nil, fmt.Errorf("unexpected token: %q; expecting ',' or ')'", lex.token)
}
}
}
func parseFieldName(lex *lexer) (string, error) {
fieldName, err := lex.nextCompoundToken()
if err != nil {
return "", err
}
fieldName = getCanonicalColumnName(fieldName)
return fieldName, nil
}
func parseFieldFilter(lex *lexer) (string, error) {
if lex.isKeyword("*") {
lex.nextToken()
return "*", nil
}
fieldName, err := lex.nextCompoundToken()
if err != nil {
return "", err
}
fieldName = getCanonicalColumnName(fieldName)
if !lex.isSkippedSpace && lex.isKeyword("*") {
lex.nextToken()
fieldName += "*"
}
return fieldName, nil
}
func fieldNamesString(fields []string) string {
a := make([]string, len(fields))
for i, f := range fields {
a[i] = quoteFieldFilterIfNeeded(f)
}
return strings.Join(a, ", ")
}
func areConstValues(values []string) bool {
if len(values) == 0 {
return false

View File

@@ -621,7 +621,7 @@ func parsePipeTop(lex *lexer) (pipe, error) {
}
byFields = bfs
} else if !lex.isKeyword("hits", "rank") && !lex.isQueryPartTrailer() {
bfs, err := parseCommaSeparatedFields(lex)
bfs, err := parseCommaSeparatedFieldNames(lex)
if err != nil {
return nil, fmt.Errorf("cannot parse 'by ...': %w", err)
}

View File

@@ -551,7 +551,7 @@ func parsePipeUniq(lex *lexer) (pipe, error) {
}
byFields = bfs
} else if !lex.isKeyword("filter", "with", "hits", "limit") && !lex.isQueryPartTrailer() {
bfs, err := parseCommaSeparatedFields(lex)
bfs, err := parseCommaSeparatedFieldNames(lex)
if err != nil {
return nil, fmt.Errorf("cannot parse 'by ...': %w", err)
}

View File

@@ -83,7 +83,6 @@ func (uctx *fieldsUnpackerContext) addField(name, value string) {
func newPipeUnpackProcessor(unpackFunc func(uctx *fieldsUnpackerContext, s string), ppNext pipeProcessor,
fromField string, fieldPrefix string, keepOriginalFields, skipEmptyResults bool, iff *ifFilter) *pipeUnpackProcessor {
return &pipeUnpackProcessor{
unpackFunc: unpackFunc,
ppNext: ppNext,

View File

@@ -41,7 +41,7 @@ func (pu *pipeUnpackJSON) String() string {
s += " from " + quoteTokenIfNeeded(pu.fromField)
}
if !prefixfilter.MatchAll(pu.fieldFilters) {
s += " fields (" + fieldNamesString(pu.fieldFilters) + ")"
s += " fields (" + fieldFiltersString(pu.fieldFilters) + ")"
}
if len(pu.preserveKeys) > 0 {
s += " preserve_keys (" + fieldNamesString(pu.preserveKeys) + ")"

View File

@@ -35,7 +35,7 @@ func (pu *pipeUnpackLogfmt) String() string {
s += " from " + quoteTokenIfNeeded(pu.fromField)
}
if !prefixfilter.MatchAll(pu.fieldFilters) {
s += " fields (" + fieldNamesString(pu.fieldFilters) + ")"
s += " fields (" + fieldFiltersString(pu.fieldFilters) + ")"
}
if pu.resultPrefix != "" {
s += " result_prefix " + quoteTokenIfNeeded(pu.resultPrefix)

View File

@@ -234,7 +234,7 @@ func parsePipeUnroll(lex *lexer) (pipe, error) {
}
fields = fs
} else {
fs, err := parseCommaSeparatedFields(lex)
fs, err := parseCommaSeparatedFieldNames(lex)
if err != nil {
return nil, fmt.Errorf("cannot parse 'by ...': %w", err)
}

View File

@@ -11,7 +11,7 @@ type runningStatsCount struct {
}
func (sc *runningStatsCount) String() string {
return "count(" + fieldNamesString(sc.fieldFilters) + ")"
return "count(" + fieldFiltersString(sc.fieldFilters) + ")"
}
func (sc *runningStatsCount) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -9,7 +9,7 @@ type runningStatsMax struct {
}
func (sm *runningStatsMax) String() string {
return "max(" + fieldNamesString(sm.fieldFilters) + ")"
return "max(" + fieldFiltersString(sm.fieldFilters) + ")"
}
func (sm *runningStatsMax) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -9,7 +9,7 @@ type runningStatsMin struct {
}
func (sm *runningStatsMin) String() string {
return "min(" + fieldNamesString(sm.fieldFilters) + ")"
return "min(" + fieldFiltersString(sm.fieldFilters) + ")"
}
func (sm *runningStatsMin) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -12,7 +12,7 @@ type runningStatsSum struct {
}
func (ss *runningStatsSum) String() string {
return "sum(" + fieldNamesString(ss.fieldFilters) + ")"
return "sum(" + fieldFiltersString(ss.fieldFilters) + ")"
}
func (ss *runningStatsSum) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -15,7 +15,7 @@ type statsAvg struct {
}
func (sa *statsAvg) String() string {
return "avg(" + fieldNamesString(sa.fieldFilters) + ")"
return "avg(" + fieldFiltersString(sa.fieldFilters) + ")"
}
func (sa *statsAvg) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -16,7 +16,7 @@ type statsCount struct {
}
func (sc *statsCount) String() string {
return "count(" + fieldNamesString(sc.fieldFilters) + ")"
return "count(" + fieldFiltersString(sc.fieldFilters) + ")"
}
func (sc *statsCount) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -16,7 +16,7 @@ type statsCountEmpty struct {
}
func (sc *statsCountEmpty) String() string {
return "count_empty(" + fieldNamesString(sc.fieldFilters) + ")"
return "count_empty(" + fieldFiltersString(sc.fieldFilters) + ")"
}
func (sc *statsCountEmpty) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -28,7 +28,7 @@ type statsJSONValues struct {
}
func (sv *statsJSONValues) String() string {
s := "json_values(" + fieldNamesString(sv.fieldFilters) + ")"
s := "json_values(" + fieldFiltersString(sv.fieldFilters) + ")"
if len(sv.sortFields) > 0 {
a := make([]string, len(sv.sortFields))

View File

@@ -17,7 +17,7 @@ type statsMax struct {
}
func (sm *statsMax) String() string {
return "max(" + fieldNamesString(sm.fieldFilters) + ")"
return "max(" + fieldFiltersString(sm.fieldFilters) + ")"
}
func (sm *statsMax) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -9,7 +9,7 @@ type statsMedian struct {
}
func (sm *statsMedian) String() string {
return "median(" + fieldNamesString(sm.sq.fieldFilters) + ")"
return "median(" + fieldFiltersString(sm.sq.fieldFilters) + ")"
}
func (sm *statsMedian) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -17,7 +17,7 @@ type statsMin struct {
}
func (sm *statsMin) String() string {
return "min(" + fieldNamesString(sm.fieldFilters) + ")"
return "min(" + fieldFiltersString(sm.fieldFilters) + ")"
}
func (sm *statsMin) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -24,7 +24,7 @@ type statsQuantile struct {
func (sq *statsQuantile) String() string {
s := "quantile(" + sq.phiStr
if !prefixfilter.MatchAll(sq.fieldFilters) {
s += ", " + fieldNamesString(sq.fieldFilters)
s += ", " + fieldFiltersString(sq.fieldFilters)
}
s += ")"
return s

View File

@@ -14,7 +14,7 @@ type statsRateSum struct {
}
func (sr *statsRateSum) String() string {
return "rate_sum(" + fieldNamesString(sr.ss.fieldFilters) + ")"
return "rate_sum(" + fieldFiltersString(sr.ss.fieldFilters) + ")"
}
func (sr *statsRateSum) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -16,7 +16,7 @@ type statsRowAny struct {
}
func (sa *statsRowAny) String() string {
return "row_any(" + fieldNamesString(sa.fieldFilters) + ")"
return "row_any(" + fieldFiltersString(sa.fieldFilters) + ")"
}
func (sa *statsRowAny) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -21,7 +21,7 @@ type statsRowMax struct {
func (sm *statsRowMax) String() string {
s := "row_max(" + quoteTokenIfNeeded(sm.srcField)
if !prefixfilter.MatchAll(sm.fieldFilters) {
s += ", " + fieldNamesString(sm.fieldFilters)
s += ", " + fieldFiltersString(sm.fieldFilters)
}
s += ")"
return s

View File

@@ -21,7 +21,7 @@ type statsRowMin struct {
func (sm *statsRowMin) String() string {
s := "row_min(" + quoteTokenIfNeeded(sm.srcField)
if !prefixfilter.MatchAll(sm.fieldFilters) {
s += ", " + fieldNamesString(sm.fieldFilters)
s += ", " + fieldFiltersString(sm.fieldFilters)
}
s += ")"
return s

View File

@@ -15,7 +15,7 @@ type statsStddev struct {
}
func (ss *statsStddev) String() string {
return "stddev(" + fieldNamesString(ss.fieldFilters) + ")"
return "stddev(" + fieldFiltersString(ss.fieldFilters) + ")"
}
func (ss *statsStddev) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -15,7 +15,7 @@ type statsSum struct {
}
func (ss *statsSum) String() string {
return "sum(" + fieldNamesString(ss.fieldFilters) + ")"
return "sum(" + fieldFiltersString(ss.fieldFilters) + ")"
}
func (ss *statsSum) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -14,7 +14,7 @@ type statsSumLen struct {
}
func (ss *statsSumLen) String() string {
return "sum_len(" + fieldNamesString(ss.fieldFilters) + ")"
return "sum_len(" + fieldFiltersString(ss.fieldFilters) + ")"
}
func (ss *statsSumLen) updateNeededFields(pf *prefixfilter.Filter) {

View File

@@ -21,7 +21,7 @@ type statsUniqValues struct {
}
func (su *statsUniqValues) String() string {
s := "uniq_values(" + fieldNamesString(su.fieldFilters) + ")"
s := "uniq_values(" + fieldFiltersString(su.fieldFilters) + ")"
if su.limit > 0 {
s += fmt.Sprintf(" limit %d", su.limit)
}

View File

@@ -17,7 +17,7 @@ type statsValues struct {
}
func (sv *statsValues) String() string {
s := "values(" + fieldNamesString(sv.fieldFilters) + ")"
s := "values(" + fieldFiltersString(sv.fieldFilters) + ")"
if sv.limit > 0 {
s += fmt.Sprintf(" limit %d", sv.limit)
}

View File

@@ -1347,7 +1347,6 @@ func (s *Storage) searchParallel(workersCount int, sso *storageSearchOptions, qs
putBlockSearch(bs)
putBitmap(bm)
qs.UpdateAtomic(qsLocal)
})
}
@@ -1601,7 +1600,6 @@ func (p *part) hasMatchingRows(pso *partitionSearchOptions, stopCh <-chan struct
putBlockSearch(bs)
putBitmap(bm)
})
}

View File

@@ -44,46 +44,54 @@ func (st *StreamTags) Reset() {
st.tags = st.tags[:0]
}
// CopyFrom copies src to dst.
func (st *StreamTags) CopyFrom(src *StreamTags) {
st.Reset()
st.tags = append(st.tags[:0], src.tags...)
}
// String returns string representation of st.
func (st *StreamTags) String() string {
b := st.marshalString(nil)
return string(b)
}
func (st *StreamTags) verifyCanonicalFieldValues(fields []Field) error {
// Verify that the unmarshaled stream tags match the corresponding fields' values.
// See https://github.com/VictoriaMetrics/VictoriaLogs/issues/38
// normalize synchronizes st with the provided fields and returns true if st was updated.
//
// This function updates or keeps tags that exist in fields and removes missing ones.
func (st *StreamTags) normalize(fields []Field) bool {
updated := false
prevTagName := ""
for _, tag := range st.tags {
tags := st.tags
dstTags := tags[:0]
for _, tag := range tags {
tagName := tag.Name
if err := CheckStreamFieldName(tagName); err != nil {
return fmt.Errorf("invalid stream tag name: %s; streamTags: %s", tagName, st)
var f *Field
for j := range fields {
if fields[j].Name == tagName {
f = &fields[j]
// break is skipped intentionally in order to get the last matching field
}
}
if f == nil {
updated = true
continue
}
if tagName <= prevTagName {
return fmt.Errorf("stream tag names must be sorted; got %q after %q; streamTags: %s", tagName, prevTagName, st)
}
tagValue := tag.Value
found := false
for _, f := range fields {
if f.Name != tagName {
continue
}
if f.Value != tagValue {
line := MarshalFieldsToJSON(nil, fields)
return fmt.Errorf("unexpected value for the stream tag %q; got %q; want %q; streamTags: %s; fields: %s", tagName, f.Value, tagValue, st, line)
}
found = true
}
if !found {
line := MarshalFieldsToJSON(nil, fields)
return fmt.Errorf("cannot find value for the stream tag %q in fields; want %q; streamTags: %s; fields: %s", tagName, tagValue, st, line)
if tag.Value != f.Value {
tag.Value = f.Value
updated = true
}
dstTags = append(dstTags, tag)
}
return nil
if updated {
clear(tags[len(dstTags):])
st.tags = dstTags
}
return updated
}
func (st *StreamTags) marshalString(dst []byte) []byte {
@@ -112,7 +120,13 @@ func (st *StreamTags) unmarshalStringInplace(s string) error {
var err error
st.tags, err = parseStreamFields(st.tags[:0], s)
return err
if err != nil {
return err
}
sort.Sort(st)
return nil
}
// Add adds (name:value) tag to st.
@@ -136,7 +150,10 @@ func (st *StreamTags) Add(name, value string) {
// MarshalCanonical marshal st in a canonical way
func (st *StreamTags) MarshalCanonical(dst []byte) []byte {
sort.Sort(st)
return st.marshalCanonicalInternal(dst)
}
func (st *StreamTags) marshalCanonicalInternal(dst []byte) []byte {
tags := st.tags
dst = encoding.MarshalVarUint64(dst, uint64(len(tags)))
for i := range tags {
@@ -160,6 +177,7 @@ func (st *StreamTags) UnmarshalCanonicalInplace(src []byte) ([]byte, error) {
return srcOrig, fmt.Errorf("cannot unmarshal tags len")
}
src = src[nSize:]
for range n {
name, nSize := encoding.UnmarshalBytes(src)
if nSize <= 0 {
@@ -178,13 +196,28 @@ func (st *StreamTags) UnmarshalCanonicalInplace(src []byte) ([]byte, error) {
st.Add(sName, sValue)
}
if !sort.IsSorted(st) {
return srcOrig, fmt.Errorf("stream tags must be sorted in alphabetical order; got unsorted: %s", st)
if err := st.checkCorrectness(); err != nil {
return srcOrig, err
}
return src, nil
}
func (st *StreamTags) checkCorrectness() error {
prevTagName := ""
for _, tag := range st.tags {
tagName := tag.Name
if err := CheckStreamFieldName(tagName); err != nil {
return fmt.Errorf("invalid stream tag name: %w", err)
}
if tagName <= prevTagName {
return fmt.Errorf("stream tags must be sorted in alphabetical order; got %q which is less or equal than %q; streamTags=%s", tagName, prevTagName, st)
}
prevTagName = tagName
}
return nil
}
func getStreamTagsString(streamTagsCanonical string) string {
st := GetStreamTags()
mustUnmarshalStreamTagsInplace(st, streamTagsCanonical)

View File

@@ -655,7 +655,6 @@ func (p *SyslogParser) parseCEFExtension(s string) bool {
p.AddField(keyName, unescapeCEFValue(s[:n]))
s = s[n+1:]
}
}
func (p *SyslogParser) tryParseTimestampRFC3164(s string) bool {

2
vendor/modules.txt vendored
View File

@@ -133,7 +133,7 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric
# github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0
## explicit; go 1.25.0
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping
# github.com/VictoriaMetrics/VictoriaLogs v1.121.1-0.20260616132739-c901a1e31cb3
# github.com/VictoriaMetrics/VictoriaLogs v1.51.1-0.20260624061259-dc94972a8708
## explicit; go 1.26.4
github.com/VictoriaMetrics/VictoriaLogs/lib/logstorage
github.com/VictoriaMetrics/VictoriaLogs/lib/prefixfilter