mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-24 05:04:05 -08:00
Merge branch 'main' into alexg/settable-user-agent
Some checks failed
CI / Go tests (push) Has been cancelled
CI / More Go tests (push) Has been cancelled
CI / Go tests with previous Go version (push) Has been cancelled
CI / UI tests (push) Has been cancelled
CI / Go tests on Windows (push) Has been cancelled
CI / Mixins tests (push) Has been cancelled
CI / Build Prometheus for common architectures (0) (push) Has been cancelled
CI / Build Prometheus for common architectures (1) (push) Has been cancelled
CI / Build Prometheus for common architectures (2) (push) Has been cancelled
CI / Build Prometheus for all architectures (0) (push) Has been cancelled
CI / Build Prometheus for all architectures (1) (push) Has been cancelled
CI / Build Prometheus for all architectures (10) (push) Has been cancelled
CI / Build Prometheus for all architectures (11) (push) Has been cancelled
CI / Build Prometheus for all architectures (2) (push) Has been cancelled
CI / Build Prometheus for all architectures (3) (push) Has been cancelled
CI / Build Prometheus for all architectures (4) (push) Has been cancelled
CI / Build Prometheus for all architectures (5) (push) Has been cancelled
CI / Build Prometheus for all architectures (6) (push) Has been cancelled
CI / Build Prometheus for all architectures (7) (push) Has been cancelled
CI / Build Prometheus for all architectures (8) (push) Has been cancelled
CI / Build Prometheus for all architectures (9) (push) Has been cancelled
CI / Check generated parser (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
CI / fuzzing (push) Has been cancelled
CI / codeql (push) Has been cancelled
CI / Report status of build Prometheus for all architectures (push) Has been cancelled
CI / Publish main branch artifacts (push) Has been cancelled
CI / Publish release artefacts (push) Has been cancelled
CI / Publish UI on npm Registry (push) Has been cancelled
Some checks failed
CI / Go tests (push) Has been cancelled
CI / More Go tests (push) Has been cancelled
CI / Go tests with previous Go version (push) Has been cancelled
CI / UI tests (push) Has been cancelled
CI / Go tests on Windows (push) Has been cancelled
CI / Mixins tests (push) Has been cancelled
CI / Build Prometheus for common architectures (0) (push) Has been cancelled
CI / Build Prometheus for common architectures (1) (push) Has been cancelled
CI / Build Prometheus for common architectures (2) (push) Has been cancelled
CI / Build Prometheus for all architectures (0) (push) Has been cancelled
CI / Build Prometheus for all architectures (1) (push) Has been cancelled
CI / Build Prometheus for all architectures (10) (push) Has been cancelled
CI / Build Prometheus for all architectures (11) (push) Has been cancelled
CI / Build Prometheus for all architectures (2) (push) Has been cancelled
CI / Build Prometheus for all architectures (3) (push) Has been cancelled
CI / Build Prometheus for all architectures (4) (push) Has been cancelled
CI / Build Prometheus for all architectures (5) (push) Has been cancelled
CI / Build Prometheus for all architectures (6) (push) Has been cancelled
CI / Build Prometheus for all architectures (7) (push) Has been cancelled
CI / Build Prometheus for all architectures (8) (push) Has been cancelled
CI / Build Prometheus for all architectures (9) (push) Has been cancelled
CI / Check generated parser (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
CI / fuzzing (push) Has been cancelled
CI / codeql (push) Has been cancelled
CI / Report status of build Prometheus for all architectures (push) Has been cancelled
CI / Publish main branch artifacts (push) Has been cancelled
CI / Publish release artefacts (push) Has been cancelled
CI / Publish UI on npm Registry (push) Has been cancelled
Signed-off-by: Alex Greenbank <alex.greenbank@grafana.com>
This commit is contained in:
commit
78930e4630
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
|
@ -16,8 +16,23 @@ updates:
|
|||
directory: "/documentation/examples/remote_storage"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
# New manteen-ui packages.
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/web/ui"
|
||||
labels:
|
||||
- dependencies
|
||||
- javascript
|
||||
- manteen-ui
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 20
|
||||
# Old react-app packages.
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/web/ui/react-app"
|
||||
labels:
|
||||
- dependencies
|
||||
- javascript
|
||||
- old-react-ui
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 20
|
||||
|
|
2
.github/workflows/buf-lint.yml
vendored
2
.github/workflows/buf-lint.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: bufbuild/buf-setup-action@62ee92603c244ad0da98bab36a834a999a5329e6 # v1.43.0
|
||||
- uses: bufbuild/buf-setup-action@9672cee01808979ea1249f81d6d321217b9a10f6 # v1.47.2
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: bufbuild/buf-lint-action@06f9dd823d873146471cfaaf108a993fe00e5325 # v1.1.1
|
||||
|
|
2
.github/workflows/buf.yml
vendored
2
.github/workflows/buf.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
if: github.repository_owner == 'prometheus'
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: bufbuild/buf-setup-action@62ee92603c244ad0da98bab36a834a999a5329e6 # v1.43.0
|
||||
- uses: bufbuild/buf-setup-action@9672cee01808979ea1249f81d6d321217b9a10f6 # v1.47.2
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: bufbuild/buf-lint-action@06f9dd823d873146471cfaaf108a993fe00e5325 # v1.1.1
|
||||
|
|
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
|
@ -80,7 +80,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
|
||||
with:
|
||||
go-version: 1.23.x
|
||||
- run: |
|
||||
|
@ -171,7 +171,7 @@ jobs:
|
|||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
|
||||
with:
|
||||
cache: false
|
||||
go-version: 1.23.x
|
||||
|
@ -184,7 +184,7 @@ jobs:
|
|||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
|
||||
with:
|
||||
go-version: 1.23.x
|
||||
- name: Install snmp_exporter/generator dependencies
|
||||
|
@ -195,7 +195,7 @@ jobs:
|
|||
with:
|
||||
args: --verbose
|
||||
# Make sure to sync this with Makefile.common and scripts/golangci-lint.yml.
|
||||
version: v1.61.0
|
||||
version: v1.62.0
|
||||
fuzzing:
|
||||
uses: ./.github/workflows/fuzzing.yml
|
||||
if: github.event_name == 'pull_request'
|
||||
|
@ -247,7 +247,7 @@ jobs:
|
|||
with:
|
||||
node-version-file: "web/ui/.nvmrc"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
- uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
|
|
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
|
@ -27,12 +27,12 @@ jobs:
|
|||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
|
||||
uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
|
||||
uses: github/codeql-action/autobuild@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
|
||||
uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
|
||||
|
|
2
.github/workflows/scorecards.yml
vendored
2
.github/workflows/scorecards.yml
vendored
|
@ -45,6 +45,6 @@ jobs:
|
|||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # tag=v3.26.10
|
||||
uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # tag=v3.27.5
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
## unreleased
|
||||
|
||||
* [CHANGE] Notifier: Increment the prometheus_notifications_errors_total metric by the number of affected alerts rather than by one per batch of affected alerts. #15428
|
||||
* [ENHANCEMENT] Remote-Write: Ability to set User-Agent #15201
|
||||
* [ENHANCEMENT] OTLP receiver: Convert also metric metadata. #15416
|
||||
|
||||
|
@ -116,7 +117,7 @@ request accessed a block on disk at about the same time as TSDB created a new bl
|
|||
|
||||
## 2.54.1 / 2024-08-27
|
||||
|
||||
* [BUGFIX] Scraping: allow multiple samples on same series, with explicit timestamps. #14685
|
||||
* [BUGFIX] Scraping: allow multiple samples on same series, with explicit timestamps (mixing samples of the same series with and without timestamps is still rejected). #14685
|
||||
* [BUGFIX] Docker SD: fix crash in `match_first_network` mode when container is reconnected to a new network. #14654
|
||||
* [BUGFIX] PromQL: fix experimental native histograms getting corrupted due to vector selector bug in range queries. #14538
|
||||
* [BUGFIX] PromQL: fix experimental native histogram counter reset detection on stale samples. #14514
|
||||
|
@ -198,6 +199,7 @@ This release changes the default for GOGC, the Go runtime control for the trade-
|
|||
## 2.52.0 / 2024-05-07
|
||||
|
||||
* [CHANGE] TSDB: Fix the predicate checking for blocks which are beyond the retention period to include the ones right at the retention boundary. #9633
|
||||
* [CHANGE] Scrape: Multiple samples (even with different timestamps) are treated as duplicates during one scrape.
|
||||
* [FEATURE] Kubernetes SD: Add a new metric `prometheus_sd_kubernetes_failures_total` to track failed requests to Kubernetes API. #13554
|
||||
* [FEATURE] Kubernetes SD: Add node and zone metadata labels when using the endpointslice role. #13935
|
||||
* [FEATURE] Azure SD/Remote Write: Allow usage of Azure authorization SDK. #13099
|
||||
|
|
|
@ -61,7 +61,7 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_
|
|||
SKIP_GOLANGCI_LINT :=
|
||||
GOLANGCI_LINT :=
|
||||
GOLANGCI_LINT_OPTS ?=
|
||||
GOLANGCI_LINT_VERSION ?= v1.61.0
|
||||
GOLANGCI_LINT_VERSION ?= v1.62.0
|
||||
# golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64.
|
||||
# windows isn't included here because of the path separator being different.
|
||||
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))
|
||||
|
|
64
RELEASE.md
64
RELEASE.md
|
@ -5,61 +5,15 @@ This page describes the release process and the currently planned schedule for u
|
|||
## Release schedule
|
||||
|
||||
Release cadence of first pre-releases being cut is 6 weeks.
|
||||
Please see [the v2.55 RELEASE.md](https://github.com/prometheus/prometheus/blob/release-2.55/RELEASE.md) for the v2 release series schedule.
|
||||
|
||||
| release series | date of first pre-release (year-month-day) | release shepherd |
|
||||
|----------------|--------------------------------------------|---------------------------------------------|
|
||||
| v2.4 | 2018-09-06 | Goutham Veeramachaneni (GitHub: @gouthamve) |
|
||||
| v2.5 | 2018-10-24 | Frederic Branczyk (GitHub: @brancz) |
|
||||
| v2.6 | 2018-12-05 | Simon Pasquier (GitHub: @simonpasquier) |
|
||||
| v2.7 | 2019-01-16 | Goutham Veeramachaneni (GitHub: @gouthamve) |
|
||||
| v2.8 | 2019-02-27 | Ganesh Vernekar (GitHub: @codesome) |
|
||||
| v2.9 | 2019-04-10 | Brian Brazil (GitHub: @brian-brazil) |
|
||||
| v2.10 | 2019-05-22 | Björn Rabenstein (GitHub: @beorn7) |
|
||||
| v2.11 | 2019-07-03 | Frederic Branczyk (GitHub: @brancz) |
|
||||
| v2.12 | 2019-08-14 | Julius Volz (GitHub: @juliusv) |
|
||||
| v2.13 | 2019-09-25 | Krasi Georgiev (GitHub: @krasi-georgiev) |
|
||||
| v2.14 | 2019-11-06 | Chris Marchbanks (GitHub: @csmarchbanks) |
|
||||
| v2.15 | 2019-12-18 | Bartek Plotka (GitHub: @bwplotka) |
|
||||
| v2.16 | 2020-01-29 | Callum Styan (GitHub: @cstyan) |
|
||||
| v2.17 | 2020-03-11 | Julien Pivotto (GitHub: @roidelapluie) |
|
||||
| v2.18 | 2020-04-22 | Bartek Plotka (GitHub: @bwplotka) |
|
||||
| v2.19 | 2020-06-03 | Ganesh Vernekar (GitHub: @codesome) |
|
||||
| v2.20 | 2020-07-15 | Björn Rabenstein (GitHub: @beorn7) |
|
||||
| v2.21 | 2020-08-26 | Julien Pivotto (GitHub: @roidelapluie) |
|
||||
| v2.22 | 2020-10-07 | Frederic Branczyk (GitHub: @brancz) |
|
||||
| v2.23 | 2020-11-18 | Ganesh Vernekar (GitHub: @codesome) |
|
||||
| v2.24 | 2020-12-30 | Björn Rabenstein (GitHub: @beorn7) |
|
||||
| v2.25 | 2021-02-10 | Julien Pivotto (GitHub: @roidelapluie) |
|
||||
| v2.26 | 2021-03-24 | Bartek Plotka (GitHub: @bwplotka) |
|
||||
| v2.27 | 2021-05-05 | Chris Marchbanks (GitHub: @csmarchbanks) |
|
||||
| v2.28 | 2021-06-16 | Julius Volz (GitHub: @juliusv) |
|
||||
| v2.29 | 2021-07-28 | Frederic Branczyk (GitHub: @brancz) |
|
||||
| v2.30 | 2021-09-08 | Ganesh Vernekar (GitHub: @codesome) |
|
||||
| v2.31 | 2021-10-20 | Julien Pivotto (GitHub: @roidelapluie) |
|
||||
| v2.32 | 2021-12-01 | Julius Volz (GitHub: @juliusv) |
|
||||
| v2.33 | 2022-01-12 | Björn Rabenstein (GitHub: @beorn7) |
|
||||
| v2.34 | 2022-02-23 | Chris Marchbanks (GitHub: @csmarchbanks) |
|
||||
| v2.35 | 2022-04-06 | Augustin Husson (GitHub: @nexucis) |
|
||||
| v2.36 | 2022-05-18 | Matthias Loibl (GitHub: @metalmatze) |
|
||||
| v2.37 LTS | 2022-06-29 | Julien Pivotto (GitHub: @roidelapluie) |
|
||||
| v2.38 | 2022-08-10 | Julius Volz (GitHub: @juliusv) |
|
||||
| v2.39 | 2022-09-21 | Ganesh Vernekar (GitHub: @codesome) |
|
||||
| v2.40 | 2022-11-02 | Ganesh Vernekar (GitHub: @codesome) |
|
||||
| v2.41 | 2022-12-14 | Julien Pivotto (GitHub: @roidelapluie) |
|
||||
| v2.42 | 2023-01-25 | Kemal Akkoyun (GitHub: @kakkoyun) |
|
||||
| v2.43 | 2023-03-08 | Julien Pivotto (GitHub: @roidelapluie) |
|
||||
| v2.44 | 2023-04-19 | Bryan Boreham (GitHub: @bboreham) |
|
||||
| v2.45 LTS | 2023-05-31 | Jesus Vazquez (Github: @jesusvazquez) |
|
||||
| v2.46 | 2023-07-12 | Julien Pivotto (GitHub: @roidelapluie) |
|
||||
| v2.47 | 2023-08-23 | Bryan Boreham (GitHub: @bboreham) |
|
||||
| v2.48 | 2023-10-04 | Levi Harrison (GitHub: @LeviHarrison) |
|
||||
| v2.49 | 2023-12-05 | Bartek Plotka (GitHub: @bwplotka) |
|
||||
| v2.50 | 2024-01-16 | Augustin Husson (GitHub: @nexucis) |
|
||||
| v2.51 | 2024-03-07 | Bryan Boreham (GitHub: @bboreham) |
|
||||
| v2.52 | 2024-04-22 | Arthur Silva Sens (GitHub: @ArthurSens) |
|
||||
| v2.53 LTS | 2024-06-03 | George Krajcsovits (GitHub: @krajorama) |
|
||||
| v2.54 | 2024-07-17 | Bryan Boreham (GitHub: @bboreham) |
|
||||
| v2.55 | 2024-09-17 | Bryan Boreham (GitHub: @bboreham) |
|
||||
| release series | date of first pre-release (year-month-day) | release shepherd |
|
||||
|----------------|--------------------------------------------|-----------------------------------|
|
||||
| v3.0 | 2024-11-14 | Jan Fajerski (GitHub: @jan--f) |
|
||||
| v3.1 | 2024-12-17 | Bryan Boreham (GitHub: @bboreham) |
|
||||
| v3.2 | 2025-01-28 | Jan Fajerski (GitHub: @jan--f) |
|
||||
| v3.3 | 2025-03-11 | Ayoub Mrini (Github: @machine424) |
|
||||
| v3.4 | 2025-04-22 | **volunteer welcome** |
|
||||
|
||||
If you are interested in volunteering please create a pull request against the [prometheus/prometheus](https://github.com/prometheus/prometheus) repository and propose yourself for the release series of your choice.
|
||||
|
||||
|
@ -204,7 +158,7 @@ Then release with `git tag-release`.
|
|||
|
||||
Signing a tag with a GPG key is appreciated, but in case you can't add a GPG key to your Github account using the following [procedure](https://help.github.com/articles/generating-a-gpg-key/), you can replace the `-s` flag by `-a` flag of the `git tag` command to only annotate the tag without signing.
|
||||
|
||||
Once a tag is created, the release process through CircleCI will be triggered for this tag and Circle CI will draft the GitHub release using the `prombot` account.
|
||||
Once a tag is created, the release process through Github Actions will be triggered for this tag and Github Actions will draft the GitHub release using the `prombot` account.
|
||||
|
||||
Finally, wait for the build step for the tag to finish. The point here is to wait for tarballs to be uploaded to the Github release and the container images to be pushed to the Docker Hub and Quay.io. Once that has happened, click _Publish release_, which will make the release publicly visible and create a GitHub notification.
|
||||
**Note:** for a release candidate version ensure the _This is a pre-release_ box is checked when drafting the release in the Github UI. The CI job should take care of this but it's a good idea to double check before clicking _Publish release_.`
|
||||
|
|
|
@ -593,12 +593,14 @@ func main() {
|
|||
logger.Error(fmt.Sprintf("Error loading config (--config.file=%s)", cfg.configFile), "file", absPath, "err", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
// Get scrape configs to validate dynamically loaded scrape_config_files.
|
||||
// They can change over time, but do the extra validation on startup for better experience.
|
||||
if _, err := cfgFile.GetScrapeConfigs(); err != nil {
|
||||
absPath, pathErr := filepath.Abs(cfg.configFile)
|
||||
if pathErr != nil {
|
||||
absPath = cfg.configFile
|
||||
}
|
||||
logger.Error(fmt.Sprintf("Error loading scrape config files from config (--config.file=%q)", cfg.configFile), "file", absPath, "err", err)
|
||||
logger.Error(fmt.Sprintf("Error loading dynamic scrape config files from config (--config.file=%q)", cfg.configFile), "file", absPath, "err", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
if cfg.tsdb.EnableExemplarStorage {
|
||||
|
@ -1369,10 +1371,12 @@ func main() {
|
|||
},
|
||||
)
|
||||
}
|
||||
if err := g.Run(); err != nil {
|
||||
logger.Error("Error running goroutines from run.Group", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
func() { // This function exists so the top of the stack is named 'main.main.funcxxx' and not 'oklog'.
|
||||
if err := g.Run(); err != nil {
|
||||
logger.Error("Fatal error", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
logger.Info("See you next time!")
|
||||
}
|
||||
|
||||
|
|
|
@ -589,7 +589,10 @@ func analyzeBlock(ctx context.Context, path, blockID string, limit int, runExten
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
postings = index.Intersect(postings, index.NewListPostings(refs))
|
||||
// Only intersect postings if matchers are specified.
|
||||
if len(matchers) > 0 {
|
||||
postings = index.Intersect(postings, index.NewListPostings(refs))
|
||||
}
|
||||
count := 0
|
||||
for postings.Next() {
|
||||
count++
|
||||
|
|
|
@ -117,11 +117,12 @@ func Load(s string, logger *slog.Logger) (*Config, error) {
|
|||
default:
|
||||
return nil, fmt.Errorf("unsupported OTLP translation strategy %q", cfg.OTLPConfig.TranslationStrategy)
|
||||
}
|
||||
|
||||
cfg.loaded = true
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// LoadFile parses the given YAML file into a Config.
|
||||
// LoadFile parses and validates the given YAML file into a read-only Config.
|
||||
// Callers should never write to or shallow copy the returned Config.
|
||||
func LoadFile(filename string, agentMode bool, logger *slog.Logger) (*Config, error) {
|
||||
content, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
|
@ -270,9 +271,12 @@ type Config struct {
|
|||
RemoteWriteConfigs []*RemoteWriteConfig `yaml:"remote_write,omitempty"`
|
||||
RemoteReadConfigs []*RemoteReadConfig `yaml:"remote_read,omitempty"`
|
||||
OTLPConfig OTLPConfig `yaml:"otlp,omitempty"`
|
||||
|
||||
loaded bool // Certain methods require configuration to use Load validation.
|
||||
}
|
||||
|
||||
// SetDirectory joins any relative file paths with dir.
|
||||
// This method writes to config, and it's not concurrency safe.
|
||||
func (c *Config) SetDirectory(dir string) {
|
||||
c.GlobalConfig.SetDirectory(dir)
|
||||
c.AlertingConfig.SetDirectory(dir)
|
||||
|
@ -302,24 +306,26 @@ func (c Config) String() string {
|
|||
return string(b)
|
||||
}
|
||||
|
||||
// GetScrapeConfigs returns the scrape configurations.
|
||||
// GetScrapeConfigs returns the read-only, validated scrape configurations including
|
||||
// the ones from the scrape_config_files.
|
||||
// This method does not write to config, and it's concurrency safe (the pointer receiver is for efficiency).
|
||||
// This method also assumes the Config was created by Load or LoadFile function, it returns error
|
||||
// if it was not. We can't re-validate or apply globals here due to races,
|
||||
// read more https://github.com/prometheus/prometheus/issues/15538.
|
||||
func (c *Config) GetScrapeConfigs() ([]*ScrapeConfig, error) {
|
||||
scfgs := make([]*ScrapeConfig, len(c.ScrapeConfigs))
|
||||
if !c.loaded {
|
||||
// Programmatic error, we warn before more confusing errors would happen due to lack of the globalization.
|
||||
return nil, errors.New("scrape config cannot be fetched, main config was not validated and loaded correctly; should not happen")
|
||||
}
|
||||
|
||||
scfgs := make([]*ScrapeConfig, len(c.ScrapeConfigs))
|
||||
jobNames := map[string]string{}
|
||||
for i, scfg := range c.ScrapeConfigs {
|
||||
// We do these checks for library users that would not call validate in
|
||||
// Unmarshal.
|
||||
if err := scfg.Validate(c.GlobalConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, ok := jobNames[scfg.JobName]; ok {
|
||||
return nil, fmt.Errorf("found multiple scrape configs with job name %q", scfg.JobName)
|
||||
}
|
||||
jobNames[scfg.JobName] = "main config file"
|
||||
scfgs[i] = scfg
|
||||
}
|
||||
|
||||
// Re-read and validate the dynamic scrape config rules.
|
||||
for _, pat := range c.ScrapeConfigFiles {
|
||||
fs, err := filepath.Glob(pat)
|
||||
if err != nil {
|
||||
|
@ -355,6 +361,7 @@ func (c *Config) GetScrapeConfigs() ([]*ScrapeConfig, error) {
|
|||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
// NOTE: This method should not be used outside of this package. Use Load or LoadFile instead.
|
||||
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*c = DefaultConfig
|
||||
// We want to set c to the defaults and then overwrite it with the input.
|
||||
|
@ -391,18 +398,18 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Do global overrides and validate unique names.
|
||||
// Do global overrides and validation.
|
||||
jobNames := map[string]struct{}{}
|
||||
for _, scfg := range c.ScrapeConfigs {
|
||||
if err := scfg.Validate(c.GlobalConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := jobNames[scfg.JobName]; ok {
|
||||
return fmt.Errorf("found multiple scrape configs with job name %q", scfg.JobName)
|
||||
}
|
||||
jobNames[scfg.JobName] = struct{}{}
|
||||
}
|
||||
|
||||
rwNames := map[string]struct{}{}
|
||||
for _, rwcfg := range c.RemoteWriteConfigs {
|
||||
if rwcfg == nil {
|
||||
|
@ -1429,8 +1436,9 @@ var (
|
|||
|
||||
// OTLPConfig is the configuration for writing to the OTLP endpoint.
|
||||
type OTLPConfig struct {
|
||||
PromoteResourceAttributes []string `yaml:"promote_resource_attributes,omitempty"`
|
||||
TranslationStrategy translationStrategyOption `yaml:"translation_strategy,omitempty"`
|
||||
PromoteResourceAttributes []string `yaml:"promote_resource_attributes,omitempty"`
|
||||
TranslationStrategy translationStrategyOption `yaml:"translation_strategy,omitempty"`
|
||||
KeepIdentifyingResourceAttributes bool `yaml:"keep_identifying_resource_attributes,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
|
|
|
@ -18,6 +18,8 @@ package config
|
|||
const ruleFilesConfigFile = "testdata/rules_abs_path.good.yml"
|
||||
|
||||
var ruleFilesExpectedConf = &Config{
|
||||
loaded: true,
|
||||
|
||||
GlobalConfig: DefaultGlobalConfig,
|
||||
Runtime: DefaultRuntimeConfig,
|
||||
RuleFiles: []string{
|
||||
|
|
|
@ -87,6 +87,7 @@ const (
|
|||
)
|
||||
|
||||
var expectedConf = &Config{
|
||||
loaded: true,
|
||||
GlobalConfig: GlobalConfig{
|
||||
ScrapeInterval: model.Duration(15 * time.Second),
|
||||
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
|
||||
|
@ -1512,10 +1513,10 @@ func TestYAMLRoundtrip(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
out, err := yaml.Marshal(want)
|
||||
|
||||
require.NoError(t, err)
|
||||
got := &Config{}
|
||||
require.NoError(t, yaml.UnmarshalStrict(out, got))
|
||||
|
||||
got, err := Load(string(out), promslog.NewNopLogger())
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, want, got)
|
||||
}
|
||||
|
@ -1525,10 +1526,10 @@ func TestRemoteWriteRetryOnRateLimit(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
out, err := yaml.Marshal(want)
|
||||
|
||||
require.NoError(t, err)
|
||||
got := &Config{}
|
||||
require.NoError(t, yaml.UnmarshalStrict(out, got))
|
||||
|
||||
got, err := Load(string(out), promslog.NewNopLogger())
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, got.RemoteWriteConfigs[0].QueueConfig.RetryOnRateLimit)
|
||||
require.False(t, got.RemoteWriteConfigs[1].QueueConfig.RetryOnRateLimit)
|
||||
|
@ -1554,6 +1555,20 @@ func TestOTLPSanitizeResourceAttributes(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestOTLPAllowServiceNameInTargetInfo(t *testing.T) {
|
||||
t.Run("good config", func(t *testing.T) {
|
||||
want, err := LoadFile(filepath.Join("testdata", "otlp_allow_keep_identifying_resource_attributes.good.yml"), false, promslog.NewNopLogger())
|
||||
require.NoError(t, err)
|
||||
|
||||
out, err := yaml.Marshal(want)
|
||||
require.NoError(t, err)
|
||||
var got Config
|
||||
require.NoError(t, yaml.UnmarshalStrict(out, &got))
|
||||
|
||||
require.True(t, got.OTLPConfig.KeepIdentifyingResourceAttributes)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOTLPAllowUTF8(t *testing.T) {
|
||||
t.Run("good config", func(t *testing.T) {
|
||||
fpath := filepath.Join("testdata", "otlp_allow_utf8.good.yml")
|
||||
|
@ -2205,6 +2220,7 @@ func TestEmptyConfig(t *testing.T) {
|
|||
c, err := Load("", promslog.NewNopLogger())
|
||||
require.NoError(t, err)
|
||||
exp := DefaultConfig
|
||||
exp.loaded = true
|
||||
require.Equal(t, exp, *c)
|
||||
}
|
||||
|
||||
|
@ -2254,6 +2270,7 @@ func TestEmptyGlobalBlock(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
exp := DefaultConfig
|
||||
exp.Runtime = DefaultRuntimeConfig
|
||||
exp.loaded = true
|
||||
require.Equal(t, exp, *c)
|
||||
}
|
||||
|
||||
|
@ -2534,3 +2551,18 @@ func TestScrapeProtocolHeader(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Regression test against https://github.com/prometheus/prometheus/issues/15538
|
||||
func TestGetScrapeConfigs_Loaded(t *testing.T) {
|
||||
t.Run("without load", func(t *testing.T) {
|
||||
c := &Config{}
|
||||
_, err := c.GetScrapeConfigs()
|
||||
require.EqualError(t, err, "scrape config cannot be fetched, main config was not validated and loaded correctly; should not happen")
|
||||
})
|
||||
t.Run("with load", func(t *testing.T) {
|
||||
c, err := Load("", promslog.NewNopLogger())
|
||||
require.NoError(t, err)
|
||||
_, err = c.GetScrapeConfigs()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ package config
|
|||
const ruleFilesConfigFile = "testdata/rules_abs_path_windows.good.yml"
|
||||
|
||||
var ruleFilesExpectedConf = &Config{
|
||||
loaded: true,
|
||||
|
||||
GlobalConfig: DefaultGlobalConfig,
|
||||
Runtime: DefaultRuntimeConfig,
|
||||
RuleFiles: []string{
|
||||
|
|
2
config/testdata/otlp_allow_keep_identifying_resource_attributes.good.yml
vendored
Normal file
2
config/testdata/otlp_allow_keep_identifying_resource_attributes.good.yml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
otlp:
|
||||
keep_identifying_resource_attributes: true
|
|
@ -241,7 +241,7 @@ func (d *Discovery) shouldWatch(name string, tags []string) bool {
|
|||
return d.shouldWatchFromName(name) && d.shouldWatchFromTags(tags)
|
||||
}
|
||||
|
||||
// shouldWatch returns whether the service of the given name should be watched based on its name.
|
||||
// shouldWatchFromName returns whether the service of the given name should be watched based on its name.
|
||||
func (d *Discovery) shouldWatchFromName(name string) bool {
|
||||
// If there's no fixed set of watched services, we watch everything.
|
||||
if len(d.watchedServices) == 0 {
|
||||
|
@ -256,7 +256,7 @@ func (d *Discovery) shouldWatchFromName(name string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// shouldWatch returns whether the service of the given name should be watched based on its tags.
|
||||
// shouldWatchFromTags returns whether the service of the given name should be watched based on its tags.
|
||||
// This gets called when the user doesn't specify a list of services in order to avoid watching
|
||||
// *all* services. Details in https://github.com/prometheus/prometheus/pull/3814
|
||||
func (d *Discovery) shouldWatchFromTags(tags []string) bool {
|
||||
|
|
|
@ -97,6 +97,7 @@ func makeEndpoints() *v1.Endpoints {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoveryBeforeRun(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleEndpoint, NamespaceDiscovery{})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -151,6 +152,7 @@ func TestEndpointsDiscoveryBeforeRun(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoveryAdd(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "testpod",
|
||||
|
@ -276,6 +278,7 @@ func TestEndpointsDiscoveryAdd(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoveryDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleEndpoint, NamespaceDiscovery{}, makeEndpoints())
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -294,6 +297,7 @@ func TestEndpointsDiscoveryDelete(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoveryUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleEndpoint, NamespaceDiscovery{}, makeEndpoints())
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -365,6 +369,7 @@ func TestEndpointsDiscoveryUpdate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoveryEmptySubsets(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleEndpoint, NamespaceDiscovery{}, makeEndpoints())
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -393,6 +398,7 @@ func TestEndpointsDiscoveryEmptySubsets(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoveryWithService(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleEndpoint, NamespaceDiscovery{}, makeEndpoints())
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -458,6 +464,7 @@ func TestEndpointsDiscoveryWithService(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoveryWithServiceUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleEndpoint, NamespaceDiscovery{}, makeEndpoints())
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -538,6 +545,7 @@ func TestEndpointsDiscoveryWithServiceUpdate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoveryWithNodeMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
metadataConfig := AttachMetadataConfig{Node: true}
|
||||
nodeLabels1 := map[string]string{"az": "us-east1"}
|
||||
nodeLabels2 := map[string]string{"az": "us-west2"}
|
||||
|
@ -611,6 +619,7 @@ func TestEndpointsDiscoveryWithNodeMetadata(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoveryWithUpdatedNodeMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
nodeLabels1 := map[string]string{"az": "us-east1"}
|
||||
nodeLabels2 := map[string]string{"az": "us-west2"}
|
||||
node1 := makeNode("foobar", "", "", nodeLabels1, nil)
|
||||
|
@ -688,6 +697,7 @@ func TestEndpointsDiscoveryWithUpdatedNodeMetadata(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoveryNamespaces(t *testing.T) {
|
||||
t.Parallel()
|
||||
epOne := makeEndpoints()
|
||||
epOne.Namespace = "ns1"
|
||||
objs := []runtime.Object{
|
||||
|
@ -839,6 +849,7 @@ func TestEndpointsDiscoveryNamespaces(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoveryOwnNamespace(t *testing.T) {
|
||||
t.Parallel()
|
||||
epOne := makeEndpoints()
|
||||
epOne.Namespace = "own-ns"
|
||||
|
||||
|
@ -933,6 +944,7 @@ func TestEndpointsDiscoveryOwnNamespace(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoveryEmptyPodStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
ep := makeEndpoints()
|
||||
ep.Namespace = "ns"
|
||||
|
||||
|
@ -978,6 +990,7 @@ func TestEndpointsDiscoveryEmptyPodStatus(t *testing.T) {
|
|||
// TestEndpointsDiscoveryUpdatePod makes sure that Endpoints discovery detects underlying Pods changes.
|
||||
// See https://github.com/prometheus/prometheus/issues/11305 for more details.
|
||||
func TestEndpointsDiscoveryUpdatePod(t *testing.T) {
|
||||
t.Parallel()
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "testpod",
|
||||
|
@ -1097,6 +1110,7 @@ func TestEndpointsDiscoveryUpdatePod(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsDiscoverySidecarContainer(t *testing.T) {
|
||||
t.Parallel()
|
||||
objs := []runtime.Object{
|
||||
&v1.Endpoints{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
)
|
||||
|
||||
func Test_EndpointSliceAdaptor_v1(t *testing.T) {
|
||||
t.Parallel()
|
||||
endpointSlice := makeEndpointSliceV1()
|
||||
adaptor := newEndpointSliceAdaptorFromV1(endpointSlice)
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ func makeEndpointSliceV1() *v1.EndpointSlice {
|
|||
}
|
||||
|
||||
func TestEndpointSliceDiscoveryBeforeRun(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleEndpointSlice, NamespaceDiscovery{Names: []string{"default"}})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -195,6 +196,7 @@ func TestEndpointSliceDiscoveryBeforeRun(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointSliceDiscoveryAdd(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "testpod",
|
||||
|
@ -322,6 +324,7 @@ func TestEndpointSliceDiscoveryAdd(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointSliceDiscoveryDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleEndpointSlice, NamespaceDiscovery{Names: []string{"default"}}, makeEndpointSliceV1())
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -340,6 +343,7 @@ func TestEndpointSliceDiscoveryDelete(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointSliceDiscoveryUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleEndpointSlice, NamespaceDiscovery{Names: []string{"default"}}, makeEndpointSliceV1())
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -396,6 +400,7 @@ func TestEndpointSliceDiscoveryUpdate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointSliceDiscoveryEmptyEndpoints(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleEndpointSlice, NamespaceDiscovery{Names: []string{"default"}}, makeEndpointSliceV1())
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -424,6 +429,7 @@ func TestEndpointSliceDiscoveryEmptyEndpoints(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointSliceDiscoveryWithService(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleEndpointSlice, NamespaceDiscovery{Names: []string{"default"}}, makeEndpointSliceV1())
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -516,6 +522,7 @@ func TestEndpointSliceDiscoveryWithService(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointSliceDiscoveryWithServiceUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleEndpointSlice, NamespaceDiscovery{Names: []string{"default"}}, makeEndpointSliceV1())
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -623,6 +630,7 @@ func TestEndpointSliceDiscoveryWithServiceUpdate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsSlicesDiscoveryWithNodeMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
metadataConfig := AttachMetadataConfig{Node: true}
|
||||
nodeLabels1 := map[string]string{"az": "us-east1"}
|
||||
nodeLabels2 := map[string]string{"az": "us-west2"}
|
||||
|
@ -722,6 +730,7 @@ func TestEndpointsSlicesDiscoveryWithNodeMetadata(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointsSlicesDiscoveryWithUpdatedNodeMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
metadataConfig := AttachMetadataConfig{Node: true}
|
||||
nodeLabels1 := map[string]string{"az": "us-east1"}
|
||||
nodeLabels2 := map[string]string{"az": "us-west2"}
|
||||
|
@ -827,6 +836,7 @@ func TestEndpointsSlicesDiscoveryWithUpdatedNodeMetadata(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointSliceDiscoveryNamespaces(t *testing.T) {
|
||||
t.Parallel()
|
||||
epOne := makeEndpointSliceV1()
|
||||
epOne.Namespace = "ns1"
|
||||
objs := []runtime.Object{
|
||||
|
@ -1003,6 +1013,7 @@ func TestEndpointSliceDiscoveryNamespaces(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointSliceDiscoveryOwnNamespace(t *testing.T) {
|
||||
t.Parallel()
|
||||
epOne := makeEndpointSliceV1()
|
||||
epOne.Namespace = "own-ns"
|
||||
|
||||
|
@ -1123,6 +1134,7 @@ func TestEndpointSliceDiscoveryOwnNamespace(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointSliceDiscoveryEmptyPodStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
ep := makeEndpointSliceV1()
|
||||
ep.Namespace = "ns"
|
||||
|
||||
|
@ -1169,6 +1181,7 @@ func TestEndpointSliceDiscoveryEmptyPodStatus(t *testing.T) {
|
|||
// sets up indexing for the main Kube informer only when needed.
|
||||
// See: https://github.com/prometheus/prometheus/pull/13554#discussion_r1490965817
|
||||
func TestEndpointSliceInfIndexersCount(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
withNodeMetadata bool
|
||||
|
@ -1179,6 +1192,7 @@ func TestEndpointSliceInfIndexersCount(t *testing.T) {
|
|||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var (
|
||||
n *Discovery
|
||||
mainInfIndexersCount int
|
||||
|
@ -1204,6 +1218,7 @@ func TestEndpointSliceInfIndexersCount(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEndpointSliceDiscoverySidecarContainer(t *testing.T) {
|
||||
t.Parallel()
|
||||
objs := []runtime.Object{
|
||||
&v1.EndpointSlice{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
|
|
@ -144,6 +144,7 @@ func expectedTargetGroups(ns string, tls TLSMode) map[string]*targetgroup.Group
|
|||
}
|
||||
|
||||
func TestIngressDiscoveryAdd(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleIngress, NamespaceDiscovery{Names: []string{"default"}})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -158,6 +159,7 @@ func TestIngressDiscoveryAdd(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIngressDiscoveryAddTLS(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleIngress, NamespaceDiscovery{Names: []string{"default"}})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -172,6 +174,7 @@ func TestIngressDiscoveryAddTLS(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIngressDiscoveryAddMixed(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleIngress, NamespaceDiscovery{Names: []string{"default"}})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -186,6 +189,7 @@ func TestIngressDiscoveryAddMixed(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIngressDiscoveryNamespaces(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleIngress, NamespaceDiscovery{Names: []string{"ns1", "ns2"}})
|
||||
|
||||
expected := expectedTargetGroups("ns1", TLSNo)
|
||||
|
@ -207,6 +211,7 @@ func TestIngressDiscoveryNamespaces(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIngressDiscoveryOwnNamespace(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleIngress, NamespaceDiscovery{IncludeOwnNamespace: true})
|
||||
|
||||
expected := expectedTargetGroups("own-ns", TLSNo)
|
||||
|
|
|
@ -273,6 +273,7 @@ func (s *Service) hasSynced() bool {
|
|||
}
|
||||
|
||||
func TestRetryOnError(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, successAt := range []int{1, 2, 3} {
|
||||
var called int
|
||||
f := func() error {
|
||||
|
@ -288,6 +289,7 @@ func TestRetryOnError(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFailuresCountMetric(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
role Role
|
||||
minFailedWatches int
|
||||
|
@ -324,6 +326,7 @@ func TestFailuresCountMetric(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNodeName(t *testing.T) {
|
||||
t.Parallel()
|
||||
node := &apiv1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
|
|
|
@ -56,6 +56,7 @@ func makeEnumeratedNode(i int) *v1.Node {
|
|||
}
|
||||
|
||||
func TestNodeDiscoveryBeforeStart(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleNode, NamespaceDiscovery{})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -95,6 +96,7 @@ func TestNodeDiscoveryBeforeStart(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNodeDiscoveryAdd(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleNode, NamespaceDiscovery{})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -124,6 +126,7 @@ func TestNodeDiscoveryAdd(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNodeDiscoveryDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := makeEnumeratedNode(0)
|
||||
n, c := makeDiscovery(RoleNode, NamespaceDiscovery{}, obj)
|
||||
|
||||
|
@ -142,6 +145,7 @@ func TestNodeDiscoveryDelete(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNodeDiscoveryUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleNode, NamespaceDiscovery{})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
|
|
@ -239,6 +239,7 @@ func expectedPodTargetGroupsWithNodeMeta(ns, nodeName string, nodeLabels map[str
|
|||
}
|
||||
|
||||
func TestPodDiscoveryBeforeRun(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RolePod, NamespaceDiscovery{})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -302,6 +303,7 @@ func TestPodDiscoveryBeforeRun(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPodDiscoveryInitContainer(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RolePod, NamespaceDiscovery{})
|
||||
|
||||
ns := "default"
|
||||
|
@ -329,6 +331,7 @@ func TestPodDiscoveryInitContainer(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPodDiscoveryAdd(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RolePod, NamespaceDiscovery{})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -343,6 +346,7 @@ func TestPodDiscoveryAdd(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPodDiscoveryDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := makePods()
|
||||
n, c := makeDiscovery(RolePod, NamespaceDiscovery{}, obj)
|
||||
|
||||
|
@ -362,6 +366,7 @@ func TestPodDiscoveryDelete(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPodDiscoveryUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
obj := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "testpod",
|
||||
|
@ -403,6 +408,7 @@ func TestPodDiscoveryUpdate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPodDiscoveryUpdateEmptyPodIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RolePod, NamespaceDiscovery{})
|
||||
initialPod := makePods()
|
||||
|
||||
|
@ -427,6 +433,7 @@ func TestPodDiscoveryUpdateEmptyPodIP(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPodDiscoveryNamespaces(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RolePod, NamespaceDiscovery{Names: []string{"ns1", "ns2"}})
|
||||
|
||||
expected := expectedPodTargetGroups("ns1")
|
||||
|
@ -448,6 +455,7 @@ func TestPodDiscoveryNamespaces(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPodDiscoveryOwnNamespace(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RolePod, NamespaceDiscovery{IncludeOwnNamespace: true})
|
||||
|
||||
expected := expectedPodTargetGroups("own-ns")
|
||||
|
@ -466,6 +474,7 @@ func TestPodDiscoveryOwnNamespace(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPodDiscoveryWithNodeMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
attachMetadata := AttachMetadataConfig{Node: true}
|
||||
n, c := makeDiscoveryWithMetadata(RolePod, NamespaceDiscovery{}, attachMetadata)
|
||||
nodeLbls := map[string]string{"l1": "v1"}
|
||||
|
@ -485,6 +494,7 @@ func TestPodDiscoveryWithNodeMetadata(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPodDiscoveryWithNodeMetadataUpdateNode(t *testing.T) {
|
||||
t.Parallel()
|
||||
nodeLbls := map[string]string{"l2": "v2"}
|
||||
attachMetadata := AttachMetadataConfig{Node: true}
|
||||
n, c := makeDiscoveryWithMetadata(RolePod, NamespaceDiscovery{}, attachMetadata)
|
||||
|
|
|
@ -118,6 +118,7 @@ func makeLoadBalancerService() *v1.Service {
|
|||
}
|
||||
|
||||
func TestServiceDiscoveryAdd(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleService, NamespaceDiscovery{})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -189,6 +190,7 @@ func TestServiceDiscoveryAdd(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestServiceDiscoveryDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleService, NamespaceDiscovery{}, makeService())
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -207,6 +209,7 @@ func TestServiceDiscoveryDelete(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestServiceDiscoveryUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleService, NamespaceDiscovery{}, makeService())
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -251,6 +254,7 @@ func TestServiceDiscoveryUpdate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestServiceDiscoveryNamespaces(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleService, NamespaceDiscovery{Names: []string{"ns1", "ns2"}})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -303,6 +307,7 @@ func TestServiceDiscoveryNamespaces(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestServiceDiscoveryOwnNamespace(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleService, NamespaceDiscovery{IncludeOwnNamespace: true})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
@ -338,6 +343,7 @@ func TestServiceDiscoveryOwnNamespace(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestServiceDiscoveryAllNamespaces(t *testing.T) {
|
||||
t.Parallel()
|
||||
n, c := makeDiscovery(RoleService, NamespaceDiscovery{})
|
||||
|
||||
k8sDiscoveryTest{
|
||||
|
|
|
@ -127,19 +127,37 @@ func (m *SDMock) HandleServiceHashiCupsGet() {
|
|||
}
|
||||
|
||||
func TestConfiguredService(t *testing.T) {
|
||||
conf := &SDConfig{
|
||||
Server: "http://localhost:4646",
|
||||
testCases := []struct {
|
||||
name string
|
||||
server string
|
||||
acceptedURL bool
|
||||
}{
|
||||
{"invalid hostname URL", "http://foo.bar:4646", true},
|
||||
{"invalid even though accepted by parsing", "foo.bar:4646", true},
|
||||
{"valid address URL", "http://172.30.29.23:4646", true},
|
||||
{"invalid URL", "172.30.29.23:4646", false},
|
||||
}
|
||||
|
||||
reg := prometheus.NewRegistry()
|
||||
refreshMetrics := discovery.NewRefreshMetrics(reg)
|
||||
metrics := conf.NewDiscovererMetrics(reg, refreshMetrics)
|
||||
require.NoError(t, metrics.Register())
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
conf := &SDConfig{
|
||||
Server: tc.server,
|
||||
}
|
||||
|
||||
_, err := NewDiscovery(conf, nil, metrics)
|
||||
require.NoError(t, err)
|
||||
reg := prometheus.NewRegistry()
|
||||
refreshMetrics := discovery.NewRefreshMetrics(reg)
|
||||
metrics := conf.NewDiscovererMetrics(reg, refreshMetrics)
|
||||
require.NoError(t, metrics.Register())
|
||||
defer metrics.Unregister()
|
||||
|
||||
metrics.Unregister()
|
||||
_, err := NewDiscovery(conf, nil, metrics)
|
||||
if tc.acceptedURL {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNomadSDRefresh(t *testing.T) {
|
||||
|
|
|
@ -135,7 +135,7 @@ global:
|
|||
[ keep_dropped_targets: <int> | default = 0 ]
|
||||
|
||||
# Specifies the validation scheme for metric and label names. Either blank or
|
||||
# "utf8" for for full UTF-8 support, or "legacy" for letters, numbers, colons,
|
||||
# "utf8" for full UTF-8 support, or "legacy" for letters, numbers, colons,
|
||||
# and underscores.
|
||||
[ metric_name_validation_scheme <string> | default "utf8" ]
|
||||
|
||||
|
@ -182,6 +182,10 @@ otlp:
|
|||
# It preserves all special characters like dots, but it still add required suffixes
|
||||
# for units and _total like in UnderscoreEscapingWithSuffixes.
|
||||
[ translation_strategy: <string> | default = "UnderscoreEscapingWithSuffixes" ]
|
||||
# Enables adding "service.name", "service.namespace" and "service.instance.id"
|
||||
# resource attributes to the "target_info" metric, on top of converting
|
||||
# them into the "instance" and "job" labels.
|
||||
[ keep_identifying_resource_attributes: <boolean> | default = false]
|
||||
|
||||
# Settings related to the remote read feature.
|
||||
remote_read:
|
||||
|
@ -672,6 +676,13 @@ http_headers:
|
|||
|
||||
Azure SD configurations allow retrieving scrape targets from Azure VMs.
|
||||
|
||||
The discovery requires at least the following permissions:
|
||||
|
||||
* `Microsoft.Compute/virtualMachines/read`: Required for VM discovery
|
||||
* `Microsoft.Network/networkInterfaces/read`: Required for VM discovery
|
||||
* `Microsoft.Compute/virtualMachineScaleSets/virtualMachines/read`: Required for scale set (VMSS) discovery
|
||||
* `Microsoft.Compute/virtualMachineScaleSets/virtualMachines/networkInterfaces/read`: Required for scale set (VMSS) discovery
|
||||
|
||||
The following meta labels are available on targets during [relabeling](#relabel_config):
|
||||
|
||||
* `__meta_azure_machine_id`: the machine ID
|
||||
|
@ -2126,7 +2137,8 @@ The following meta labels are available on targets during [relabeling](#relabel_
|
|||
[ namespace: <string> | default = default ]
|
||||
[ refresh_interval: <duration> | default = 60s ]
|
||||
[ region: <string> | default = global ]
|
||||
[ server: <host> ]
|
||||
# The URL to connect to the API.
|
||||
[ server: <string> ]
|
||||
[ tag_separator: <string> | default = ,]
|
||||
|
||||
# HTTP client settings, including authentication methods (such as basic auth and
|
||||
|
|
|
@ -262,6 +262,9 @@ process ID.
|
|||
## Shutting down your instance gracefully.
|
||||
|
||||
While Prometheus does have recovery mechanisms in the case that there is an
|
||||
abrupt process failure it is recommend to use the `SIGTERM` signal to cleanly
|
||||
shutdown a Prometheus instance. If you're running on Linux this can be performed
|
||||
by using `kill -s SIGTERM <PID>`, replacing `<PID>` with your Prometheus process ID.
|
||||
abrupt process failure it is recommended to use signals or interrupts for a
|
||||
clean shutdown of a Prometheus instance. On Linux, this can be done by sending
|
||||
the `SIGTERM` or `SIGINT` signals to the Prometheus process. For example, you
|
||||
can use `kill -s <SIGNAL> <PID>`, replacing `<SIGNAL>` with the signal name
|
||||
and `<PID>` with the Prometheus process ID. Alternatively, you can press the
|
||||
interrupt character at the controlling terminal, which by default is `^C` (Control-C).
|
||||
|
|
|
@ -112,7 +112,7 @@ may now fail if this fallback protocol is not specified.
|
|||
The TSDB format has been changed slightly in Prometheus v2.55 in preparation for changes
|
||||
to the index format. Consequently, a Prometheus v3 TSDB can only be read by a
|
||||
Prometheus v2.55 or newer. Keep that in mind when upgrading to v3 -- you will be only
|
||||
able to downgrade to v2.55, not lower, without loosing your TSDB persitent data.
|
||||
able to downgrade to v2.55, not lower, without losing your TSDB persitent data.
|
||||
|
||||
As an extra safety measure, you could optionally consider upgrading to v2.55 first and
|
||||
confirm Prometheus works as expected, before upgrading to v3.
|
||||
|
|
|
@ -433,18 +433,40 @@ URL query parameters:
|
|||
series from which to read the label values. Optional.
|
||||
- `limit=<number>`: Maximum number of returned series. Optional. 0 means disabled.
|
||||
|
||||
|
||||
The `data` section of the JSON response is a list of string label values.
|
||||
|
||||
This example queries for all label values for the `job` label:
|
||||
This example queries for all label values for the `http_status_code` label:
|
||||
|
||||
```json
|
||||
$ curl http://localhost:9090/api/v1/label/job/values
|
||||
$ curl http://localhost:9090/api/v1/label/http_status_code/values
|
||||
{
|
||||
"status" : "success",
|
||||
"data" : [
|
||||
"node",
|
||||
"prometheus"
|
||||
"200",
|
||||
"504"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Label names can optionally be encoded using the Values Escaping method, and is necessary if a name includes the `/` character. To encode a name in this way:
|
||||
|
||||
* Prepend the label with `U__`.
|
||||
* Letters, numbers, and colons appear as-is.
|
||||
* Convert single underscores to double underscores.
|
||||
* For all other characters, use the UTF-8 codepoint as a hex integer, surrounded
|
||||
by underscores. So ` ` becomes `_20_` and a `.` becomes `_2e_`.
|
||||
|
||||
More information about text escaping can be found in the original UTF-8 [Proposal document](https://github.com/prometheus/proposals/blob/main/proposals/2023-08-21-utf8.md#text-escaping).
|
||||
|
||||
This example queries for all label values for the `http.status_code` label:
|
||||
|
||||
```json
|
||||
$ curl http://localhost:9090/api/v1/label/U__http_2e_status_code/values
|
||||
{
|
||||
"status" : "success",
|
||||
"data" : [
|
||||
"200",
|
||||
"404"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
|
@ -37,7 +37,7 @@ Depending on the use-case (e.g. when graphing vs. displaying the output of an
|
|||
expression), only some of these types are legal as the result of a
|
||||
user-specified expression.
|
||||
For [instant queries](api.md#instant-queries), any of the above data types are allowed as the root of the expression.
|
||||
[Range queries](api.md/#range-queries) only support scalar-typed and instant-vector-typed expressions.
|
||||
[Range queries](api.md#range-queries) only support scalar-typed and instant-vector-typed expressions.
|
||||
|
||||
_Notes about the experimental native histograms:_
|
||||
|
||||
|
@ -152,7 +152,7 @@ The value returned will be that of the most recent sample at or before the
|
|||
query's evaluation timestamp (in the case of an
|
||||
[instant query](api.md#instant-queries))
|
||||
or the current step within the query (in the case of a
|
||||
[range query](api.md/#range-queries)).
|
||||
[range query](api.md#range-queries)).
|
||||
The [`@` modifier](#modifier) allows overriding the timestamp relative to which
|
||||
the selection takes place. Time series are only returned if their most recent sample is less than the [lookback period](#staleness) ago.
|
||||
|
||||
|
@ -178,7 +178,7 @@ against regular expressions. The following label matching operators exist:
|
|||
* `=~`: Select labels that regex-match the provided string.
|
||||
* `!~`: Select labels that do not regex-match the provided string.
|
||||
|
||||
Regex matches are fully anchored. A match of `env=~"foo"` is treated as `env=~"^foo$"`.
|
||||
[Regex](#regular-expressions) matches are fully anchored. A match of `env=~"foo"` is treated as `env=~"^foo$"`.
|
||||
|
||||
For example, this selects all `http_requests_total` time series for `staging`,
|
||||
`testing`, and `development` environments and HTTP methods other than `GET`.
|
||||
|
@ -241,9 +241,6 @@ A workaround for this restriction is to use the `__name__` label:
|
|||
|
||||
{__name__="on"} # Good!
|
||||
|
||||
All regular expressions in Prometheus use [RE2
|
||||
syntax](https://github.com/google/re2/wiki/Syntax).
|
||||
|
||||
### Range Vector Selectors
|
||||
|
||||
Range vector literals work like instant vector literals, except that they
|
||||
|
@ -365,6 +362,12 @@ PromQL supports line comments that start with `#`. Example:
|
|||
|
||||
# This is a comment
|
||||
|
||||
## Regular expressions
|
||||
|
||||
All regular expressions in Prometheus use [RE2 syntax](https://github.com/google/re2/wiki/Syntax).
|
||||
|
||||
Regex matches are always fully anchored.
|
||||
|
||||
## Gotchas
|
||||
|
||||
### Staleness
|
||||
|
|
|
@ -25,14 +25,11 @@ for the same vector, making it a [range vector](../basics/#range-vector-selector
|
|||
Note that an expression resulting in a range vector cannot be graphed directly,
|
||||
but viewed in the tabular ("Console") view of the expression browser.
|
||||
|
||||
Using regular expressions, you could select time series only for jobs whose
|
||||
Using [regular expressions](./basics.md#regular-expressions), you could select time series only for jobs whose
|
||||
name match a certain pattern, in this case, all jobs that end with `server`:
|
||||
|
||||
http_requests_total{job=~".*server"}
|
||||
|
||||
All regular expressions in Prometheus use [RE2
|
||||
syntax](https://github.com/google/re2/wiki/Syntax).
|
||||
|
||||
To select all HTTP status codes except 4xx ones, you could run:
|
||||
|
||||
http_requests_total{status!~"4.."}
|
||||
|
|
|
@ -598,7 +598,7 @@ label_join(up{job="api-server",src1="a",src2="b",src3="c"}, "foo", ",", "src1",
|
|||
## `label_replace()`
|
||||
|
||||
For each timeseries in `v`, `label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string)`
|
||||
matches the [regular expression](https://github.com/google/re2/wiki/Syntax) `regex` against the value of the label `src_label`. If it
|
||||
matches the [regular expression](./basics.md#regular-expressions) `regex` against the value of the label `src_label`. If it
|
||||
matches, the value of the label `dst_label` in the returned timeseries will be the expansion
|
||||
of `replacement`, together with the original labels in the input. Capturing groups in the
|
||||
regular expression can be referenced with `$1`, `$2`, etc. Named capturing groups in the regular expression can be referenced with `$name` (where `name` is the capturing group name). If the regular expression doesn't match then the timeseries is returned unchanged.
|
||||
|
|
|
@ -115,13 +115,12 @@ time series you scrape (fewer targets or fewer series per target), or you
|
|||
can increase the scrape interval. However, reducing the number of series is
|
||||
likely more effective, due to compression of samples within a series.
|
||||
|
||||
If your local storage becomes corrupted for whatever reason, the best
|
||||
strategy to address the problem is to shut down Prometheus then remove the
|
||||
entire storage directory. You can also try removing individual block directories,
|
||||
or the WAL directory to resolve the problem. Note that this means losing
|
||||
approximately two hours data per block directory. Again, Prometheus's local
|
||||
storage is not intended to be durable long-term storage; external solutions
|
||||
offer extended retention and data durability.
|
||||
If your local storage becomes corrupted to the point where Prometheus will not
|
||||
start it is recommended to backup the storage directory and restore the
|
||||
corrupted block directories from your backups. If you do not have backups the
|
||||
last resort is to remove the corrupted files. For example you can try removing
|
||||
individual block directories or the write-ahead-log (wal) files. Note that this
|
||||
means losing the data for the time range those blocks or wal covers.
|
||||
|
||||
CAUTION: Non-POSIX compliant filesystems are not supported for Prometheus'
|
||||
local storage as unrecoverable corruptions may happen. NFS filesystems
|
||||
|
|
|
@ -6,11 +6,11 @@ require (
|
|||
github.com/alecthomas/kingpin/v2 v2.4.0
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang/snappy v0.0.4
|
||||
github.com/influxdata/influxdb v1.11.7
|
||||
github.com/influxdata/influxdb v1.11.8
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/prometheus/common v0.60.1
|
||||
github.com/prometheus/prometheus v0.53.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
|
@ -166,8 +166,8 @@ github.com/hetznercloud/hcloud-go/v2 v2.9.0 h1:s0N6R7Zoi2DPfMtUF5o9VeUBzTtHVY6MI
|
|||
github.com/hetznercloud/hcloud-go/v2 v2.9.0/go.mod h1:qtW/TuU7Bs16ibXl/ktJarWqU2LwHr7eGlwoilHxtgg=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/influxdata/influxdb v1.11.7 h1:C31A+S9YfjTCOuAv9Qs0ZdQufslOZZBtejjxiV8QNQw=
|
||||
github.com/influxdata/influxdb v1.11.7/go.mod h1:zRTAuk/Ie/V1LGxJUv8jfDmfv+ypz22lxfhc1MxC3rI=
|
||||
github.com/influxdata/influxdb v1.11.8 h1:lX8MJDfk91O7nqzzonQkjk87gOeQy9V/Xp3gpELhG1s=
|
||||
github.com/influxdata/influxdb v1.11.8/go.mod h1:zRTAuk/Ie/V1LGxJUv8jfDmfv+ypz22lxfhc1MxC3rI=
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.1.11 h1:J/uRN4UWO3wCyGOeDdMKv8LWRzKu6UIkLEaes38Kzh8=
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.1.11/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
|
@ -293,8 +293,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
|
||||
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
|
|
|
@ -43,14 +43,14 @@ func TestMarshalStoreSamplesRequest(t *testing.T) {
|
|||
Value: 3.1415,
|
||||
Tags: tagsFromMetric(metric),
|
||||
}
|
||||
expectedJSON := []byte(`{"metric":"test_.metric","timestamp":4711,"value":3.1415,"tags":{"many_chars":"abc_21ABC_.012-3_2145_C3_B667_7E89./","testlabel":"test_.value"}}`)
|
||||
expectedJSON := `{"metric":"test_.metric","timestamp":4711,"value":3.1415,"tags":{"many_chars":"abc_21ABC_.012-3_2145_C3_B667_7E89./","testlabel":"test_.value"}}`
|
||||
|
||||
resultingJSON, err := json.Marshal(request)
|
||||
require.NoError(t, err, "Marshal(request) resulted in err.")
|
||||
require.Equal(t, expectedJSON, resultingJSON)
|
||||
require.JSONEq(t, expectedJSON, string(resultingJSON))
|
||||
|
||||
var unmarshaledRequest StoreSamplesRequest
|
||||
err = json.Unmarshal(expectedJSON, &unmarshaledRequest)
|
||||
err = json.Unmarshal([]byte(expectedJSON), &unmarshaledRequest)
|
||||
require.NoError(t, err, "Unmarshal(expectedJSON, &unmarshaledRequest) resulted in err.")
|
||||
require.Equal(t, request, unmarshaledRequest)
|
||||
}
|
||||
|
|
|
@ -84,8 +84,8 @@
|
|||
severity: 'warning',
|
||||
},
|
||||
annotations: {
|
||||
summary: 'Prometheus has encountered more than 1% errors sending alerts to a specific Alertmanager.',
|
||||
description: '{{ printf "%%.1f" $value }}%% errors while sending alerts from Prometheus %(prometheusName)s to Alertmanager {{$labels.alertmanager}}.' % $._config,
|
||||
summary: 'More than 1% of alerts sent by Prometheus to a specific Alertmanager were affected by errors.',
|
||||
description: '{{ printf "%%.1f" $value }}%% of alerts sent by Prometheus %(prometheusName)s to Alertmanager {{$labels.alertmanager}} were affected by errors.' % $._config,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
80
go.mod
80
go.mod
|
@ -17,7 +17,7 @@ require (
|
|||
github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3
|
||||
github.com/cespare/xxhash/v2 v2.3.0
|
||||
github.com/dennwc/varint v1.0.0
|
||||
github.com/digitalocean/godo v1.128.0
|
||||
github.com/digitalocean/godo v1.131.0
|
||||
github.com/docker/docker v27.3.1+incompatible
|
||||
github.com/edsrzf/mmap-go v1.2.0
|
||||
github.com/envoyproxy/go-control-plane v0.13.1
|
||||
|
@ -36,12 +36,12 @@ require (
|
|||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||
github.com/hashicorp/consul/api v1.30.0
|
||||
github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.15.0
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.2.1
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.17.0
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.3.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/klauspost/compress v1.17.11
|
||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b
|
||||
github.com/linode/linodego v1.42.0
|
||||
github.com/linode/linodego v1.43.0
|
||||
github.com/miekg/dns v1.1.62
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f
|
||||
|
@ -52,48 +52,48 @@ require (
|
|||
github.com/prometheus/alertmanager v0.27.0
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/prometheus/client_model v0.6.1
|
||||
github.com/prometheus/common v0.60.1
|
||||
github.com/prometheus/common v0.61.0
|
||||
github.com/prometheus/common/assets v0.2.0
|
||||
github.com/prometheus/exporter-toolkit v0.13.1
|
||||
github.com/prometheus/sigv4 v0.1.0
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30
|
||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/vultr/govultr/v2 v2.17.2
|
||||
go.opentelemetry.io/collector/pdata v1.18.0
|
||||
go.opentelemetry.io/collector/semconv v0.112.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0
|
||||
go.opentelemetry.io/otel v1.31.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0
|
||||
go.opentelemetry.io/otel/sdk v1.31.0
|
||||
go.opentelemetry.io/otel/trace v1.31.0
|
||||
go.opentelemetry.io/collector/pdata v1.20.0
|
||||
go.opentelemetry.io/collector/semconv v0.114.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.57.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0
|
||||
go.opentelemetry.io/otel v1.32.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0
|
||||
go.opentelemetry.io/otel/sdk v1.32.0
|
||||
go.opentelemetry.io/otel/trace v1.32.0
|
||||
go.uber.org/atomic v1.11.0
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
go.uber.org/goleak v1.3.0
|
||||
go.uber.org/multierr v1.11.0
|
||||
golang.org/x/oauth2 v0.23.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sys v0.26.0
|
||||
golang.org/x/text v0.19.0
|
||||
golang.org/x/tools v0.26.0
|
||||
google.golang.org/api v0.204.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53
|
||||
google.golang.org/grpc v1.67.1
|
||||
google.golang.org/protobuf v1.35.1
|
||||
golang.org/x/oauth2 v0.24.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/sys v0.28.0
|
||||
golang.org/x/text v0.21.0
|
||||
golang.org/x/tools v0.28.0
|
||||
google.golang.org/api v0.209.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28
|
||||
google.golang.org/grpc v1.68.1
|
||||
google.golang.org/protobuf v1.35.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.31.1
|
||||
k8s.io/apimachinery v0.31.1
|
||||
k8s.io/client-go v0.31.1
|
||||
k8s.io/api v0.31.3
|
||||
k8s.io/apimachinery v0.31.3
|
||||
k8s.io/client-go v0.31.3
|
||||
k8s.io/klog v1.0.0
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/auth v0.10.0 // indirect
|
||||
cloud.google.com/go/auth v0.10.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.5 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.5.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
|
@ -104,7 +104,7 @@ require (
|
|||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cilium/ebpf v0.11.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.0.3 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
|
@ -127,7 +127,7 @@ require (
|
|||
github.com/go-openapi/spec v0.20.14 // indirect
|
||||
github.com/go-openapi/swag v0.22.9 // indirect
|
||||
github.com/go-openapi/validate v0.23.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.13.1 // indirect
|
||||
github.com/go-resty/resty/v2 v2.15.3 // indirect
|
||||
github.com/godbus/dbus/v5 v5.0.4 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/glog v1.2.2 // indirect
|
||||
|
@ -138,9 +138,9 @@ require (
|
|||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/s2a-go v0.1.8 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect
|
||||
github.com/hashicorp/cronexpr v1.1.2 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
|
@ -185,15 +185,15 @@ require (
|
|||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.32.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/crypto v0.30.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/term v0.25.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.32.0 // indirect
|
||||
golang.org/x/term v0.27.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
|
191
go.sum
191
go.sum
|
@ -1,7 +1,7 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go/auth v0.10.0 h1:tWlkvFAh+wwTOzXIjrwM64karR1iTBZ/GRr0S/DULYo=
|
||||
cloud.google.com/go/auth v0.10.0/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
|
||||
cloud.google.com/go/auth v0.10.2 h1:oKF7rgBfSHdp/kuhXtqU/tNDr0mZqhYbEh+6SiqzkKo=
|
||||
cloud.google.com/go/auth v0.10.2/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.5 h1:2p29+dePqsCHPP1bqDJcKj4qxRyYCcbzKpFyKGt3MTk=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.5/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
|
||||
cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
|
||||
|
@ -74,8 +74,8 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D
|
|||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20 h1:N+3sFI5GUjRKBi+i0TxYVST9h4Ie192jJWpHvthBBgg=
|
||||
github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI=
|
||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
|
||||
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
|
@ -91,8 +91,8 @@ github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE=
|
|||
github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/digitalocean/godo v1.128.0 h1:cGn/ibMSRZ9+8etbzMv2MnnCEPTTGlEnx3HHTPwdk1U=
|
||||
github.com/digitalocean/godo v1.128.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
|
||||
github.com/digitalocean/godo v1.131.0 h1:0WHymufAV5avpodT0h5/pucUVfO4v7biquOIqhLeROY=
|
||||
github.com/digitalocean/godo v1.131.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
|
||||
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
|
||||
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
|
@ -160,8 +160,8 @@ github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZC
|
|||
github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE=
|
||||
github.com/go-openapi/validate v0.23.0 h1:2l7PJLzCis4YUGEoW6eoQw3WhyM65WSIcjX6SQnlfDw=
|
||||
github.com/go-openapi/validate v0.23.0/go.mod h1:EeiAZ5bmpSIOJV1WLfyYF9qp/B1ZgSaEpHTJHtN5cbE=
|
||||
github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g=
|
||||
github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0=
|
||||
github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8=
|
||||
github.com/go-resty/resty/v2 v2.15.3/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
|
@ -225,8 +225,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
|
||||
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
|
||||
github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o=
|
||||
github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk=
|
||||
github.com/gophercloud/gophercloud v1.14.1 h1:DTCNaTVGl8/cFu58O1JwWgis9gtISAFONqpMKNg/Vpw=
|
||||
github.com/gophercloud/gophercloud v1.14.1/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
|
@ -235,8 +235,8 @@ github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrR
|
|||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0=
|
||||
github.com/hashicorp/consul/api v1.30.0 h1:ArHVMMILb1nQv8vZSGIwwQd2gtc+oSQZ6CalyiyH2XQ=
|
||||
github.com/hashicorp/consul/api v1.30.0/go.mod h1:B2uGchvaXVW2JhFoS8nqTxMD5PBykr4ebY4JWHTTeLM=
|
||||
github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg=
|
||||
|
@ -287,12 +287,12 @@ github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3 h1:fgVfQ4AC1av
|
|||
github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3/go.mod h1:svtxn6QnrQ69P23VvIWMR34tg3vmwLz4UdUzm1dSCgE=
|
||||
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
|
||||
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.15.0 h1:6mpMJ/RuX1woZj+MCJdyKNEX9129KDkEIDeeyfr4GD4=
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.15.0/go.mod h1:h8sHav+27Xa+48cVMAvAUMELov5h298Ilg2vflyTHgg=
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.17.0 h1:ge0w2piey9SV6XGyU/wQ6HBR24QyMbJ3wLzezplqR68=
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.17.0/go.mod h1:zfyZ4Orx+mPpYDzWAxXR7DHGL50nnlZ5Edzgs1o6f/s=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.2.1 h1:mxxN+frNVmbFrmmFfXnBC3g2USYJrl6mc1LW2iNYbFY=
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.2.1/go.mod h1:SXrO9OGyWjd2rZhAhEpdYN6VUAODzzqRdqA9BCviQtI=
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.3.0 h1:/lTieTH9Mo/CWm3cTlFLnK10jgxjUGkAqRffGqvPteY=
|
||||
github.com/ionos-cloud/sdk-go/v6 v6.3.0/go.mod h1:SXrO9OGyWjd2rZhAhEpdYN6VUAODzzqRdqA9BCviQtI=
|
||||
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
|
||||
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
|
@ -329,8 +329,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/linode/linodego v1.42.0 h1:ZSbi4MtvwrfB9Y6bknesorvvueBGGilcmh2D5dq76RM=
|
||||
github.com/linode/linodego v1.42.0/go.mod h1:2yzmY6pegPBDgx2HDllmt0eIk2IlzqcgK6NR0wFCFRY=
|
||||
github.com/linode/linodego v1.43.0 h1:sGeBB3caZt7vKBoPS5p4AVzmlG4JoqQOdigIibx3egk=
|
||||
github.com/linode/linodego v1.43.0/go.mod h1:n4TMFu1UVNala+icHqrTEFFaicYSF74cSAUG5zkTwfA=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
|
@ -438,8 +438,8 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p
|
|||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
|
||||
github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
|
||||
github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
|
||||
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
|
||||
github.com/prometheus/common/assets v0.2.0 h1:0P5OrzoHrYBOSM1OigWL3mY8ZvV2N4zIE/5AahrSrfM=
|
||||
github.com/prometheus/common/assets v0.2.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI=
|
||||
github.com/prometheus/exporter-toolkit v0.13.1 h1:Evsh0gWQo2bdOHlnz9+0Nm7/OFfIwhE2Ws4A2jIlR04=
|
||||
|
@ -487,8 +487,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
|
|||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
|
||||
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
|
||||
|
@ -498,33 +499,32 @@ github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8
|
|||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
|
||||
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/collector/pdata v1.18.0 h1:/yg2rO2dxqDM2p6GutsMCxXN6sKlXwyIz/ZYyUPONBg=
|
||||
go.opentelemetry.io/collector/pdata v1.18.0/go.mod h1:Ox1YVLe87cZDB/TL30i4SUz1cA5s6AM6SpFMfY61ICs=
|
||||
go.opentelemetry.io/collector/semconv v0.112.0 h1:JPQyvZhlNLVSuVI+FScONaiFygB7h7NTZceUEKIQUEc=
|
||||
go.opentelemetry.io/collector/semconv v0.112.0/go.mod h1:zCJ5njhWpejR+A40kiEoeFm1xq1uzyZwMnRNX6/D82A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 h1:4BZHA+B1wXEQoGNHxW8mURaLhcdGwvRnmhGbm+odRbc=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
||||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
|
||||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
|
||||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||
go.opentelemetry.io/collector/pdata v1.20.0 h1:ePcwt4bdtISP0loHaE+C9xYoU2ZkIvWv89Fob16o9SM=
|
||||
go.opentelemetry.io/collector/pdata v1.20.0/go.mod h1:Ox1YVLe87cZDB/TL30i4SUz1cA5s6AM6SpFMfY61ICs=
|
||||
go.opentelemetry.io/collector/semconv v0.114.0 h1:/eKcCJwZepQUtEuFuxa0thx2XIOvhFpaf214ZG1a11k=
|
||||
go.opentelemetry.io/collector/semconv v0.114.0/go.mod h1:zCJ5njhWpejR+A40kiEoeFm1xq1uzyZwMnRNX6/D82A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.57.0 h1:7F3XCD6WYzDkwbi8I8N+oYJWquPVScnRosKGgqjsR8c=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.57.0/go.mod h1:Dk3C0BfIlZDZ5c6eVS7TYiH2vssuyUU3vUsgbrR+5V4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
|
@ -540,12 +540,9 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
|
||||
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
|
||||
|
@ -554,10 +551,8 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
|
|||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -575,17 +570,12 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -593,10 +583,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -622,41 +610,24 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
@ -667,34 +638,32 @@ golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtn
|
|||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
|
||||
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.204.0 h1:3PjmQQEDkR/ENVZZwIYB4W/KzYtN8OrqnNcHWpeR8E4=
|
||||
google.golang.org/api v0.204.0/go.mod h1:69y8QSoKIbL9F94bWgWAq6wGqGwyjBgi2y8rAK8zLag=
|
||||
google.golang.org/api v0.209.0 h1:Ja2OXNlyRlWCWu8o+GgI4yUn/wz9h/5ZfFbKz+dQX+w=
|
||||
google.golang.org/api v0.209.0/go.mod h1:I53S168Yr/PNDNMi5yPnDc0/LGRZO6o7PoEbl/HY3CM=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f h1:C1QccEa9kUwvMgEUORqQD9S17QesQijxjZ84sO82mfo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113202542-65e8d215514f/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0=
|
||||
google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
@ -704,8 +673,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
@ -733,12 +702,12 @@ gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
|||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
|
||||
k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
|
||||
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
|
||||
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
|
||||
k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
|
||||
k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8=
|
||||
k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE=
|
||||
k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4=
|
||||
k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4=
|
||||
k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
|
|
|
@ -51,7 +51,11 @@ func (ls Labels) String() string {
|
|||
b.WriteByte(',')
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
b.WriteString(l.Name)
|
||||
if !model.LabelName(l.Name).IsValidLegacy() {
|
||||
b.Write(strconv.AppendQuote(b.AvailableBuffer(), l.Name))
|
||||
} else {
|
||||
b.WriteString(l.Name)
|
||||
}
|
||||
b.WriteByte('=')
|
||||
b.Write(strconv.AppendQuote(b.AvailableBuffer(), l.Value))
|
||||
i++
|
||||
|
|
|
@ -39,6 +39,10 @@ func TestLabels_String(t *testing.T) {
|
|||
labels: Labels{},
|
||||
expected: "{}",
|
||||
},
|
||||
{
|
||||
labels: FromStrings("service.name", "t1", "whatever\\whatever", "t2"),
|
||||
expected: `{"service.name"="t1", "whatever\\whatever"="t2"}`,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
str := c.labels.String()
|
||||
|
@ -954,7 +958,7 @@ func TestMarshaling(t *testing.T) {
|
|||
expectedJSON := "{\"aaa\":\"111\",\"bbb\":\"2222\",\"ccc\":\"33333\"}"
|
||||
b, err := json.Marshal(lbls)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedJSON, string(b))
|
||||
require.JSONEq(t, expectedJSON, string(b))
|
||||
|
||||
var gotJ Labels
|
||||
err = json.Unmarshal(b, &gotJ)
|
||||
|
@ -964,7 +968,7 @@ func TestMarshaling(t *testing.T) {
|
|||
expectedYAML := "aaa: \"111\"\nbbb: \"2222\"\nccc: \"33333\"\n"
|
||||
b, err = yaml.Marshal(lbls)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedYAML, string(b))
|
||||
require.YAMLEq(t, expectedYAML, string(b))
|
||||
|
||||
var gotY Labels
|
||||
err = yaml.Unmarshal(b, &gotY)
|
||||
|
@ -980,7 +984,7 @@ func TestMarshaling(t *testing.T) {
|
|||
b, err = json.Marshal(f)
|
||||
require.NoError(t, err)
|
||||
expectedJSONFromStruct := "{\"a_labels\":" + expectedJSON + "}"
|
||||
require.Equal(t, expectedJSONFromStruct, string(b))
|
||||
require.JSONEq(t, expectedJSONFromStruct, string(b))
|
||||
|
||||
var gotFJ foo
|
||||
err = json.Unmarshal(b, &gotFJ)
|
||||
|
@ -990,7 +994,7 @@ func TestMarshaling(t *testing.T) {
|
|||
b, err = yaml.Marshal(f)
|
||||
require.NoError(t, err)
|
||||
expectedYAMLFromStruct := "a_labels:\n aaa: \"111\"\n bbb: \"2222\"\n ccc: \"33333\"\n"
|
||||
require.Equal(t, expectedYAMLFromStruct, string(b))
|
||||
require.YAMLEq(t, expectedYAMLFromStruct, string(b))
|
||||
|
||||
var gotFY foo
|
||||
err = yaml.Unmarshal(b, &gotFY)
|
||||
|
|
|
@ -16,6 +16,7 @@ package relabel
|
|||
import (
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
@ -84,20 +85,20 @@ func (a *Action) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
type Config struct {
|
||||
// A list of labels from which values are taken and concatenated
|
||||
// with the configured separator in order.
|
||||
SourceLabels model.LabelNames `yaml:"source_labels,flow,omitempty"`
|
||||
SourceLabels model.LabelNames `yaml:"source_labels,flow,omitempty" json:"sourceLabels,omitempty"`
|
||||
// Separator is the string between concatenated values from the source labels.
|
||||
Separator string `yaml:"separator,omitempty"`
|
||||
Separator string `yaml:"separator,omitempty" json:"separator,omitempty"`
|
||||
// Regex against which the concatenation is matched.
|
||||
Regex Regexp `yaml:"regex,omitempty"`
|
||||
Regex Regexp `yaml:"regex,omitempty" json:"regex,omitempty"`
|
||||
// Modulus to take of the hash of concatenated values from the source labels.
|
||||
Modulus uint64 `yaml:"modulus,omitempty"`
|
||||
Modulus uint64 `yaml:"modulus,omitempty" json:"modulus,omitempty"`
|
||||
// TargetLabel is the label to which the resulting string is written in a replacement.
|
||||
// Regexp interpolation is allowed for the replace action.
|
||||
TargetLabel string `yaml:"target_label,omitempty"`
|
||||
TargetLabel string `yaml:"target_label,omitempty" json:"targetLabel,omitempty"`
|
||||
// Replacement is the regex replacement pattern to be used.
|
||||
Replacement string `yaml:"replacement,omitempty"`
|
||||
Replacement string `yaml:"replacement,omitempty" json:"replacement,omitempty"`
|
||||
// Action is the action to be performed for the relabeling.
|
||||
Action Action `yaml:"action,omitempty"`
|
||||
Action Action `yaml:"action,omitempty" json:"action,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
|
@ -207,6 +208,25 @@ func (re Regexp) MarshalYAML() (interface{}, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (re *Regexp) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
r, err := NewRegexp(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*re = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (re Regexp) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(re.String())
|
||||
}
|
||||
|
||||
// IsZero implements the yaml.IsZeroer interface.
|
||||
func (re Regexp) IsZero() bool {
|
||||
return re.Regexp == DefaultRelabelConfig.Regex.Regexp
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
package relabel
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
|
@ -964,3 +965,35 @@ func TestRegexp_ShouldMarshalAndUnmarshalZeroValue(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Nil(t, unmarshalled.Regexp)
|
||||
}
|
||||
|
||||
func TestRegexp_JSONUnmarshalThenMarshal(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
}{
|
||||
{
|
||||
name: "Empty regex",
|
||||
input: `{"regex":""}`,
|
||||
},
|
||||
{
|
||||
name: "string literal",
|
||||
input: `{"regex":"foo"}`,
|
||||
},
|
||||
{
|
||||
name: "regex",
|
||||
input: `{"regex":".*foo.*"}`,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var unmarshalled Config
|
||||
err := json.Unmarshal([]byte(test.input), &unmarshalled)
|
||||
require.NoError(t, err)
|
||||
|
||||
marshalled, err := json.Marshal(&unmarshalled)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, test.input, string(marshalled))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -243,7 +243,8 @@ func (p *NHCBParser) compareLabels() bool {
|
|||
// Different metric type.
|
||||
return true
|
||||
}
|
||||
if p.lastHistogramName != convertnhcb.GetHistogramMetricBaseName(p.lset.Get(labels.MetricName)) {
|
||||
_, name := convertnhcb.GetHistogramMetricBaseName(p.lset.Get(labels.MetricName))
|
||||
if p.lastHistogramName != name {
|
||||
// Different metric name.
|
||||
return true
|
||||
}
|
||||
|
@ -253,8 +254,8 @@ func (p *NHCBParser) compareLabels() bool {
|
|||
}
|
||||
|
||||
// Save the label set of the classic histogram without suffix and bucket `le` label.
|
||||
func (p *NHCBParser) storeClassicLabels() {
|
||||
p.lastHistogramName = convertnhcb.GetHistogramMetricBaseName(p.lset.Get(labels.MetricName))
|
||||
func (p *NHCBParser) storeClassicLabels(name string) {
|
||||
p.lastHistogramName = name
|
||||
p.lastHistogramLabelsHash, _ = p.lset.HashWithoutLabels(p.hBuffer, labels.BucketLabel)
|
||||
p.lastHistogramExponential = false
|
||||
}
|
||||
|
@ -275,25 +276,30 @@ func (p *NHCBParser) handleClassicHistogramSeries(lset labels.Labels) bool {
|
|||
}
|
||||
mName := lset.Get(labels.MetricName)
|
||||
// Sanity check to ensure that the TYPE metadata entry name is the same as the base name.
|
||||
if convertnhcb.GetHistogramMetricBaseName(mName) != string(p.bName) {
|
||||
suffixType, name := convertnhcb.GetHistogramMetricBaseName(mName)
|
||||
if name != string(p.bName) {
|
||||
return false
|
||||
}
|
||||
switch {
|
||||
case strings.HasSuffix(mName, "_bucket") && lset.Has(labels.BucketLabel):
|
||||
switch suffixType {
|
||||
case convertnhcb.SuffixBucket:
|
||||
if !lset.Has(labels.BucketLabel) {
|
||||
// This should not really happen.
|
||||
return false
|
||||
}
|
||||
le, err := strconv.ParseFloat(lset.Get(labels.BucketLabel), 64)
|
||||
if err == nil && !math.IsNaN(le) {
|
||||
p.processClassicHistogramSeries(lset, "_bucket", func(hist *convertnhcb.TempHistogram) {
|
||||
p.processClassicHistogramSeries(lset, name, func(hist *convertnhcb.TempHistogram) {
|
||||
_ = hist.SetBucketCount(le, p.value)
|
||||
})
|
||||
return true
|
||||
}
|
||||
case strings.HasSuffix(mName, "_count"):
|
||||
p.processClassicHistogramSeries(lset, "_count", func(hist *convertnhcb.TempHistogram) {
|
||||
case convertnhcb.SuffixCount:
|
||||
p.processClassicHistogramSeries(lset, name, func(hist *convertnhcb.TempHistogram) {
|
||||
_ = hist.SetCount(p.value)
|
||||
})
|
||||
return true
|
||||
case strings.HasSuffix(mName, "_sum"):
|
||||
p.processClassicHistogramSeries(lset, "_sum", func(hist *convertnhcb.TempHistogram) {
|
||||
case convertnhcb.SuffixSum:
|
||||
p.processClassicHistogramSeries(lset, name, func(hist *convertnhcb.TempHistogram) {
|
||||
_ = hist.SetSum(p.value)
|
||||
})
|
||||
return true
|
||||
|
@ -301,12 +307,12 @@ func (p *NHCBParser) handleClassicHistogramSeries(lset labels.Labels) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (p *NHCBParser) processClassicHistogramSeries(lset labels.Labels, suffix string, updateHist func(*convertnhcb.TempHistogram)) {
|
||||
func (p *NHCBParser) processClassicHistogramSeries(lset labels.Labels, name string, updateHist func(*convertnhcb.TempHistogram)) {
|
||||
if p.state != stateCollecting {
|
||||
p.storeClassicLabels()
|
||||
p.storeClassicLabels(name)
|
||||
p.tempCT = p.parser.CreatedTimestamp()
|
||||
p.state = stateCollecting
|
||||
p.tempLsetNHCB = convertnhcb.GetHistogramMetricBase(lset, suffix)
|
||||
p.tempLsetNHCB = convertnhcb.GetHistogramMetricBase(lset, name)
|
||||
}
|
||||
p.storeExemplars()
|
||||
updateHist(&p.tempNHCB)
|
||||
|
|
|
@ -69,6 +69,7 @@ S [ ]
|
|||
<sTimestamp>{S}#{S}\{ l.state = sExemplar; return tComment
|
||||
|
||||
<sExemplar>{L}({L}|{D})* return tLName
|
||||
<sExemplar>\"(\\.|[^\\"\n])*\" l.state = sExemplar; return tQString
|
||||
<sExemplar>\} l.state = sEValue; return tBraceClose
|
||||
<sExemplar>= l.state = sEValue; return tEqual
|
||||
<sEValue>\"(\\.|[^\\"\n])*\" l.state = sExemplar; return tLValue
|
||||
|
|
|
@ -53,9 +53,9 @@ yystate0:
|
|||
case 8: // start condition: sExemplar
|
||||
goto yystart57
|
||||
case 9: // start condition: sEValue
|
||||
goto yystart62
|
||||
goto yystart65
|
||||
case 10: // start condition: sETimestamp
|
||||
goto yystart68
|
||||
goto yystart71
|
||||
}
|
||||
|
||||
yystate1:
|
||||
|
@ -538,125 +538,153 @@ yystart57:
|
|||
switch {
|
||||
default:
|
||||
goto yyabort
|
||||
case c == ',':
|
||||
case c == '"':
|
||||
goto yystate58
|
||||
case c == '=':
|
||||
goto yystate59
|
||||
case c == '}':
|
||||
case c == ',':
|
||||
goto yystate61
|
||||
case c == '=':
|
||||
goto yystate62
|
||||
case c == '}':
|
||||
goto yystate64
|
||||
case c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
|
||||
goto yystate60
|
||||
goto yystate63
|
||||
}
|
||||
|
||||
yystate58:
|
||||
c = l.next()
|
||||
goto yyrule26
|
||||
switch {
|
||||
default:
|
||||
goto yyabort
|
||||
case c == '"':
|
||||
goto yystate59
|
||||
case c == '\\':
|
||||
goto yystate60
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ':
|
||||
goto yystate58
|
||||
}
|
||||
|
||||
yystate59:
|
||||
c = l.next()
|
||||
goto yyrule24
|
||||
goto yyrule23
|
||||
|
||||
yystate60:
|
||||
c = l.next()
|
||||
switch {
|
||||
default:
|
||||
goto yyrule22
|
||||
case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
|
||||
goto yystate60
|
||||
goto yyabort
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ':
|
||||
goto yystate58
|
||||
}
|
||||
|
||||
yystate61:
|
||||
c = l.next()
|
||||
goto yyrule23
|
||||
goto yyrule27
|
||||
|
||||
yystate62:
|
||||
c = l.next()
|
||||
yystart62:
|
||||
switch {
|
||||
default:
|
||||
goto yyabort
|
||||
case c == ' ':
|
||||
goto yystate63
|
||||
case c == '"':
|
||||
goto yystate65
|
||||
}
|
||||
goto yyrule25
|
||||
|
||||
yystate63:
|
||||
c = l.next()
|
||||
switch {
|
||||
default:
|
||||
goto yyabort
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ':
|
||||
goto yystate64
|
||||
goto yyrule22
|
||||
case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
|
||||
goto yystate63
|
||||
}
|
||||
|
||||
yystate64:
|
||||
c = l.next()
|
||||
switch {
|
||||
default:
|
||||
goto yyrule27
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ':
|
||||
goto yystate64
|
||||
}
|
||||
goto yyrule24
|
||||
|
||||
yystate65:
|
||||
c = l.next()
|
||||
yystart65:
|
||||
switch {
|
||||
default:
|
||||
goto yyabort
|
||||
case c == '"':
|
||||
case c == ' ':
|
||||
goto yystate66
|
||||
case c == '\\':
|
||||
goto yystate67
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ':
|
||||
goto yystate65
|
||||
case c == '"':
|
||||
goto yystate68
|
||||
}
|
||||
|
||||
yystate66:
|
||||
c = l.next()
|
||||
goto yyrule25
|
||||
switch {
|
||||
default:
|
||||
goto yyabort
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ':
|
||||
goto yystate67
|
||||
}
|
||||
|
||||
yystate67:
|
||||
c = l.next()
|
||||
switch {
|
||||
default:
|
||||
goto yyabort
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ':
|
||||
goto yystate65
|
||||
goto yyrule28
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ':
|
||||
goto yystate67
|
||||
}
|
||||
|
||||
yystate68:
|
||||
c = l.next()
|
||||
yystart68:
|
||||
switch {
|
||||
default:
|
||||
goto yyabort
|
||||
case c == ' ':
|
||||
goto yystate70
|
||||
case c == '\n':
|
||||
case c == '"':
|
||||
goto yystate69
|
||||
case c == '\\':
|
||||
goto yystate70
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ':
|
||||
goto yystate68
|
||||
}
|
||||
|
||||
yystate69:
|
||||
c = l.next()
|
||||
goto yyrule29
|
||||
goto yyrule26
|
||||
|
||||
yystate70:
|
||||
c = l.next()
|
||||
switch {
|
||||
default:
|
||||
goto yyabort
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ':
|
||||
goto yystate71
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ':
|
||||
goto yystate68
|
||||
}
|
||||
|
||||
yystate71:
|
||||
c = l.next()
|
||||
yystart71:
|
||||
switch {
|
||||
default:
|
||||
goto yyrule28
|
||||
goto yyabort
|
||||
case c == ' ':
|
||||
goto yystate73
|
||||
case c == '\n':
|
||||
goto yystate72
|
||||
}
|
||||
|
||||
yystate72:
|
||||
c = l.next()
|
||||
goto yyrule30
|
||||
|
||||
yystate73:
|
||||
c = l.next()
|
||||
switch {
|
||||
default:
|
||||
goto yyabort
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ':
|
||||
goto yystate71
|
||||
goto yystate74
|
||||
}
|
||||
|
||||
yystate74:
|
||||
c = l.next()
|
||||
switch {
|
||||
default:
|
||||
goto yyrule29
|
||||
case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ':
|
||||
goto yystate74
|
||||
}
|
||||
|
||||
yyrule1: // #{S}
|
||||
|
@ -782,39 +810,45 @@ yyrule22: // {L}({L}|{D})*
|
|||
{
|
||||
return tLName
|
||||
}
|
||||
yyrule23: // \}
|
||||
yyrule23: // \"(\\.|[^\\"\n])*\"
|
||||
{
|
||||
l.state = sExemplar
|
||||
return tQString
|
||||
goto yystate0
|
||||
}
|
||||
yyrule24: // \}
|
||||
{
|
||||
l.state = sEValue
|
||||
return tBraceClose
|
||||
goto yystate0
|
||||
}
|
||||
yyrule24: // =
|
||||
yyrule25: // =
|
||||
{
|
||||
l.state = sEValue
|
||||
return tEqual
|
||||
goto yystate0
|
||||
}
|
||||
yyrule25: // \"(\\.|[^\\"\n])*\"
|
||||
yyrule26: // \"(\\.|[^\\"\n])*\"
|
||||
{
|
||||
l.state = sExemplar
|
||||
return tLValue
|
||||
goto yystate0
|
||||
}
|
||||
yyrule26: // ,
|
||||
yyrule27: // ,
|
||||
{
|
||||
return tComma
|
||||
}
|
||||
yyrule27: // {S}[^ \n]+
|
||||
yyrule28: // {S}[^ \n]+
|
||||
{
|
||||
l.state = sETimestamp
|
||||
return tValue
|
||||
goto yystate0
|
||||
}
|
||||
yyrule28: // {S}[^ \n]+
|
||||
yyrule29: // {S}[^ \n]+
|
||||
{
|
||||
return tTimestamp
|
||||
}
|
||||
yyrule29: // \n
|
||||
yyrule30: // \n
|
||||
if true { // avoid go vet determining the below panic will not be reached
|
||||
l.state = sInit
|
||||
return tLinebreak
|
||||
|
@ -859,10 +893,10 @@ yyabort: // no lexem recognized
|
|||
goto yystate57
|
||||
}
|
||||
if false {
|
||||
goto yystate62
|
||||
goto yystate65
|
||||
}
|
||||
if false {
|
||||
goto yystate68
|
||||
goto yystate71
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -486,9 +486,12 @@ func TestUTF8OpenMetricsParse(t *testing.T) {
|
|||
{"http.status",q="0.9",a="b"} 8.3835e-05
|
||||
{q="0.9","http.status",a="b"} 8.3835e-05
|
||||
{"go.gc_duration_seconds_sum"} 0.004304266
|
||||
{"Heizölrückstoßabdämpfung 10€ metric with \"interesting\" {character\nchoices}","strange©™\n'quoted' \"name\""="6"} 10.0`
|
||||
{"Heizölrückstoßabdämpfung 10€ metric with \"interesting\" {character\nchoices}","strange©™\n'quoted' \"name\""="6"} 10.0
|
||||
quotedexemplar_count 1 # {"id.thing"="histogram-count-test"} 4
|
||||
quotedexemplar2_count 1 # {"id.thing"="histogram-count-test",other="hello"} 4
|
||||
`
|
||||
|
||||
input += "\n# EOF\n"
|
||||
input += "# EOF\n"
|
||||
|
||||
exp := []parsedEntry{
|
||||
{
|
||||
|
@ -535,6 +538,20 @@ func TestUTF8OpenMetricsParse(t *testing.T) {
|
|||
v: 10.0,
|
||||
lset: labels.FromStrings("__name__", `Heizölrückstoßabdämpfung 10€ metric with "interesting" {character
|
||||
choices}`, "strange©™\n'quoted' \"name\"", "6"),
|
||||
}, {
|
||||
m: `quotedexemplar_count`,
|
||||
v: 1,
|
||||
lset: labels.FromStrings("__name__", "quotedexemplar_count"),
|
||||
es: []exemplar.Exemplar{
|
||||
{Labels: labels.FromStrings("id.thing", "histogram-count-test"), Value: 4},
|
||||
},
|
||||
}, {
|
||||
m: `quotedexemplar2_count`,
|
||||
v: 1,
|
||||
lset: labels.FromStrings("__name__", "quotedexemplar2_count"),
|
||||
es: []exemplar.Exemplar{
|
||||
{Labels: labels.FromStrings("id.thing", "histogram-count-test", "other", "hello"), Value: 4},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ func newAlertMetrics(r prometheus.Registerer, queueCap int, queueLen, alertmanag
|
|||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Name: "errors_total",
|
||||
Help: "Total number of errors sending alert notifications.",
|
||||
Help: "Total number of sent alerts affected by errors.",
|
||||
},
|
||||
[]string{alertmanagerLabel},
|
||||
),
|
||||
|
@ -619,13 +619,13 @@ func (n *Manager) sendAll(alerts ...*Alert) bool {
|
|||
|
||||
go func(ctx context.Context, client *http.Client, url string, payload []byte, count int) {
|
||||
if err := n.sendOne(ctx, client, url, payload); err != nil {
|
||||
n.logger.Error("Error sending alert", "alertmanager", url, "count", count, "err", err)
|
||||
n.metrics.errors.WithLabelValues(url).Inc()
|
||||
n.logger.Error("Error sending alerts", "alertmanager", url, "count", count, "err", err)
|
||||
n.metrics.errors.WithLabelValues(url).Add(float64(count))
|
||||
} else {
|
||||
numSuccess.Inc()
|
||||
}
|
||||
n.metrics.latency.WithLabelValues(url).Observe(time.Since(begin).Seconds())
|
||||
n.metrics.sent.WithLabelValues(url).Add(float64(len(amAlerts)))
|
||||
n.metrics.sent.WithLabelValues(url).Add(float64(count))
|
||||
|
||||
wg.Done()
|
||||
}(ctx, ams.client, am.url().String(), payload, len(amAlerts))
|
||||
|
|
|
@ -126,10 +126,7 @@ type QueryEngine interface {
|
|||
// QueryLogger is an interface that can be used to log all the queries logged
|
||||
// by the engine.
|
||||
type QueryLogger interface {
|
||||
Error(msg string, args ...any)
|
||||
Info(msg string, args ...any)
|
||||
Debug(msg string, args ...any)
|
||||
Warn(msg string, args ...any)
|
||||
Log(context.Context, slog.Level, string, ...any)
|
||||
With(args ...any)
|
||||
Close() error
|
||||
}
|
||||
|
@ -637,20 +634,20 @@ func (ng *Engine) exec(ctx context.Context, q *query) (v parser.Value, ws annota
|
|||
// The step provided by the user is in seconds.
|
||||
params["step"] = int64(eq.Interval / (time.Second / time.Nanosecond))
|
||||
}
|
||||
l.With("params", params)
|
||||
f := []interface{}{"params", params}
|
||||
if err != nil {
|
||||
l.With("error", err)
|
||||
f = append(f, "error", err)
|
||||
}
|
||||
l.With("stats", stats.NewQueryStats(q.Stats()))
|
||||
f = append(f, "stats", stats.NewQueryStats(q.Stats()))
|
||||
if span := trace.SpanFromContext(ctx); span != nil {
|
||||
l.With("spanID", span.SpanContext().SpanID())
|
||||
f = append(f, "spanID", span.SpanContext().SpanID())
|
||||
}
|
||||
if origin := ctx.Value(QueryOrigin{}); origin != nil {
|
||||
for k, v := range origin.(map[string]interface{}) {
|
||||
l.With(k, v)
|
||||
f = append(f, k, v)
|
||||
}
|
||||
}
|
||||
l.Info("promql query logged")
|
||||
l.Log(context.Background(), slog.LevelInfo, "promql query logged", f...)
|
||||
// TODO: @tjhop -- do we still need this metric/error log if logger doesn't return errors?
|
||||
// ng.metrics.queryLogFailures.Inc()
|
||||
// ng.logger.Error("can't log query", "err", err)
|
||||
|
@ -1524,7 +1521,7 @@ func (ev *evaluator) evalSubquery(ctx context.Context, subq *parser.SubqueryExpr
|
|||
// Avoid double counting samples when running a subquery, those samples will be counted in later stage.
|
||||
ev.samplesStats = ev.samplesStats.NewChild()
|
||||
val, ws := ev.eval(ctx, subq)
|
||||
// But do incorporate the peak from the subquery
|
||||
// But do incorporate the peak from the subquery.
|
||||
samplesStats.UpdatePeakFromSubquery(ev.samplesStats)
|
||||
ev.samplesStats = samplesStats
|
||||
mat := val.(Matrix)
|
||||
|
@ -1989,7 +1986,7 @@ func (ev *evaluator) eval(ctx context.Context, expr parser.Expr) (parser.Value,
|
|||
// Start with the first timestamp after (ev.startTimestamp - offset - range)
|
||||
// that is aligned with the step (multiple of 'newEv.interval').
|
||||
newEv.startTimestamp = newEv.interval * ((ev.startTimestamp - offsetMillis - rangeMillis) / newEv.interval)
|
||||
if newEv.startTimestamp < (ev.startTimestamp - offsetMillis - rangeMillis) {
|
||||
if newEv.startTimestamp <= (ev.startTimestamp - offsetMillis - rangeMillis) {
|
||||
newEv.startTimestamp += newEv.interval
|
||||
}
|
||||
|
||||
|
@ -3187,18 +3184,19 @@ func (ev *evaluator) aggregationK(e *parser.AggregateExpr, k int, r float64, inp
|
|||
|
||||
seriesLoop:
|
||||
for si := range inputMatrix {
|
||||
f, _, ok := ev.nextValues(enh.Ts, &inputMatrix[si])
|
||||
f, h, ok := ev.nextValues(enh.Ts, &inputMatrix[si])
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
s = Sample{Metric: inputMatrix[si].Metric, F: f, DropName: inputMatrix[si].DropName}
|
||||
s = Sample{Metric: inputMatrix[si].Metric, F: f, H: h, DropName: inputMatrix[si].DropName}
|
||||
|
||||
group := &groups[seriesToResult[si]]
|
||||
// Initialize this group if it's the first time we've seen it.
|
||||
if !group.seen {
|
||||
// LIMIT_RATIO is a special case, as we may not add this very sample to the heap,
|
||||
// while we also don't know the final size of it.
|
||||
if op == parser.LIMIT_RATIO {
|
||||
switch op {
|
||||
case parser.LIMIT_RATIO:
|
||||
*group = groupedAggregation{
|
||||
seen: true,
|
||||
heap: make(vectorByValueHeap, 0),
|
||||
|
@ -3206,12 +3204,34 @@ seriesLoop:
|
|||
if ratiosampler.AddRatioSample(r, &s) {
|
||||
heap.Push(&group.heap, &s)
|
||||
}
|
||||
} else {
|
||||
case parser.LIMITK:
|
||||
*group = groupedAggregation{
|
||||
seen: true,
|
||||
heap: make(vectorByValueHeap, 1, k),
|
||||
}
|
||||
group.heap[0] = s
|
||||
case parser.TOPK:
|
||||
*group = groupedAggregation{
|
||||
seen: true,
|
||||
heap: make(vectorByValueHeap, 0, k),
|
||||
}
|
||||
if s.H != nil {
|
||||
group.seen = false
|
||||
annos.Add(annotations.NewHistogramIgnoredInAggregationInfo("topk", e.PosRange))
|
||||
} else {
|
||||
heap.Push(&group.heap, &s)
|
||||
}
|
||||
case parser.BOTTOMK:
|
||||
*group = groupedAggregation{
|
||||
seen: true,
|
||||
heap: make(vectorByValueHeap, 0, k),
|
||||
}
|
||||
if s.H != nil {
|
||||
group.seen = false
|
||||
annos.Add(annotations.NewHistogramIgnoredInAggregationInfo("bottomk", e.PosRange))
|
||||
} else {
|
||||
heap.Push(&group.heap, &s)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
@ -3220,6 +3240,9 @@ seriesLoop:
|
|||
case parser.TOPK:
|
||||
// We build a heap of up to k elements, with the smallest element at heap[0].
|
||||
switch {
|
||||
case s.H != nil:
|
||||
// Ignore histogram sample and add info annotation.
|
||||
annos.Add(annotations.NewHistogramIgnoredInAggregationInfo("topk", e.PosRange))
|
||||
case len(group.heap) < k:
|
||||
heap.Push(&group.heap, &s)
|
||||
case group.heap[0].F < s.F || (math.IsNaN(group.heap[0].F) && !math.IsNaN(s.F)):
|
||||
|
@ -3233,6 +3256,9 @@ seriesLoop:
|
|||
case parser.BOTTOMK:
|
||||
// We build a heap of up to k elements, with the biggest element at heap[0].
|
||||
switch {
|
||||
case s.H != nil:
|
||||
// Ignore histogram sample and add info annotation.
|
||||
annos.Add(annotations.NewHistogramIgnoredInAggregationInfo("bottomk", e.PosRange))
|
||||
case len(group.heap) < k:
|
||||
heap.Push((*vectorByReverseValueHeap)(&group.heap), &s)
|
||||
case group.heap[0].F > s.F || (math.IsNaN(group.heap[0].F) && !math.IsNaN(s.F)):
|
||||
|
@ -3275,10 +3301,14 @@ seriesLoop:
|
|||
mat = make(Matrix, 0, len(groups))
|
||||
}
|
||||
|
||||
add := func(lbls labels.Labels, f float64, dropName bool) {
|
||||
add := func(lbls labels.Labels, f float64, h *histogram.FloatHistogram, dropName bool) {
|
||||
// If this could be an instant query, add directly to the matrix so the result is in consistent order.
|
||||
if ev.endTimestamp == ev.startTimestamp {
|
||||
mat = append(mat, Series{Metric: lbls, Floats: []FPoint{{T: enh.Ts, F: f}}, DropName: dropName})
|
||||
if h != nil {
|
||||
mat = append(mat, Series{Metric: lbls, Histograms: []HPoint{{T: enh.Ts, H: h}}, DropName: dropName})
|
||||
} else {
|
||||
mat = append(mat, Series{Metric: lbls, Floats: []FPoint{{T: enh.Ts, F: f}}, DropName: dropName})
|
||||
}
|
||||
} else {
|
||||
// Otherwise the results are added into seriess elements.
|
||||
hash := lbls.Hash()
|
||||
|
@ -3286,7 +3316,7 @@ seriesLoop:
|
|||
if !ok {
|
||||
ss = Series{Metric: lbls, DropName: dropName}
|
||||
}
|
||||
addToSeries(&ss, enh.Ts, f, nil, numSteps)
|
||||
addToSeries(&ss, enh.Ts, f, h, numSteps)
|
||||
seriess[hash] = ss
|
||||
}
|
||||
}
|
||||
|
@ -3301,7 +3331,7 @@ seriesLoop:
|
|||
sort.Sort(sort.Reverse(aggr.heap))
|
||||
}
|
||||
for _, v := range aggr.heap {
|
||||
add(v.Metric, v.F, v.DropName)
|
||||
add(v.Metric, v.F, v.H, v.DropName)
|
||||
}
|
||||
|
||||
case parser.BOTTOMK:
|
||||
|
@ -3310,12 +3340,12 @@ seriesLoop:
|
|||
sort.Sort(sort.Reverse((*vectorByReverseValueHeap)(&aggr.heap)))
|
||||
}
|
||||
for _, v := range aggr.heap {
|
||||
add(v.Metric, v.F, v.DropName)
|
||||
add(v.Metric, v.F, v.H, v.DropName)
|
||||
}
|
||||
|
||||
case parser.LIMITK, parser.LIMIT_RATIO:
|
||||
for _, v := range aggr.heap {
|
||||
add(v.Metric, v.F, v.DropName)
|
||||
add(v.Metric, v.F, v.H, v.DropName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -750,7 +751,7 @@ load 10s
|
|||
Interval: 5 * time.Second,
|
||||
},
|
||||
{
|
||||
Query: `count_values("wrong label!", metric)`,
|
||||
Query: `count_values("wrong label!\xff", metric)`,
|
||||
ShouldError: true,
|
||||
},
|
||||
}
|
||||
|
@ -1426,23 +1427,23 @@ load 10s
|
|||
},
|
||||
{
|
||||
// The peak samples in memory is during the first evaluation:
|
||||
// - Subquery takes 22 samples, 11 for each bigmetric, but samples on the left bound won't be evaluated.
|
||||
// - Subquery takes 20 samples, 10 for each bigmetric.
|
||||
// - Result is calculated per series where the series samples is buffered, hence 10 more here.
|
||||
// - The result of two series is added before the last series buffer is discarded, so 2 more here.
|
||||
// Hence at peak it is 22 (subquery) + 10 (buffer of a series) + 2 (result from 2 series).
|
||||
// Hence at peak it is 20 (subquery) + 10 (buffer of a series) + 2 (result from 2 series).
|
||||
// The subquery samples and the buffer is discarded before duplicating.
|
||||
Query: `rate(bigmetric[10s:1s] @ 10)`,
|
||||
MaxSamples: 34,
|
||||
MaxSamples: 32,
|
||||
Start: time.Unix(0, 0),
|
||||
End: time.Unix(10, 0),
|
||||
Interval: 5 * time.Second,
|
||||
},
|
||||
{
|
||||
// Here the reasoning is same as above. But LHS and RHS are done one after another.
|
||||
// So while one of them takes 34 samples at peak, we need to hold the 2 sample
|
||||
// So while one of them takes 32 samples at peak, we need to hold the 2 sample
|
||||
// result of the other till then.
|
||||
Query: `rate(bigmetric[10s:1s] @ 10) + rate(bigmetric[10s:1s] @ 30)`,
|
||||
MaxSamples: 36,
|
||||
MaxSamples: 34,
|
||||
Start: time.Unix(0, 0),
|
||||
End: time.Unix(10, 0),
|
||||
Interval: 5 * time.Second,
|
||||
|
@ -1450,28 +1451,28 @@ load 10s
|
|||
{
|
||||
// promql.Sample as above but with only 1 part as step invariant.
|
||||
// Here the peak is caused by the non-step invariant part as it touches more time range.
|
||||
// Hence at peak it is 2*21 (subquery from 0s to 20s)
|
||||
// Hence at peak it is 2*20 (subquery from 0s to 20s)
|
||||
// + 10 (buffer of a series per evaluation)
|
||||
// + 6 (result from 2 series at 3 eval times).
|
||||
Query: `rate(bigmetric[10s:1s]) + rate(bigmetric[10s:1s] @ 30)`,
|
||||
MaxSamples: 58,
|
||||
MaxSamples: 56,
|
||||
Start: time.Unix(10, 0),
|
||||
End: time.Unix(20, 0),
|
||||
Interval: 5 * time.Second,
|
||||
},
|
||||
{
|
||||
// Nested subquery.
|
||||
// We saw that innermost rate takes 34 samples which is still the peak
|
||||
// We saw that innermost rate takes 32 samples which is still the peak
|
||||
// since the other two subqueries just duplicate the result.
|
||||
Query: `rate(rate(bigmetric[10s:1s] @ 10)[100s:25s] @ 1000)[100s:20s] @ 2000`,
|
||||
MaxSamples: 34,
|
||||
Query: `rate(rate(bigmetric[10:1s] @ 10)[100s:25s] @ 1000)[100s:20s] @ 2000`,
|
||||
MaxSamples: 32,
|
||||
Start: time.Unix(10, 0),
|
||||
},
|
||||
{
|
||||
// Nested subquery.
|
||||
// Now the outermost subquery produces more samples than inner most rate.
|
||||
// Now the outermost subquery produces more samples than innermost rate.
|
||||
Query: `rate(rate(bigmetric[10s:1s] @ 10)[100s:25s] @ 1000)[17s:1s] @ 2000`,
|
||||
MaxSamples: 36,
|
||||
MaxSamples: 34,
|
||||
Start: time.Unix(10, 0),
|
||||
},
|
||||
}
|
||||
|
@ -1616,6 +1617,19 @@ load 1ms
|
|||
}, {
|
||||
query: "metric[100s:25s] @ 300",
|
||||
start: 100,
|
||||
result: promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 22, T: 225000}, {F: 25, T: 250000}, {F: 27, T: 275000}, {F: 30, T: 300000}},
|
||||
Metric: lbls1,
|
||||
},
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 44, T: 225000}, {F: 50, T: 250000}, {F: 54, T: 275000}, {F: 60, T: 300000}},
|
||||
Metric: lbls2,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
query: "metric[100s1ms:25s] @ 300", // Add 1ms to the range to see the legacy behavior of the previous test.
|
||||
start: 100,
|
||||
result: promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 20, T: 200000}, {F: 22, T: 225000}, {F: 25, T: 250000}, {F: 27, T: 275000}, {F: 30, T: 300000}},
|
||||
|
@ -1629,6 +1643,15 @@ load 1ms
|
|||
}, {
|
||||
query: "metric_neg[50s:25s] @ 0",
|
||||
start: 100,
|
||||
result: promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 26, T: -25000}, {F: 1, T: 0}},
|
||||
Metric: lblsneg,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
query: "metric_neg[50s1ms:25s] @ 0", // Add 1ms to the range to see the legacy behavior of the previous test.
|
||||
start: 100,
|
||||
result: promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 51, T: -50000}, {F: 26, T: -25000}, {F: 1, T: 0}},
|
||||
|
@ -1638,6 +1661,15 @@ load 1ms
|
|||
}, {
|
||||
query: "metric_neg[50s:25s] @ -100",
|
||||
start: 100,
|
||||
result: promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 126, T: -125000}, {F: 101, T: -100000}},
|
||||
Metric: lblsneg,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
query: "metric_neg[50s1ms:25s] @ -100", // Add 1ms to the range to see the legacy behavior of the previous test.
|
||||
start: 100,
|
||||
result: promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 151, T: -150000}, {F: 126, T: -125000}, {F: 101, T: -100000}},
|
||||
|
@ -1645,7 +1677,7 @@ load 1ms
|
|||
},
|
||||
},
|
||||
}, {
|
||||
query: `metric_ms[100ms:25ms] @ 2.345`,
|
||||
query: `metric_ms[101ms:25ms] @ 2.345`,
|
||||
start: 100,
|
||||
result: promql.Matrix{
|
||||
promql.Series{
|
||||
|
@ -1830,7 +1862,7 @@ func TestSubquerySelector(t *testing.T) {
|
|||
nil,
|
||||
promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 2, T: 10000}, {F: 2, T: 15000}, {F: 2, T: 20000}, {F: 2, T: 25000}, {F: 2, T: 30000}},
|
||||
Floats: []promql.FPoint{{F: 2, T: 15000}, {F: 2, T: 20000}, {F: 2, T: 25000}, {F: 2, T: 30000}},
|
||||
Metric: labels.FromStrings("__name__", "metric"),
|
||||
},
|
||||
},
|
||||
|
@ -1877,6 +1909,20 @@ func TestSubquerySelector(t *testing.T) {
|
|||
cases: []caseType{
|
||||
{ // Normal selector.
|
||||
Query: `http_requests{group=~"pro.*",instance="0"}[30s:10s]`,
|
||||
Result: promql.Result{
|
||||
nil,
|
||||
promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 10000, T: 10000000}, {F: 100, T: 10010000}, {F: 130, T: 10020000}},
|
||||
Metric: labels.FromStrings("__name__", "http_requests", "job", "api-server", "instance", "0", "group", "production"),
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
Start: time.Unix(10020, 0),
|
||||
},
|
||||
{ // Normal selector. Add 1ms to the range to see the legacy behavior of the previous test.
|
||||
Query: `http_requests{group=~"pro.*",instance="0"}[30s1ms:10s]`,
|
||||
Result: promql.Result{
|
||||
nil,
|
||||
promql.Matrix{
|
||||
|
@ -1919,6 +1965,36 @@ func TestSubquerySelector(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Query: `rate(http_requests[1m])[15s:5s]`,
|
||||
Result: promql.Result{
|
||||
nil,
|
||||
promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 3, T: 7990000}, {F: 3, T: 7995000}, {F: 3, T: 8000000}},
|
||||
Metric: labels.FromStrings("job", "api-server", "instance", "0", "group", "canary"),
|
||||
DropName: true,
|
||||
},
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 4, T: 7990000}, {F: 4, T: 7995000}, {F: 4, T: 8000000}},
|
||||
Metric: labels.FromStrings("job", "api-server", "instance", "1", "group", "canary"),
|
||||
DropName: true,
|
||||
},
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 1, T: 7990000}, {F: 1, T: 7995000}, {F: 1, T: 8000000}},
|
||||
Metric: labels.FromStrings("job", "api-server", "instance", "0", "group", "production"),
|
||||
DropName: true,
|
||||
},
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 2, T: 7990000}, {F: 2, T: 7995000}, {F: 2, T: 8000000}},
|
||||
Metric: labels.FromStrings("job", "api-server", "instance", "1", "group", "production"),
|
||||
DropName: true,
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
Start: time.Unix(8000, 0),
|
||||
},
|
||||
{
|
||||
Query: `rate(http_requests[1m])[15s1ms:5s]`, // Add 1ms to the range to see the legacy behavior of the previous test.
|
||||
Result: promql.Result{
|
||||
nil,
|
||||
promql.Matrix{
|
||||
|
@ -1949,6 +2025,35 @@ func TestSubquerySelector(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Query: `sum(http_requests{group=~"pro.*"})[30s:10s]`,
|
||||
Result: promql.Result{
|
||||
nil,
|
||||
promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 300, T: 100000}, {F: 330, T: 110000}, {F: 360, T: 120000}},
|
||||
Metric: labels.EmptyLabels(),
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
Start: time.Unix(120, 0),
|
||||
},
|
||||
{
|
||||
Query: `sum(http_requests{group=~"pro.*"})[30s:10s]`,
|
||||
Result: promql.Result{
|
||||
nil,
|
||||
promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 300, T: 100000}, {F: 330, T: 110000}, {F: 360, T: 120000}},
|
||||
Metric: labels.EmptyLabels(),
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
Start: time.Unix(121, 0), // 1s later doesn't change the result.
|
||||
},
|
||||
{
|
||||
// Add 1ms to the range to see the legacy behavior of the previous test.
|
||||
Query: `sum(http_requests{group=~"pro.*"})[30s1ms:10s]`,
|
||||
Result: promql.Result{
|
||||
nil,
|
||||
promql.Matrix{
|
||||
|
@ -1963,6 +2068,20 @@ func TestSubquerySelector(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Query: `sum(http_requests)[40s:10s]`,
|
||||
Result: promql.Result{
|
||||
nil,
|
||||
promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 900, T: 90000}, {F: 1000, T: 100000}, {F: 1100, T: 110000}, {F: 1200, T: 120000}},
|
||||
Metric: labels.EmptyLabels(),
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
Start: time.Unix(120, 0),
|
||||
},
|
||||
{
|
||||
Query: `sum(http_requests)[40s1ms:10s]`, // Add 1ms to the range to see the legacy behavior of the previous test.
|
||||
Result: promql.Result{
|
||||
nil,
|
||||
promql.Matrix{
|
||||
|
@ -1977,6 +2096,21 @@ func TestSubquerySelector(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Query: `(sum(http_requests{group=~"p.*"})+sum(http_requests{group=~"c.*"}))[20s:5s]`,
|
||||
Result: promql.Result{
|
||||
nil,
|
||||
promql.Matrix{
|
||||
promql.Series{
|
||||
Floats: []promql.FPoint{{F: 1000, T: 105000}, {F: 1100, T: 110000}, {F: 1100, T: 115000}, {F: 1200, T: 120000}},
|
||||
Metric: labels.EmptyLabels(),
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
Start: time.Unix(120, 0),
|
||||
},
|
||||
{
|
||||
// Add 1ms to the range to see the legacy behavior of the previous test.
|
||||
Query: `(sum(http_requests{group=~"p.*"})+sum(http_requests{group=~"c.*"}))[20s1ms:5s]`,
|
||||
Result: promql.Result{
|
||||
nil,
|
||||
promql.Matrix{
|
||||
|
@ -2034,31 +2168,10 @@ func (f *FakeQueryLogger) Close() error {
|
|||
}
|
||||
|
||||
// It implements the promql.QueryLogger interface.
|
||||
func (f *FakeQueryLogger) Info(msg string, args ...any) {
|
||||
log := append([]any{msg}, args...)
|
||||
log = append(log, f.attrs...)
|
||||
f.attrs = f.attrs[:0]
|
||||
f.logs = append(f.logs, log...)
|
||||
}
|
||||
|
||||
// It implements the promql.QueryLogger interface.
|
||||
func (f *FakeQueryLogger) Error(msg string, args ...any) {
|
||||
log := append([]any{msg}, args...)
|
||||
log = append(log, f.attrs...)
|
||||
f.attrs = f.attrs[:0]
|
||||
f.logs = append(f.logs, log...)
|
||||
}
|
||||
|
||||
// It implements the promql.QueryLogger interface.
|
||||
func (f *FakeQueryLogger) Warn(msg string, args ...any) {
|
||||
log := append([]any{msg}, args...)
|
||||
log = append(log, f.attrs...)
|
||||
f.attrs = f.attrs[:0]
|
||||
f.logs = append(f.logs, log...)
|
||||
}
|
||||
|
||||
// It implements the promql.QueryLogger interface.
|
||||
func (f *FakeQueryLogger) Debug(msg string, args ...any) {
|
||||
func (f *FakeQueryLogger) Log(ctx context.Context, level slog.Level, msg string, args ...any) {
|
||||
// Test usage only really cares about existence of keyvals passed in
|
||||
// via args, just append in the log message before handling the
|
||||
// provided args and any embedded kvs added via `.With()` on f.attrs.
|
||||
log := append([]any{msg}, args...)
|
||||
log = append(log, f.attrs...)
|
||||
f.attrs = f.attrs[:0]
|
||||
|
@ -3494,51 +3607,51 @@ func TestEvaluationWithDelayedNameRemovalDisabled(t *testing.T) {
|
|||
|
||||
promqltest.RunTest(t, `
|
||||
load 5m
|
||||
metric{env="1"} 0 60 120
|
||||
metric_total{env="1"} 0 60 120
|
||||
another_metric{env="1"} 60 120 180
|
||||
|
||||
# Does not drop __name__ for vector selector
|
||||
eval instant at 10m metric{env="1"}
|
||||
metric{env="1"} 120
|
||||
eval instant at 10m metric_total{env="1"}
|
||||
metric_total{env="1"} 120
|
||||
|
||||
# Drops __name__ for unary operators
|
||||
eval instant at 10m -metric
|
||||
eval instant at 10m -metric_total
|
||||
{env="1"} -120
|
||||
|
||||
# Drops __name__ for binary operators
|
||||
eval instant at 10m metric + another_metric
|
||||
eval instant at 10m metric_total + another_metric
|
||||
{env="1"} 300
|
||||
|
||||
# Does not drop __name__ for binary comparison operators
|
||||
eval instant at 10m metric <= another_metric
|
||||
metric{env="1"} 120
|
||||
eval instant at 10m metric_total <= another_metric
|
||||
metric_total{env="1"} 120
|
||||
|
||||
# Drops __name__ for binary comparison operators with "bool" modifier
|
||||
eval instant at 10m metric <= bool another_metric
|
||||
eval instant at 10m metric_total <= bool another_metric
|
||||
{env="1"} 1
|
||||
|
||||
# Drops __name__ for vector-scalar operations
|
||||
eval instant at 10m metric * 2
|
||||
eval instant at 10m metric_total * 2
|
||||
{env="1"} 240
|
||||
|
||||
# Drops __name__ for instant-vector functions
|
||||
eval instant at 10m clamp(metric, 0, 100)
|
||||
eval instant at 10m clamp(metric_total, 0, 100)
|
||||
{env="1"} 100
|
||||
|
||||
# Drops __name__ for round function
|
||||
eval instant at 10m round(metric)
|
||||
eval instant at 10m round(metric_total)
|
||||
{env="1"} 120
|
||||
|
||||
# Drops __name__ for range-vector functions
|
||||
eval instant at 10m rate(metric{env="1"}[10m])
|
||||
eval instant at 10m rate(metric_total{env="1"}[10m])
|
||||
{env="1"} 0.2
|
||||
|
||||
# Does not drop __name__ for last_over_time function
|
||||
eval instant at 10m last_over_time(metric{env="1"}[10m])
|
||||
metric{env="1"} 120
|
||||
eval instant at 10m last_over_time(metric_total{env="1"}[10m])
|
||||
metric_total{env="1"} 120
|
||||
|
||||
# Drops name for other _over_time functions
|
||||
eval instant at 10m max_over_time(metric{env="1"}[10m])
|
||||
eval instant at 10m max_over_time(metric_total{env="1"}[10m])
|
||||
{env="1"} 120
|
||||
`, engine)
|
||||
}
|
||||
|
|
|
@ -345,11 +345,14 @@ func calcTrendValue(i int, tf, s0, s1, b float64) float64 {
|
|||
return x + y
|
||||
}
|
||||
|
||||
// Holt-Winters is similar to a weighted moving average, where historical data has exponentially less influence on the current data.
|
||||
// Holt-Winter also accounts for trends in data. The smoothing factor (0 < sf < 1) affects how historical data will affect the current
|
||||
// data. A lower smoothing factor increases the influence of historical data. The trend factor (0 < tf < 1) affects
|
||||
// how trends in historical data will affect the current data. A higher trend factor increases the influence.
|
||||
// of trends. Algorithm taken from https://en.wikipedia.org/wiki/Exponential_smoothing titled: "Double exponential smoothing".
|
||||
// Double exponential smoothing is similar to a weighted moving average, where
|
||||
// historical data has exponentially less influence on the current data. It also
|
||||
// accounts for trends in data. The smoothing factor (0 < sf < 1) affects how
|
||||
// historical data will affect the current data. A lower smoothing factor
|
||||
// increases the influence of historical data. The trend factor (0 < tf < 1)
|
||||
// affects how trends in historical data will affect the current data. A higher
|
||||
// trend factor increases the influence. of trends. Algorithm taken from
|
||||
// https://en.wikipedia.org/wiki/Exponential_smoothing .
|
||||
func funcDoubleExponentialSmoothing(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
samples := vals[0].(Matrix)[0]
|
||||
|
||||
|
@ -465,11 +468,7 @@ func funcSortByLabelDesc(vals []parser.Value, args parser.Expressions, enh *Eval
|
|||
return vals[0].(Vector), nil
|
||||
}
|
||||
|
||||
// === clamp(Vector parser.ValueTypeVector, min, max Scalar) (Vector, Annotations) ===
|
||||
func funcClamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
vec := vals[0].(Vector)
|
||||
minVal := vals[1].(Vector)[0].F
|
||||
maxVal := vals[2].(Vector)[0].F
|
||||
func clamp(vec Vector, minVal, maxVal float64, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
if maxVal < minVal {
|
||||
return enh.Out, nil
|
||||
}
|
||||
|
@ -490,46 +489,26 @@ func funcClamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper
|
|||
return enh.Out, nil
|
||||
}
|
||||
|
||||
// === clamp(Vector parser.ValueTypeVector, min, max Scalar) (Vector, Annotations) ===
|
||||
func funcClamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
vec := vals[0].(Vector)
|
||||
minVal := vals[1].(Vector)[0].F
|
||||
maxVal := vals[2].(Vector)[0].F
|
||||
return clamp(vec, minVal, maxVal, enh)
|
||||
}
|
||||
|
||||
// === clamp_max(Vector parser.ValueTypeVector, max Scalar) (Vector, Annotations) ===
|
||||
func funcClampMax(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
vec := vals[0].(Vector)
|
||||
maxVal := vals[1].(Vector)[0].F
|
||||
for _, el := range vec {
|
||||
if el.H != nil {
|
||||
// Process only float samples.
|
||||
continue
|
||||
}
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
el.Metric = el.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: el.Metric,
|
||||
F: math.Min(maxVal, el.F),
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
return clamp(vec, math.Inf(-1), maxVal, enh)
|
||||
}
|
||||
|
||||
// === clamp_min(Vector parser.ValueTypeVector, min Scalar) (Vector, Annotations) ===
|
||||
func funcClampMin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
vec := vals[0].(Vector)
|
||||
minVal := vals[1].(Vector)[0].F
|
||||
for _, el := range vec {
|
||||
if el.H != nil {
|
||||
// Process only float samples.
|
||||
continue
|
||||
}
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
el.Metric = el.Metric.DropMetricName()
|
||||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: el.Metric,
|
||||
F: math.Max(minVal, el.F),
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
return enh.Out, nil
|
||||
return clamp(vec, minVal, math.Inf(+1), enh)
|
||||
}
|
||||
|
||||
// === round(Vector parser.ValueTypeVector, toNearest=1 Scalar) (Vector, Annotations) ===
|
||||
|
@ -1324,7 +1303,7 @@ func funcHistogramFraction(vals []parser.Value, args parser.Expressions, enh *Ev
|
|||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: sample.Metric,
|
||||
F: histogramFraction(lower, upper, sample.H),
|
||||
F: HistogramFraction(lower, upper, sample.H),
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
|
@ -1376,7 +1355,7 @@ func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *Ev
|
|||
mb = &metricWithBuckets{sample.Metric, nil}
|
||||
enh.signatureToMetricWithBuckets[string(enh.lblBuf)] = mb
|
||||
}
|
||||
mb.buckets = append(mb.buckets, bucket{upperBound, sample.F})
|
||||
mb.buckets = append(mb.buckets, Bucket{upperBound, sample.F})
|
||||
}
|
||||
|
||||
// Now deal with the histograms.
|
||||
|
@ -1398,14 +1377,14 @@ func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *Ev
|
|||
}
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: sample.Metric,
|
||||
F: histogramQuantile(q, sample.H),
|
||||
F: HistogramQuantile(q, sample.H),
|
||||
DropName: true,
|
||||
})
|
||||
}
|
||||
|
||||
for _, mb := range enh.signatureToMetricWithBuckets {
|
||||
if len(mb.buckets) > 0 {
|
||||
res, forcedMonotonicity, _ := bucketQuantile(q, mb.buckets)
|
||||
res, forcedMonotonicity, _ := BucketQuantile(q, mb.buckets)
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: mb.metric,
|
||||
F: res,
|
||||
|
@ -1424,27 +1403,41 @@ func funcResets(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelpe
|
|||
floats := vals[0].(Matrix)[0].Floats
|
||||
histograms := vals[0].(Matrix)[0].Histograms
|
||||
resets := 0
|
||||
|
||||
if len(floats) > 1 {
|
||||
prev := floats[0].F
|
||||
for _, sample := range floats[1:] {
|
||||
current := sample.F
|
||||
if current < prev {
|
||||
resets++
|
||||
}
|
||||
prev = current
|
||||
}
|
||||
if len(floats) == 0 && len(histograms) == 0 {
|
||||
return enh.Out, nil
|
||||
}
|
||||
|
||||
if len(histograms) > 1 {
|
||||
prev := histograms[0].H
|
||||
for _, sample := range histograms[1:] {
|
||||
current := sample.H
|
||||
if current.DetectReset(prev) {
|
||||
var prevSample, curSample Sample
|
||||
for iFloat, iHistogram := 0, 0; iFloat < len(floats) || iHistogram < len(histograms); {
|
||||
switch {
|
||||
// Process a float sample if no histogram sample remains or its timestamp is earlier.
|
||||
// Process a histogram sample if no float sample remains or its timestamp is earlier.
|
||||
case iHistogram >= len(histograms) || iFloat < len(floats) && floats[iFloat].T < histograms[iHistogram].T:
|
||||
curSample.F = floats[iFloat].F
|
||||
curSample.H = nil
|
||||
iFloat++
|
||||
case iFloat >= len(floats) || iHistogram < len(histograms) && floats[iFloat].T > histograms[iHistogram].T:
|
||||
curSample.H = histograms[iHistogram].H
|
||||
iHistogram++
|
||||
}
|
||||
// Skip the comparison for the first sample, just initialize prevSample.
|
||||
if iFloat+iHistogram == 1 {
|
||||
prevSample = curSample
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case prevSample.H == nil && curSample.H == nil:
|
||||
if curSample.F < prevSample.F {
|
||||
resets++
|
||||
}
|
||||
case prevSample.H != nil && curSample.H == nil, prevSample.H == nil && curSample.H != nil:
|
||||
resets++
|
||||
case prevSample.H != nil && curSample.H != nil:
|
||||
if curSample.H.DetectReset(prevSample.H) {
|
||||
resets++
|
||||
}
|
||||
prev = current
|
||||
}
|
||||
prevSample = curSample
|
||||
}
|
||||
|
||||
return append(enh.Out, Sample{F: float64(resets)}), nil
|
||||
|
@ -1453,20 +1446,43 @@ func funcResets(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelpe
|
|||
// === changes(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
|
||||
func funcChanges(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
|
||||
floats := vals[0].(Matrix)[0].Floats
|
||||
histograms := vals[0].(Matrix)[0].Histograms
|
||||
changes := 0
|
||||
|
||||
if len(floats) == 0 {
|
||||
// TODO(beorn7): Only histogram values, still need to add support.
|
||||
if len(floats) == 0 && len(histograms) == 0 {
|
||||
return enh.Out, nil
|
||||
}
|
||||
|
||||
prev := floats[0].F
|
||||
for _, sample := range floats[1:] {
|
||||
current := sample.F
|
||||
if current != prev && !(math.IsNaN(current) && math.IsNaN(prev)) {
|
||||
changes++
|
||||
var prevSample, curSample Sample
|
||||
for iFloat, iHistogram := 0, 0; iFloat < len(floats) || iHistogram < len(histograms); {
|
||||
switch {
|
||||
// Process a float sample if no histogram sample remains or its timestamp is earlier.
|
||||
// Process a histogram sample if no float sample remains or its timestamp is earlier.
|
||||
case iHistogram >= len(histograms) || iFloat < len(floats) && floats[iFloat].T < histograms[iHistogram].T:
|
||||
curSample.F = floats[iFloat].F
|
||||
curSample.H = nil
|
||||
iFloat++
|
||||
case iFloat >= len(floats) || iHistogram < len(histograms) && floats[iFloat].T > histograms[iHistogram].T:
|
||||
curSample.H = histograms[iHistogram].H
|
||||
iHistogram++
|
||||
}
|
||||
prev = current
|
||||
// Skip the comparison for the first sample, just initialize prevSample.
|
||||
if iFloat+iHistogram == 1 {
|
||||
prevSample = curSample
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case prevSample.H == nil && curSample.H == nil:
|
||||
if curSample.F != prevSample.F && !(math.IsNaN(curSample.F) && math.IsNaN(prevSample.F)) {
|
||||
changes++
|
||||
}
|
||||
case prevSample.H != nil && curSample.H == nil, prevSample.H == nil && curSample.H != nil:
|
||||
changes++
|
||||
case prevSample.H != nil && curSample.H != nil:
|
||||
if !curSample.H.Equals(prevSample.H) {
|
||||
changes++
|
||||
}
|
||||
}
|
||||
prevSample = curSample
|
||||
}
|
||||
|
||||
return append(enh.Out, Sample{F: float64(changes)}), nil
|
||||
|
@ -1577,6 +1593,10 @@ func dateWrapper(vals []parser.Value, enh *EvalNodeHelper, f func(time.Time) flo
|
|||
}
|
||||
|
||||
for _, el := range vals[0].(Vector) {
|
||||
if el.H != nil {
|
||||
// Ignore histogram sample.
|
||||
continue
|
||||
}
|
||||
t := time.Unix(int64(el.F), 0).UTC()
|
||||
if !enh.enableDelayedNameRemoval {
|
||||
el.Metric = el.Metric.DropMetricName()
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
package promql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
|
@ -108,7 +107,7 @@ func TestHistogramStatsDecoding(t *testing.T) {
|
|||
decodedStats = append(decodedStats, h)
|
||||
}
|
||||
for i := 0; i < len(tc.histograms); i++ {
|
||||
require.Equal(t, tc.expectedHints[i], decodedStats[i].CounterResetHint, fmt.Sprintf("mismatch in counter reset hint for histogram %d", i))
|
||||
require.Equalf(t, tc.expectedHints[i], decodedStats[i].CounterResetHint, "mismatch in counter reset hint for histogram %d", i)
|
||||
h := tc.histograms[i]
|
||||
if value.IsStaleNaN(h.Sum) {
|
||||
require.True(t, value.IsStaleNaN(decodedStats[i].Sum))
|
||||
|
|
|
@ -667,8 +667,14 @@ label_set_list : label_set_list COMMA label_set_item
|
|||
|
||||
label_set_item : IDENTIFIER EQL STRING
|
||||
{ $$ = labels.Label{Name: $1.Val, Value: yylex.(*parser).unquoteString($3.Val) } }
|
||||
| string_identifier EQL STRING
|
||||
{ $$ = labels.Label{Name: $1.Val, Value: yylex.(*parser).unquoteString($3.Val) } }
|
||||
| string_identifier
|
||||
{ $$ = labels.Label{Name: labels.MetricName, Value: $1.Val} }
|
||||
| IDENTIFIER EQL error
|
||||
{ yylex.(*parser).unexpected("label set", "string"); $$ = labels.Label{}}
|
||||
| string_identifier EQL error
|
||||
{ yylex.(*parser).unexpected("label set", "string"); $$ = labels.Label{}}
|
||||
| IDENTIFIER error
|
||||
{ yylex.(*parser).unexpected("label set", "\"=\""); $$ = labels.Label{}}
|
||||
| error
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -244,7 +244,8 @@ type seriesDescription struct {
|
|||
values []SequenceValue
|
||||
}
|
||||
|
||||
// ParseSeriesDesc parses the description of a time series.
|
||||
// ParseSeriesDesc parses the description of a time series. It is only used in
|
||||
// the PromQL testing framework code.
|
||||
func ParseSeriesDesc(input string) (labels labels.Labels, values []SequenceValue, err error) {
|
||||
p := NewParser(input)
|
||||
p.lex.seriesDesc = true
|
||||
|
|
|
@ -2398,7 +2398,7 @@ var testExpr = []struct {
|
|||
},
|
||||
},
|
||||
{
|
||||
input: `sum by ("foo")({"some.metric"})`,
|
||||
input: `sum by ("foo bar")({"some.metric"})`,
|
||||
expected: &AggregateExpr{
|
||||
Op: SUM,
|
||||
Expr: &VectorSelector{
|
||||
|
@ -2406,14 +2406,14 @@ var testExpr = []struct {
|
|||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some.metric"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 15,
|
||||
End: 30,
|
||||
Start: 19,
|
||||
End: 34,
|
||||
},
|
||||
},
|
||||
Grouping: []string{"foo"},
|
||||
Grouping: []string{"foo bar"},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 0,
|
||||
End: 31,
|
||||
End: 35,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -4023,6 +4023,76 @@ func TestParseExpressions(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParseSeriesDesc(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expectedLabels labels.Labels
|
||||
expectedValues []SequenceValue
|
||||
expectError string
|
||||
}{
|
||||
{
|
||||
name: "empty string",
|
||||
expectedLabels: labels.EmptyLabels(),
|
||||
expectedValues: []SequenceValue{},
|
||||
},
|
||||
{
|
||||
name: "simple line",
|
||||
input: `http_requests{job="api-server", instance="0", group="production"}`,
|
||||
expectedLabels: labels.FromStrings(
|
||||
"__name__", "http_requests",
|
||||
"group", "production",
|
||||
"instance", "0",
|
||||
"job", "api-server",
|
||||
),
|
||||
expectedValues: []SequenceValue{},
|
||||
},
|
||||
{
|
||||
name: "label name characters that require quoting",
|
||||
input: `{"http.requests", "service.name"="api-server", instance="0", group="canary"} 0+50x2`,
|
||||
expectedLabels: labels.FromStrings(
|
||||
"__name__", "http.requests",
|
||||
"group", "canary",
|
||||
"instance", "0",
|
||||
"service.name", "api-server",
|
||||
),
|
||||
expectedValues: []SequenceValue{
|
||||
{Value: 0, Omitted: false, Histogram: (*histogram.FloatHistogram)(nil)},
|
||||
{Value: 50, Omitted: false, Histogram: (*histogram.FloatHistogram)(nil)},
|
||||
{Value: 100, Omitted: false, Histogram: (*histogram.FloatHistogram)(nil)},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "confirm failure on junk after identifier",
|
||||
input: `{"http.requests"xx} 0+50x2`,
|
||||
expectError: `parse error: unexpected identifier "xx" in label set, expected "," or "}"`,
|
||||
},
|
||||
{
|
||||
name: "confirm failure on bare operator after identifier",
|
||||
input: `{"http.requests"=, x="y"} 0+50x2`,
|
||||
expectError: `parse error: unexpected "," in label set, expected string`,
|
||||
},
|
||||
{
|
||||
name: "confirm failure on unterminated string identifier",
|
||||
input: `{"http.requests} 0+50x2`,
|
||||
expectError: `parse error: unterminated quoted string`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
l, v, err := ParseSeriesDesc(tc.input)
|
||||
if tc.expectError != "" {
|
||||
require.Contains(t, err.Error(), tc.expectError)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.True(t, labels.Equal(tc.expectedLabels, l))
|
||||
require.Equal(t, tc.expectedValues, v)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// NaN has no equality. Thus, we need a separate test for it.
|
||||
func TestNaNExpression(t *testing.T) {
|
||||
expr, err := ParseExpr("NaN")
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -91,13 +93,20 @@ func (node *AggregateExpr) getAggOpStr() string {
|
|||
}
|
||||
|
||||
func joinLabels(ss []string) string {
|
||||
var bytea [1024]byte // On stack to avoid memory allocation while building the output.
|
||||
b := bytes.NewBuffer(bytea[:0])
|
||||
|
||||
for i, s := range ss {
|
||||
// If the label is already quoted, don't quote it again.
|
||||
if s[0] != '"' && s[0] != '\'' && s[0] != '`' && !model.IsValidLegacyMetricName(string(model.LabelValue(s))) {
|
||||
ss[i] = fmt.Sprintf("\"%s\"", s)
|
||||
if i > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
if !model.IsValidLegacyMetricName(string(model.LabelValue(s))) {
|
||||
b.Write(strconv.AppendQuote(b.AvailableBuffer(), s))
|
||||
} else {
|
||||
b.WriteString(s)
|
||||
}
|
||||
}
|
||||
return strings.Join(ss, ", ")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func (node *BinaryExpr) returnBool() string {
|
||||
|
|
|
@ -56,6 +56,10 @@ const (
|
|||
DefaultMaxSamplesPerQuery = 10000
|
||||
)
|
||||
|
||||
func init() {
|
||||
model.NameValidationScheme = model.UTF8Validation
|
||||
}
|
||||
|
||||
type TBRun interface {
|
||||
testing.TB
|
||||
Run(string, func(*testing.T)) bool
|
||||
|
@ -66,7 +70,7 @@ var testStartTime = time.Unix(0, 0).UTC()
|
|||
// LoadedStorage returns storage with generated data using the provided load statements.
|
||||
// Non-load statements will cause test errors.
|
||||
func LoadedStorage(t testutil.T, input string) *teststorage.TestStorage {
|
||||
test, err := newTest(t, input)
|
||||
test, err := newTest(t, input, false, newTestStorage)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, cmd := range test.cmds {
|
||||
|
@ -77,7 +81,7 @@ func LoadedStorage(t testutil.T, input string) *teststorage.TestStorage {
|
|||
t.Errorf("only 'load' commands accepted, got '%s'", cmd)
|
||||
}
|
||||
}
|
||||
return test.storage
|
||||
return test.storage.(*teststorage.TestStorage)
|
||||
}
|
||||
|
||||
// NewTestEngine creates a promql.Engine with enablePerStepStats, lookbackDelta and maxSamples, and returns it.
|
||||
|
@ -108,6 +112,11 @@ func NewTestEngineWithOpts(tb testing.TB, opts promql.EngineOpts) *promql.Engine
|
|||
|
||||
// RunBuiltinTests runs an acceptance test suite against the provided engine.
|
||||
func RunBuiltinTests(t TBRun, engine promql.QueryEngine) {
|
||||
RunBuiltinTestsWithStorage(t, engine, newTestStorage)
|
||||
}
|
||||
|
||||
// RunBuiltinTestsWithStorage runs an acceptance test suite against the provided engine and storage.
|
||||
func RunBuiltinTestsWithStorage(t TBRun, engine promql.QueryEngine, newStorage func(testutil.T) storage.Storage) {
|
||||
t.Cleanup(func() { parser.EnableExperimentalFunctions = false })
|
||||
parser.EnableExperimentalFunctions = true
|
||||
|
||||
|
@ -118,18 +127,29 @@ func RunBuiltinTests(t TBRun, engine promql.QueryEngine) {
|
|||
t.Run(fn, func(t *testing.T) {
|
||||
content, err := fs.ReadFile(testsFs, fn)
|
||||
require.NoError(t, err)
|
||||
RunTest(t, string(content), engine)
|
||||
RunTestWithStorage(t, string(content), engine, newStorage)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// RunTest parses and runs the test against the provided engine.
|
||||
func RunTest(t testutil.T, input string, engine promql.QueryEngine) {
|
||||
require.NoError(t, runTest(t, input, engine))
|
||||
RunTestWithStorage(t, input, engine, newTestStorage)
|
||||
}
|
||||
|
||||
func runTest(t testutil.T, input string, engine promql.QueryEngine) error {
|
||||
test, err := newTest(t, input)
|
||||
// RunTestWithStorage parses and runs the test against the provided engine and storage.
|
||||
func RunTestWithStorage(t testutil.T, input string, engine promql.QueryEngine, newStorage func(testutil.T) storage.Storage) {
|
||||
require.NoError(t, runTest(t, input, engine, newStorage, false))
|
||||
}
|
||||
|
||||
// testTest allows tests to be run in "test-the-test" mode (true for
|
||||
// testingMode). This is a special mode for testing test code execution itself.
|
||||
func testTest(t testutil.T, input string, engine promql.QueryEngine) error {
|
||||
return runTest(t, input, engine, newTestStorage, true)
|
||||
}
|
||||
|
||||
func runTest(t testutil.T, input string, engine promql.QueryEngine, newStorage func(testutil.T) storage.Storage, testingMode bool) error {
|
||||
test, err := newTest(t, input, testingMode, newStorage)
|
||||
|
||||
// Why do this before checking err? newTest() can create the test storage and then return an error,
|
||||
// and we want to make sure to clean that up to avoid leaking goroutines.
|
||||
|
@ -164,20 +184,25 @@ func runTest(t testutil.T, input string, engine promql.QueryEngine) error {
|
|||
// against a test storage.
|
||||
type test struct {
|
||||
testutil.T
|
||||
// testingMode distinguishes between normal execution and test-execution mode.
|
||||
testingMode bool
|
||||
|
||||
cmds []testCommand
|
||||
|
||||
storage *teststorage.TestStorage
|
||||
open func(testutil.T) storage.Storage
|
||||
storage storage.Storage
|
||||
|
||||
context context.Context
|
||||
cancelCtx context.CancelFunc
|
||||
}
|
||||
|
||||
// newTest returns an initialized empty Test.
|
||||
func newTest(t testutil.T, input string) (*test, error) {
|
||||
func newTest(t testutil.T, input string, testingMode bool, newStorage func(testutil.T) storage.Storage) (*test, error) {
|
||||
test := &test{
|
||||
T: t,
|
||||
cmds: []testCommand{},
|
||||
T: t,
|
||||
cmds: []testCommand{},
|
||||
testingMode: testingMode,
|
||||
open: newStorage,
|
||||
}
|
||||
err := test.parse(input)
|
||||
test.clear()
|
||||
|
@ -185,6 +210,8 @@ func newTest(t testutil.T, input string) (*test, error) {
|
|||
return test, err
|
||||
}
|
||||
|
||||
func newTestStorage(t testutil.T) storage.Storage { return teststorage.New(t) }
|
||||
|
||||
//go:embed testdata
|
||||
var testsFs embed.FS
|
||||
|
||||
|
@ -491,8 +518,8 @@ func newTempHistogramWrapper() tempHistogramWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
func processClassicHistogramSeries(m labels.Labels, suffix string, histogramMap map[uint64]tempHistogramWrapper, smpls []promql.Sample, updateHistogram func(*convertnhcb.TempHistogram, float64)) {
|
||||
m2 := convertnhcb.GetHistogramMetricBase(m, suffix)
|
||||
func processClassicHistogramSeries(m labels.Labels, name string, histogramMap map[uint64]tempHistogramWrapper, smpls []promql.Sample, updateHistogram func(*convertnhcb.TempHistogram, float64)) {
|
||||
m2 := convertnhcb.GetHistogramMetricBase(m, name)
|
||||
m2hash := m2.Hash()
|
||||
histogramWrapper, exists := histogramMap[m2hash]
|
||||
if !exists {
|
||||
|
@ -523,21 +550,25 @@ func (cmd *loadCmd) appendCustomHistogram(a storage.Appender) error {
|
|||
for hash, smpls := range cmd.defs {
|
||||
m := cmd.metrics[hash]
|
||||
mName := m.Get(labels.MetricName)
|
||||
switch {
|
||||
case strings.HasSuffix(mName, "_bucket") && m.Has(labels.BucketLabel):
|
||||
suffixType, name := convertnhcb.GetHistogramMetricBaseName(mName)
|
||||
switch suffixType {
|
||||
case convertnhcb.SuffixBucket:
|
||||
if !m.Has(labels.BucketLabel) {
|
||||
panic(fmt.Sprintf("expected bucket label in metric %s", m))
|
||||
}
|
||||
le, err := strconv.ParseFloat(m.Get(labels.BucketLabel), 64)
|
||||
if err != nil || math.IsNaN(le) {
|
||||
continue
|
||||
}
|
||||
processClassicHistogramSeries(m, "_bucket", histogramMap, smpls, func(histogram *convertnhcb.TempHistogram, f float64) {
|
||||
processClassicHistogramSeries(m, name, histogramMap, smpls, func(histogram *convertnhcb.TempHistogram, f float64) {
|
||||
_ = histogram.SetBucketCount(le, f)
|
||||
})
|
||||
case strings.HasSuffix(mName, "_count"):
|
||||
processClassicHistogramSeries(m, "_count", histogramMap, smpls, func(histogram *convertnhcb.TempHistogram, f float64) {
|
||||
case convertnhcb.SuffixCount:
|
||||
processClassicHistogramSeries(m, name, histogramMap, smpls, func(histogram *convertnhcb.TempHistogram, f float64) {
|
||||
_ = histogram.SetCount(f)
|
||||
})
|
||||
case strings.HasSuffix(mName, "_sum"):
|
||||
processClassicHistogramSeries(m, "_sum", histogramMap, smpls, func(histogram *convertnhcb.TempHistogram, f float64) {
|
||||
case convertnhcb.SuffixSum:
|
||||
processClassicHistogramSeries(m, name, histogramMap, smpls, func(histogram *convertnhcb.TempHistogram, f float64) {
|
||||
_ = histogram.SetSum(f)
|
||||
})
|
||||
}
|
||||
|
@ -1074,11 +1105,25 @@ func (t *test) exec(tc testCommand, engine promql.QueryEngine) error {
|
|||
}
|
||||
|
||||
func (t *test) execEval(cmd *evalCmd, engine promql.QueryEngine) error {
|
||||
if cmd.isRange {
|
||||
return t.execRangeEval(cmd, engine)
|
||||
do := func() error {
|
||||
if cmd.isRange {
|
||||
return t.execRangeEval(cmd, engine)
|
||||
}
|
||||
|
||||
return t.execInstantEval(cmd, engine)
|
||||
}
|
||||
|
||||
return t.execInstantEval(cmd, engine)
|
||||
if t.testingMode {
|
||||
return do()
|
||||
}
|
||||
|
||||
if tt, ok := t.T.(*testing.T); ok {
|
||||
tt.Run(fmt.Sprintf("line %d/%s", cmd.line, cmd.expr), func(t *testing.T) {
|
||||
require.NoError(t, do())
|
||||
})
|
||||
return nil
|
||||
}
|
||||
return errors.New("t.T is not testing.T")
|
||||
}
|
||||
|
||||
func (t *test) execRangeEval(cmd *evalCmd, engine promql.QueryEngine) error {
|
||||
|
@ -1097,12 +1142,16 @@ func (t *test) execRangeEval(cmd *evalCmd, engine promql.QueryEngine) error {
|
|||
if res.Err == nil && cmd.fail {
|
||||
return fmt.Errorf("expected error evaluating query %q (line %d) but got none", cmd.expr, cmd.line)
|
||||
}
|
||||
countWarnings, _ := res.Warnings.CountWarningsAndInfo()
|
||||
if !cmd.warn && countWarnings > 0 {
|
||||
countWarnings, countInfo := res.Warnings.CountWarningsAndInfo()
|
||||
switch {
|
||||
case !cmd.warn && countWarnings > 0:
|
||||
return fmt.Errorf("unexpected warnings evaluating query %q (line %d): %v", cmd.expr, cmd.line, res.Warnings)
|
||||
}
|
||||
if cmd.warn && countWarnings == 0 {
|
||||
case cmd.warn && countWarnings == 0:
|
||||
return fmt.Errorf("expected warnings evaluating query %q (line %d) but got none", cmd.expr, cmd.line)
|
||||
case !cmd.info && countInfo > 0:
|
||||
return fmt.Errorf("unexpected info annotations evaluating query %q (line %d): %v", cmd.expr, cmd.line, res.Warnings)
|
||||
case cmd.info && countInfo == 0:
|
||||
return fmt.Errorf("expected info annotations evaluating query %q (line %d) but got none", cmd.expr, cmd.line)
|
||||
}
|
||||
defer q.Close()
|
||||
|
||||
|
@ -1148,13 +1197,14 @@ func (t *test) runInstantQuery(iq atModifierTestCase, cmd *evalCmd, engine promq
|
|||
return fmt.Errorf("expected error evaluating query %q (line %d) but got none", iq.expr, cmd.line)
|
||||
}
|
||||
countWarnings, countInfo := res.Warnings.CountWarningsAndInfo()
|
||||
if !cmd.warn && countWarnings > 0 {
|
||||
switch {
|
||||
case !cmd.warn && countWarnings > 0:
|
||||
return fmt.Errorf("unexpected warnings evaluating query %q (line %d): %v", iq.expr, cmd.line, res.Warnings)
|
||||
}
|
||||
if cmd.warn && countWarnings == 0 {
|
||||
case cmd.warn && countWarnings == 0:
|
||||
return fmt.Errorf("expected warnings evaluating query %q (line %d) but got none", iq.expr, cmd.line)
|
||||
}
|
||||
if cmd.info && countInfo == 0 {
|
||||
case !cmd.info && countInfo > 0:
|
||||
return fmt.Errorf("unexpected info annotations evaluating query %q (line %d): %v", iq.expr, cmd.line, res.Warnings)
|
||||
case cmd.info && countInfo == 0:
|
||||
return fmt.Errorf("expected info annotations evaluating query %q (line %d) but got none", iq.expr, cmd.line)
|
||||
}
|
||||
err = cmd.compareResult(res.Value)
|
||||
|
@ -1235,7 +1285,7 @@ func (t *test) clear() {
|
|||
if t.cancelCtx != nil {
|
||||
t.cancelCtx()
|
||||
}
|
||||
t.storage = teststorage.New(t)
|
||||
t.storage = t.open(t.T)
|
||||
t.context, t.cancelCtx = context.WithCancel(context.Background())
|
||||
}
|
||||
|
||||
|
|
|
@ -165,6 +165,8 @@ load 5m
|
|||
http_requests{job="api-server", instance="1", group="production"} 0+20x10
|
||||
http_requests{job="api-server", instance="0", group="canary"} 0+30x10
|
||||
http_requests{job="api-server", instance="1", group="canary"} 0+40x10
|
||||
{"http.requests", "service.name"="api-server", instance="0", group="canary"} 0+50x10
|
||||
{"http.requests", "service.name"="api-server", instance="1", group="canary"} 0+60x10
|
||||
`
|
||||
|
||||
testCases := map[string]struct {
|
||||
|
@ -176,6 +178,12 @@ load 5m
|
|||
eval instant at 5m sum by (group) (http_requests)
|
||||
{group="production"} 30
|
||||
{group="canary"} 70
|
||||
`,
|
||||
},
|
||||
"instant query on UTF-8 metric with expected float result": {
|
||||
input: testData + `
|
||||
eval instant at 5m sum by ("service.name") ({"http.requests"})
|
||||
{"service.name"="api-server"} 110
|
||||
`,
|
||||
},
|
||||
"instant query with unexpected float result": {
|
||||
|
@ -184,7 +192,7 @@ eval instant at 5m sum by (group) (http_requests)
|
|||
{group="production"} 30
|
||||
{group="canary"} 80
|
||||
`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 8): expected 80 for {group="canary"} but got 70`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 10): expected 80 for {group="canary"} but got 70`,
|
||||
},
|
||||
"instant query with expected histogram result": {
|
||||
input: `
|
||||
|
@ -230,7 +238,7 @@ eval instant at 0 testmetric
|
|||
eval instant at 5m sum by (group) (http_requests)
|
||||
{group="production"} 30
|
||||
`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 8): unexpected metric {group="canary"} in result, has value 70`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 10): unexpected metric {group="canary"} in result, has value 70`,
|
||||
},
|
||||
"instant query, but result has an unexpected series with a histogram value": {
|
||||
input: `
|
||||
|
@ -248,7 +256,7 @@ eval instant at 5m sum by (group) (http_requests)
|
|||
{group="canary"} 70
|
||||
{group="test"} 100
|
||||
`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 8): expected metric {group="test"} with 3: [100.000000] not found`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 10): expected metric {group="test"} with 3: [100.000000] not found`,
|
||||
},
|
||||
"instant query expected to fail, and query fails": {
|
||||
input: `
|
||||
|
@ -334,7 +342,7 @@ eval_ordered instant at 50m sort(http_requests)
|
|||
http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
`,
|
||||
expectedError: `error in eval sort(http_requests) (line 8): expected metric {__name__="http_requests", group="canary", instance="0", job="api-server"} with [300.000000] at position 4 but was at 3`,
|
||||
expectedError: `error in eval sort(http_requests) (line 10): expected metric {__name__="http_requests", group="canary", instance="0", job="api-server"} with [300.000000] at position 4 but was at 3`,
|
||||
},
|
||||
"instant query with results expected to match provided order, but result has an unexpected series": {
|
||||
input: testData + `
|
||||
|
@ -343,7 +351,7 @@ eval_ordered instant at 50m sort(http_requests)
|
|||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
`,
|
||||
expectedError: `error in eval sort(http_requests) (line 8): unexpected metric {__name__="http_requests", group="canary", instance="1", job="api-server"} in result, has value 400`,
|
||||
expectedError: `error in eval sort(http_requests) (line 10): unexpected metric {__name__="http_requests", group="canary", instance="1", job="api-server"} in result, has value 400`,
|
||||
},
|
||||
"instant query with invalid timestamp": {
|
||||
input: `eval instant at abc123 vector(0)`,
|
||||
|
@ -362,7 +370,7 @@ eval range from 0 to 10m step 5m sum by (group) (http_requests)
|
|||
{group="production"} 0 30 60
|
||||
{group="canary"} 0 80 140
|
||||
`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 8): expected float value at index 1 (t=300000) for {group="canary"} to be 80, but got 70 (result has 3 float points [0 @[0] 70 @[300000] 140 @[600000]] and 0 histogram points [])`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 10): expected float value at index 1 (t=300000) for {group="canary"} to be 80, but got 70 (result has 3 float points [0 @[0] 70 @[300000] 140 @[600000]] and 0 histogram points [])`,
|
||||
},
|
||||
"range query with expected histogram values": {
|
||||
input: `
|
||||
|
@ -389,7 +397,7 @@ eval range from 0 to 10m step 5m sum by (group) (http_requests)
|
|||
{group="production"} 0 30 60 90
|
||||
{group="canary"} 0 70 140
|
||||
`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 8): expected 4 points for {group="production"}, but query time range cannot return this many points`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 10): expected 4 points for {group="production"}, but query time range cannot return this many points`,
|
||||
},
|
||||
"range query with missing point in result": {
|
||||
input: `
|
||||
|
@ -407,14 +415,14 @@ eval range from 0 to 10m step 5m sum by (group) (http_requests)
|
|||
{group="production"} 0 30
|
||||
{group="canary"} 0 70 140
|
||||
`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 8): expected 2 float points and 0 histogram points for {group="production"}, but got 3 float points [0 @[0] 30 @[300000] 60 @[600000]] and 0 histogram points []`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 10): expected 2 float points and 0 histogram points for {group="production"}, but got 3 float points [0 @[0] 30 @[300000] 60 @[600000]] and 0 histogram points []`,
|
||||
},
|
||||
"range query, but result has an unexpected series": {
|
||||
input: testData + `
|
||||
eval range from 0 to 10m step 5m sum by (group) (http_requests)
|
||||
{group="production"} 0 30 60
|
||||
`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 8): unexpected metric {group="canary"} in result, has 3 float points [0 @[0] 70 @[300000] 140 @[600000]] and 0 histogram points []`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 10): unexpected metric {group="canary"} in result, has 3 float points [0 @[0] 70 @[300000] 140 @[600000]] and 0 histogram points []`,
|
||||
},
|
||||
"range query, but result is missing a series": {
|
||||
input: testData + `
|
||||
|
@ -423,7 +431,7 @@ eval range from 0 to 10m step 5m sum by (group) (http_requests)
|
|||
{group="canary"} 0 70 140
|
||||
{group="test"} 0 100 200
|
||||
`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 8): expected metric {group="test"} not found`,
|
||||
expectedError: `error in eval sum by (group) (http_requests) (line 10): expected metric {group="test"} not found`,
|
||||
},
|
||||
"range query expected to fail, and query fails": {
|
||||
input: `
|
||||
|
@ -595,7 +603,7 @@ eval range from 0 to 5m step 5m testmetric
|
|||
|
||||
for name, testCase := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
err := runTest(t, testCase.input, NewTestEngine(t, false, 0, DefaultMaxSamplesPerQuery))
|
||||
err := testTest(t, testCase.input, NewTestEngine(t, false, 0, DefaultMaxSamplesPerQuery))
|
||||
|
||||
if testCase.expectedError == "" {
|
||||
require.NoError(t, err)
|
||||
|
|
60
promql/promqltest/testdata/aggregators.test
vendored
60
promql/promqltest/testdata/aggregators.test
vendored
|
@ -272,6 +272,8 @@ load 5m
|
|||
http_requests{job="app-server", instance="1", group="production"} 0+60x10
|
||||
http_requests{job="app-server", instance="0", group="canary"} 0+70x10
|
||||
http_requests{job="app-server", instance="1", group="canary"} 0+80x10
|
||||
http_requests_histogram{job="app-server", instance="2", group="canary"} {{schema:0 sum:10 count:10}}x11
|
||||
http_requests_histogram{job="api-server", instance="3", group="production"} {{schema:0 sum:20 count:20}}x11
|
||||
foo 3+0x10
|
||||
|
||||
eval_ordered instant at 50m topk(3, http_requests)
|
||||
|
@ -338,6 +340,47 @@ eval_ordered instant at 50m topk(scalar(foo), http_requests)
|
|||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
|
||||
# Tests for histogram: should ignore histograms.
|
||||
eval_info instant at 50m topk(100, http_requests_histogram)
|
||||
#empty
|
||||
|
||||
eval_info range from 0 to 50m step 5m topk(100, http_requests_histogram)
|
||||
#empty
|
||||
|
||||
eval_info instant at 50m topk(1, {__name__=~"http_requests(_histogram)?"})
|
||||
{__name__="http_requests", group="canary", instance="1", job="app-server"} 800
|
||||
|
||||
eval_info instant at 50m count(topk(1000, {__name__=~"http_requests(_histogram)?"}))
|
||||
{} 9
|
||||
|
||||
eval_info range from 0 to 50m step 5m count(topk(1000, {__name__=~"http_requests(_histogram)?"}))
|
||||
{} 9x10
|
||||
|
||||
eval_info instant at 50m topk by (instance) (1, {__name__=~"http_requests(_histogram)?"})
|
||||
{__name__="http_requests", group="canary", instance="0", job="app-server"} 700
|
||||
{__name__="http_requests", group="canary", instance="1", job="app-server"} 800
|
||||
{__name__="http_requests", group="production", instance="2", job="api-server"} NaN
|
||||
|
||||
eval_info instant at 50m bottomk(100, http_requests_histogram)
|
||||
#empty
|
||||
|
||||
eval_info range from 0 to 50m step 5m bottomk(100, http_requests_histogram)
|
||||
#empty
|
||||
|
||||
eval_info instant at 50m bottomk(1, {__name__=~"http_requests(_histogram)?"})
|
||||
{__name__="http_requests", group="production", instance="0", job="api-server"} 100
|
||||
|
||||
eval_info instant at 50m count(bottomk(1000, {__name__=~"http_requests(_histogram)?"}))
|
||||
{} 9
|
||||
|
||||
eval_info range from 0 to 50m step 5m count(bottomk(1000, {__name__=~"http_requests(_histogram)?"}))
|
||||
{} 9x10
|
||||
|
||||
eval_info instant at 50m bottomk by (instance) (1, {__name__=~"http_requests(_histogram)?"})
|
||||
{__name__="http_requests", group="production", instance="0", job="api-server"} 100
|
||||
{__name__="http_requests", group="production", instance="1", job="api-server"} 200
|
||||
{__name__="http_requests", group="production", instance="2", job="api-server"} NaN
|
||||
|
||||
clear
|
||||
|
||||
# Tests for count_values.
|
||||
|
@ -500,6 +543,7 @@ load 10s
|
|||
data{test="bigzero",point="b"} -9.988465674311579e+307
|
||||
data{test="bigzero",point="c"} 9.988465674311579e+307
|
||||
data{test="bigzero",point="d"} 9.988465674311579e+307
|
||||
data{test="value is nan"} NaN
|
||||
|
||||
eval instant at 1m avg(data{test="ten"})
|
||||
{} 10
|
||||
|
@ -534,6 +578,10 @@ eval instant at 1m avg(data{test="-big"})
|
|||
eval instant at 1m avg(data{test="bigzero"})
|
||||
{} 0
|
||||
|
||||
# If NaN is in the mix, the result is NaN.
|
||||
eval instant at 1m avg(data)
|
||||
{} NaN
|
||||
|
||||
# Test summing and averaging extreme values.
|
||||
clear
|
||||
|
||||
|
@ -624,11 +672,11 @@ eval_info instant at 0m stddev({label="c"})
|
|||
|
||||
eval_info instant at 0m stdvar({label="c"})
|
||||
|
||||
eval instant at 0m stddev by (label) (series)
|
||||
eval_info instant at 0m stddev by (label) (series)
|
||||
{label="a"} 0
|
||||
{label="b"} 0
|
||||
|
||||
eval instant at 0m stdvar by (label) (series)
|
||||
eval_info instant at 0m stdvar by (label) (series)
|
||||
{label="a"} 0
|
||||
{label="b"} 0
|
||||
|
||||
|
@ -639,17 +687,17 @@ load 5m
|
|||
series{label="b"} 1
|
||||
series{label="c"} 2
|
||||
|
||||
eval instant at 0m stddev(series)
|
||||
eval_info instant at 0m stddev(series)
|
||||
{} 0.5
|
||||
|
||||
eval instant at 0m stdvar(series)
|
||||
eval_info instant at 0m stdvar(series)
|
||||
{} 0.25
|
||||
|
||||
eval instant at 0m stddev by (label) (series)
|
||||
eval_info instant at 0m stddev by (label) (series)
|
||||
{label="b"} 0
|
||||
{label="c"} 0
|
||||
|
||||
eval instant at 0m stdvar by (label) (series)
|
||||
eval_info instant at 0m stdvar by (label) (series)
|
||||
{label="b"} 0
|
||||
{label="c"} 0
|
||||
|
||||
|
|
3
promql/promqltest/testdata/at_modifier.test
vendored
3
promql/promqltest/testdata/at_modifier.test
vendored
|
@ -90,7 +90,8 @@ eval instant at 25s sum_over_time(metric{job="1"}[100] offset 50s @ 100)
|
|||
eval instant at 25s metric{job="1"} @ 50 + metric{job="1"} @ 100
|
||||
{job="1"} 15
|
||||
|
||||
eval instant at 25s rate(metric{job="1"}[100s] @ 100) + label_replace(rate(metric{job="2"}[123s] @ 200), "job", "1", "", "")
|
||||
# Note that this triggers an info annotation because we are rate'ing a metric that does not end in `_total`.
|
||||
eval_info instant at 25s rate(metric{job="1"}[100s] @ 100) + label_replace(rate(metric{job="2"}[123s] @ 200), "job", "1", "", "")
|
||||
{job="1"} 0.3
|
||||
|
||||
eval instant at 25s sum_over_time(metric{job="1"}[100s] @ 100) + label_replace(sum_over_time(metric{job="2"}[100s] @ 100), "job", "1", "", "")
|
||||
|
|
259
promql/promqltest/testdata/functions.test
vendored
259
promql/promqltest/testdata/functions.test
vendored
|
@ -2,7 +2,10 @@
|
|||
load 5m
|
||||
http_requests{path="/foo"} 1 2 3 0 1 0 0 1 2 0
|
||||
http_requests{path="/bar"} 1 2 3 4 5 1 2 3 4 5
|
||||
http_requests{path="/biz"} 0 0 0 0 0 1 1 1 1 1
|
||||
http_requests{path="/biz"} 0 0 0 0 0 1 1 1 1 1
|
||||
http_requests_histogram{path="/foo"} {{schema:0 sum:1 count:1}}x9
|
||||
http_requests_histogram{path="/bar"} 0 0 0 0 0 0 0 0 {{schema:0 sum:1 count:1}} {{schema:0 sum:1 count:1}}
|
||||
http_requests_histogram{path="/biz"} 0 1 0 2 0 3 0 {{schema:0 sum:1 count:1 z_bucket_w:0.001 z_bucket:2}} {{schema:0 sum:2 count:2 z_bucket_w:0.001 z_bucket:1}} {{schema:0 sum:1 count:1 z_bucket_w:0.001 z_bucket:2}}
|
||||
|
||||
# Tests for resets().
|
||||
eval instant at 50m resets(http_requests[5m])
|
||||
|
@ -39,6 +42,18 @@ eval instant at 50m resets(http_requests[50m])
|
|||
|
||||
eval instant at 50m resets(nonexistent_metric[50m])
|
||||
|
||||
# Test for mix of floats and histograms.
|
||||
|
||||
eval instant at 50m resets(http_requests_histogram[6m])
|
||||
{path="/foo"} 0
|
||||
{path="/bar"} 0
|
||||
{path="/biz"} 0
|
||||
|
||||
eval instant at 50m resets(http_requests_histogram[60m])
|
||||
{path="/foo"} 0
|
||||
{path="/bar"} 1
|
||||
{path="/biz"} 6
|
||||
|
||||
# Tests for changes().
|
||||
eval instant at 50m changes(http_requests[5m])
|
||||
|
||||
|
@ -69,6 +84,21 @@ eval instant at 50m changes((http_requests[50m]))
|
|||
|
||||
eval instant at 50m changes(nonexistent_metric[50m])
|
||||
|
||||
# Test for mix of floats and histograms.
|
||||
# Because of bug #14172 we are not able to test more complex cases like below:
|
||||
# 0 1 2 {{schema:0 sum:1 count:1}} 3 {{schema:0 sum:2 count:2}} 4 {{schema:0 sum:3 count:3}}.
|
||||
eval instant at 50m changes(http_requests_histogram[5m])
|
||||
|
||||
eval instant at 50m changes(http_requests_histogram[6m])
|
||||
{path="/foo"} 0
|
||||
{path="/bar"} 0
|
||||
{path="/biz"} 0
|
||||
|
||||
eval instant at 50m changes(http_requests_histogram[60m])
|
||||
{path="/foo"} 0
|
||||
{path="/bar"} 1
|
||||
{path="/biz"} 9
|
||||
|
||||
clear
|
||||
|
||||
load 5m
|
||||
|
@ -83,13 +113,13 @@ clear
|
|||
|
||||
# Tests for increase().
|
||||
load 5m
|
||||
http_requests{path="/foo"} 0+10x10
|
||||
http_requests{path="/bar"} 0+18x5 0+18x5
|
||||
http_requests{path="/dings"} 10+10x10
|
||||
http_requests{path="/bumms"} 1+10x10
|
||||
http_requests_total{path="/foo"} 0+10x10
|
||||
http_requests_total{path="/bar"} 0+18x5 0+18x5
|
||||
http_requests_total{path="/dings"} 10+10x10
|
||||
http_requests_total{path="/bumms"} 1+10x10
|
||||
|
||||
# Tests for increase().
|
||||
eval instant at 50m increase(http_requests[50m])
|
||||
eval instant at 50m increase(http_requests_total[50m])
|
||||
{path="/foo"} 100
|
||||
{path="/bar"} 160
|
||||
{path="/dings"} 100
|
||||
|
@ -102,7 +132,7 @@ eval instant at 50m increase(http_requests[50m])
|
|||
# chosen. However, "bumms" has value 1 at t=0 and would reach 0 at
|
||||
# t=-30s. Here the extrapolation to t=-2m30s would reach a negative
|
||||
# value, and therefore the extrapolation happens only by 30s.
|
||||
eval instant at 50m increase(http_requests[100m])
|
||||
eval instant at 50m increase(http_requests_total[100m])
|
||||
{path="/foo"} 100
|
||||
{path="/bar"} 162
|
||||
{path="/dings"} 105
|
||||
|
@ -115,57 +145,57 @@ clear
|
|||
# So the sequence 3 2 (decreasing counter = reset) is interpreted the same as 3 0 1 2.
|
||||
# Prometheus assumes it missed the intermediate values 0 and 1.
|
||||
load 5m
|
||||
http_requests{path="/foo"} 0 1 2 3 2 3 4
|
||||
http_requests_total{path="/foo"} 0 1 2 3 2 3 4
|
||||
|
||||
eval instant at 30m increase(http_requests[30m])
|
||||
eval instant at 30m increase(http_requests_total[30m])
|
||||
{path="/foo"} 7
|
||||
|
||||
clear
|
||||
|
||||
# Tests for rate().
|
||||
load 5m
|
||||
testcounter_reset_middle 0+27x4 0+27x5
|
||||
testcounter_reset_end 0+10x9 0 10
|
||||
testcounter_reset_middle_total 0+27x4 0+27x5
|
||||
testcounter_reset_end_total 0+10x9 0 10
|
||||
|
||||
# Counter resets at in the middle of range are handled correctly by rate().
|
||||
eval instant at 50m rate(testcounter_reset_middle[50m])
|
||||
eval instant at 50m rate(testcounter_reset_middle_total[50m])
|
||||
{} 0.08
|
||||
|
||||
# Counter resets at end of range are ignored by rate().
|
||||
eval instant at 50m rate(testcounter_reset_end[5m])
|
||||
eval instant at 50m rate(testcounter_reset_end_total[5m])
|
||||
|
||||
eval instant at 50m rate(testcounter_reset_end[6m])
|
||||
eval instant at 50m rate(testcounter_reset_end_total[6m])
|
||||
{} 0
|
||||
|
||||
clear
|
||||
|
||||
load 5m
|
||||
calculate_rate_offset{x="a"} 0+10x10
|
||||
calculate_rate_offset{x="b"} 0+20x10
|
||||
calculate_rate_window 0+80x10
|
||||
calculate_rate_offset_total{x="a"} 0+10x10
|
||||
calculate_rate_offset_total{x="b"} 0+20x10
|
||||
calculate_rate_window_total 0+80x10
|
||||
|
||||
# Rates should calculate per-second rates.
|
||||
eval instant at 50m rate(calculate_rate_window[50m])
|
||||
eval instant at 50m rate(calculate_rate_window_total[50m])
|
||||
{} 0.26666666666666666
|
||||
|
||||
eval instant at 50m rate(calculate_rate_offset[10m] offset 5m)
|
||||
eval instant at 50m rate(calculate_rate_offset_total[10m] offset 5m)
|
||||
{x="a"} 0.03333333333333333
|
||||
{x="b"} 0.06666666666666667
|
||||
|
||||
clear
|
||||
|
||||
load 4m
|
||||
testcounter_zero_cutoff{start="0m"} 0+240x10
|
||||
testcounter_zero_cutoff{start="1m"} 60+240x10
|
||||
testcounter_zero_cutoff{start="2m"} 120+240x10
|
||||
testcounter_zero_cutoff{start="3m"} 180+240x10
|
||||
testcounter_zero_cutoff{start="4m"} 240+240x10
|
||||
testcounter_zero_cutoff{start="5m"} 300+240x10
|
||||
testcounter_zero_cutoff_total{start="0m"} 0+240x10
|
||||
testcounter_zero_cutoff_total{start="1m"} 60+240x10
|
||||
testcounter_zero_cutoff_total{start="2m"} 120+240x10
|
||||
testcounter_zero_cutoff_total{start="3m"} 180+240x10
|
||||
testcounter_zero_cutoff_total{start="4m"} 240+240x10
|
||||
testcounter_zero_cutoff_total{start="5m"} 300+240x10
|
||||
|
||||
# Zero cutoff for left-side extrapolation happens until we
|
||||
# reach half a sampling interval (2m). Beyond that, we only
|
||||
# extrapolate by half a sampling interval.
|
||||
eval instant at 10m rate(testcounter_zero_cutoff[20m])
|
||||
eval instant at 10m rate(testcounter_zero_cutoff_total[20m])
|
||||
{start="0m"} 0.5
|
||||
{start="1m"} 0.55
|
||||
{start="2m"} 0.6
|
||||
|
@ -174,7 +204,7 @@ eval instant at 10m rate(testcounter_zero_cutoff[20m])
|
|||
{start="5m"} 0.6
|
||||
|
||||
# Normal half-interval cutoff for left-side extrapolation.
|
||||
eval instant at 50m rate(testcounter_zero_cutoff[20m])
|
||||
eval instant at 50m rate(testcounter_zero_cutoff_total[20m])
|
||||
{start="0m"} 0.6
|
||||
{start="1m"} 0.6
|
||||
{start="2m"} 0.6
|
||||
|
@ -186,15 +216,15 @@ clear
|
|||
|
||||
# Tests for irate().
|
||||
load 5m
|
||||
http_requests{path="/foo"} 0+10x10
|
||||
http_requests{path="/bar"} 0+10x5 0+10x5
|
||||
http_requests_total{path="/foo"} 0+10x10
|
||||
http_requests_total{path="/bar"} 0+10x5 0+10x5
|
||||
|
||||
eval instant at 50m irate(http_requests[50m])
|
||||
eval instant at 50m irate(http_requests_total[50m])
|
||||
{path="/foo"} .03333333333333333333
|
||||
{path="/bar"} .03333333333333333333
|
||||
|
||||
# Counter reset.
|
||||
eval instant at 30m irate(http_requests[50m])
|
||||
eval instant at 30m irate(http_requests_total[50m])
|
||||
{path="/foo"} .03333333333333333333
|
||||
{path="/bar"} 0
|
||||
|
||||
|
@ -224,20 +254,33 @@ clear
|
|||
|
||||
# Tests for deriv() and predict_linear().
|
||||
load 5m
|
||||
testcounter_reset_middle 0+10x4 0+10x5
|
||||
http_requests{job="app-server", instance="1", group="canary"} 0+80x10
|
||||
testcounter_reset_middle_total 0+10x4 0+10x5
|
||||
http_requests_total{job="app-server", instance="1", group="canary"} 0+80x10
|
||||
testcounter_reset_middle_mix 0+10x4 0+10x5 {{schema:0 sum:1 count:1}} {{schema:1 sum:2 count:2}}
|
||||
http_requests_mix{job="app-server", instance="1", group="canary"} 0+80x10 {{schema:0 sum:1 count:1}}
|
||||
http_requests_histogram{job="app-server", instance="1", group="canary"} {{schema:0 sum:1 count:2}}x10
|
||||
|
||||
# deriv should return the same as rate in simple cases.
|
||||
eval instant at 50m rate(http_requests{group="canary", instance="1", job="app-server"}[50m])
|
||||
eval instant at 50m rate(http_requests_total{group="canary", instance="1", job="app-server"}[50m])
|
||||
{group="canary", instance="1", job="app-server"} 0.26666666666666666
|
||||
|
||||
eval instant at 50m deriv(http_requests{group="canary", instance="1", job="app-server"}[50m])
|
||||
eval instant at 50m deriv(http_requests_total{group="canary", instance="1", job="app-server"}[50m])
|
||||
{group="canary", instance="1", job="app-server"} 0.26666666666666666
|
||||
|
||||
# deriv should return correct result.
|
||||
eval instant at 50m deriv(testcounter_reset_middle[100m])
|
||||
eval instant at 50m deriv(testcounter_reset_middle_total[100m])
|
||||
{} 0.010606060606060607
|
||||
|
||||
# deriv should ignore histograms.
|
||||
eval instant at 110m deriv(http_requests_mix{group="canary", instance="1", job="app-server"}[110m])
|
||||
{group="canary", instance="1", job="app-server"} 0.26666666666666666
|
||||
|
||||
eval instant at 100m deriv(testcounter_reset_middle_mix[110m])
|
||||
{} 0.010606060606060607
|
||||
|
||||
eval instant at 50m deriv(http_requests_histogram[60m])
|
||||
#empty
|
||||
|
||||
# predict_linear should return correct result.
|
||||
# X/s = [ 0, 300, 600, 900,1200,1500,1800,2100,2400,2700,3000]
|
||||
# Y = [ 0, 10, 20, 30, 40, 0, 10, 20, 30, 40, 50]
|
||||
|
@ -252,31 +295,31 @@ eval instant at 50m deriv(testcounter_reset_middle[100m])
|
|||
# intercept at t=0: 6.818181818181818
|
||||
# intercept at t=3000: 38.63636363636364
|
||||
# intercept at t=3000+3600: 76.81818181818181
|
||||
eval instant at 50m predict_linear(testcounter_reset_middle[50m], 3600)
|
||||
eval instant at 50m predict_linear(testcounter_reset_middle_total[50m], 3600)
|
||||
{} 70
|
||||
|
||||
eval instant at 50m predict_linear(testcounter_reset_middle[50m], 1h)
|
||||
eval instant at 50m predict_linear(testcounter_reset_middle_total[50m], 1h)
|
||||
{} 70
|
||||
|
||||
# intercept at t = 3000+3600 = 6600
|
||||
eval instant at 50m predict_linear(testcounter_reset_middle[55m] @ 3000, 3600)
|
||||
eval instant at 50m predict_linear(testcounter_reset_middle_total[55m] @ 3000, 3600)
|
||||
{} 76.81818181818181
|
||||
|
||||
eval instant at 50m predict_linear(testcounter_reset_middle[55m] @ 3000, 1h)
|
||||
eval instant at 50m predict_linear(testcounter_reset_middle_total[55m] @ 3000, 1h)
|
||||
{} 76.81818181818181
|
||||
|
||||
# intercept at t = 600+3600 = 4200
|
||||
eval instant at 10m predict_linear(testcounter_reset_middle[55m] @ 3000, 3600)
|
||||
eval instant at 10m predict_linear(testcounter_reset_middle_total[55m] @ 3000, 3600)
|
||||
{} 51.36363636363637
|
||||
|
||||
# intercept at t = 4200+3600 = 7800
|
||||
eval instant at 70m predict_linear(testcounter_reset_middle[55m] @ 3000, 3600)
|
||||
eval instant at 70m predict_linear(testcounter_reset_middle_total[55m] @ 3000, 3600)
|
||||
{} 89.54545454545455
|
||||
|
||||
# With http_requests, there is a sample value exactly at the end of
|
||||
# With http_requests_total, there is a sample value exactly at the end of
|
||||
# the range, and it has exactly the predicted value, so predict_linear
|
||||
# can be emulated with deriv.
|
||||
eval instant at 50m predict_linear(http_requests[50m], 3600) - (http_requests + deriv(http_requests[50m]) * 3600)
|
||||
eval instant at 50m predict_linear(http_requests_total[50m], 3600) - (http_requests_total + deriv(http_requests_total[50m]) * 3600)
|
||||
{group="canary", instance="1", job="app-server"} 0
|
||||
|
||||
clear
|
||||
|
@ -476,6 +519,7 @@ load 5m
|
|||
test_sgn{src="sgn-d"} -50
|
||||
test_sgn{src="sgn-e"} 0
|
||||
test_sgn{src="sgn-f"} 100
|
||||
test_sgn{src="sgn-histogram"} {{schema:1 sum:1 count:1}}
|
||||
|
||||
eval instant at 0m sgn(test_sgn)
|
||||
{src="sgn-a"} -1
|
||||
|
@ -713,6 +757,7 @@ load 10s
|
|||
metric8 9.988465674311579e+307 9.988465674311579e+307
|
||||
metric9 -9.988465674311579e+307 -9.988465674311579e+307 -9.988465674311579e+307
|
||||
metric10 -9.988465674311579e+307 9.988465674311579e+307
|
||||
metric11 1 2 3 NaN NaN
|
||||
|
||||
eval instant at 55s avg_over_time(metric[1m])
|
||||
{} 3
|
||||
|
@ -806,6 +851,19 @@ eval instant at 45s sum_over_time(metric10[1m])/count_over_time(metric10[1m])
|
|||
eval instant at 1m sum_over_time(metric10[2m])/count_over_time(metric10[2m])
|
||||
{} 0
|
||||
|
||||
# NaN behavior.
|
||||
eval instant at 20s avg_over_time(metric11[1m])
|
||||
{} 2
|
||||
|
||||
eval instant at 30s avg_over_time(metric11[1m])
|
||||
{} NaN
|
||||
|
||||
eval instant at 1m avg_over_time(metric11[1m])
|
||||
{} NaN
|
||||
|
||||
eval instant at 1m sum_over_time(metric11[1m])/count_over_time(metric11[1m])
|
||||
{} NaN
|
||||
|
||||
# Test if very big intermediate values cause loss of detail.
|
||||
clear
|
||||
load 10s
|
||||
|
@ -901,6 +959,9 @@ eval_warn instant at 1m (quantile_over_time(2, (data[2m])))
|
|||
clear
|
||||
|
||||
# Test time-related functions.
|
||||
load 5m
|
||||
histogram_sample {{schema:0 sum:1 count:1}}
|
||||
|
||||
eval instant at 0m year()
|
||||
{} 1970
|
||||
|
||||
|
@ -982,6 +1043,23 @@ eval instant at 0m days_in_month(vector(1454284800))
|
|||
eval instant at 0m days_in_month(vector(1485907200))
|
||||
{} 28
|
||||
|
||||
# Test for histograms.
|
||||
eval instant at 0m day_of_month(histogram_sample)
|
||||
|
||||
eval instant at 0m day_of_week(histogram_sample)
|
||||
|
||||
eval instant at 0m day_of_year(histogram_sample)
|
||||
|
||||
eval instant at 0m days_in_month(histogram_sample)
|
||||
|
||||
eval instant at 0m hour(histogram_sample)
|
||||
|
||||
eval instant at 0m minute(histogram_sample)
|
||||
|
||||
eval instant at 0m month(histogram_sample)
|
||||
|
||||
eval instant at 0m year(histogram_sample)
|
||||
|
||||
clear
|
||||
|
||||
# Test duplicate labelset in promql output.
|
||||
|
@ -1045,11 +1123,16 @@ clear
|
|||
# Don't return anything when there's something there.
|
||||
load 5m
|
||||
http_requests{job="api-server", instance="0", group="production"} 0+10x10
|
||||
http_requests_histogram{job="api-server", instance="0", group="production"} {{schema:0 sum:1 count:1}}x11
|
||||
|
||||
eval instant at 50m absent(http_requests)
|
||||
|
||||
eval instant at 50m absent(sum(http_requests))
|
||||
|
||||
eval instant at 50m absent(http_requests_histogram)
|
||||
|
||||
eval instant at 50m absent(sum(http_requests_histogram))
|
||||
|
||||
clear
|
||||
|
||||
eval instant at 50m absent(sum(nonexistent{job="testjob", instance="testinstance"}))
|
||||
|
@ -1073,49 +1156,50 @@ eval instant at 50m absent(rate(nonexistant[5m]))
|
|||
clear
|
||||
|
||||
# Testdata for absent_over_time()
|
||||
eval instant at 1m absent_over_time(http_requests[5m])
|
||||
eval instant at 1m absent_over_time(http_requests_total[5m])
|
||||
{} 1
|
||||
|
||||
eval instant at 1m absent_over_time(http_requests{handler="/foo"}[5m])
|
||||
eval instant at 1m absent_over_time(http_requests_total{handler="/foo"}[5m])
|
||||
{handler="/foo"} 1
|
||||
|
||||
eval instant at 1m absent_over_time(http_requests{handler!="/foo"}[5m])
|
||||
eval instant at 1m absent_over_time(http_requests_total{handler!="/foo"}[5m])
|
||||
{} 1
|
||||
|
||||
eval instant at 1m absent_over_time(http_requests{handler="/foo", handler="/bar", handler="/foobar"}[5m])
|
||||
eval instant at 1m absent_over_time(http_requests_total{handler="/foo", handler="/bar", handler="/foobar"}[5m])
|
||||
{} 1
|
||||
|
||||
eval instant at 1m absent_over_time(rate(nonexistant[5m])[5m:])
|
||||
{} 1
|
||||
|
||||
eval instant at 1m absent_over_time(http_requests{handler="/foo", handler="/bar", instance="127.0.0.1"}[5m])
|
||||
eval instant at 1m absent_over_time(http_requests_total{handler="/foo", handler="/bar", instance="127.0.0.1"}[5m])
|
||||
{instance="127.0.0.1"} 1
|
||||
|
||||
load 1m
|
||||
http_requests{path="/foo",instance="127.0.0.1",job="httpd"} 1+1x10
|
||||
http_requests{path="/bar",instance="127.0.0.1",job="httpd"} 1+1x10
|
||||
http_requests_total{path="/foo",instance="127.0.0.1",job="httpd"} 1+1x10
|
||||
http_requests_total{path="/bar",instance="127.0.0.1",job="httpd"} 1+1x10
|
||||
httpd_handshake_failures_total{instance="127.0.0.1",job="node"} 1+1x15
|
||||
httpd_log_lines_total{instance="127.0.0.1",job="node"} 1
|
||||
ssl_certificate_expiry_seconds{job="ingress"} NaN NaN NaN NaN NaN
|
||||
http_requests_histogram{path="/foo",instance="127.0.0.1",job="httpd"} {{schema:0 sum:1 count:1}}x11
|
||||
|
||||
eval instant at 5m absent_over_time(http_requests[5m])
|
||||
eval instant at 5m absent_over_time(http_requests_total[5m])
|
||||
|
||||
eval instant at 5m absent_over_time(rate(http_requests[5m])[5m:1m])
|
||||
eval instant at 5m absent_over_time(rate(http_requests_total[5m])[5m:1m])
|
||||
|
||||
eval instant at 0m absent_over_time(httpd_log_lines_total[30s])
|
||||
|
||||
eval instant at 1m absent_over_time(httpd_log_lines_total[30s])
|
||||
{} 1
|
||||
|
||||
eval instant at 15m absent_over_time(http_requests[5m])
|
||||
eval instant at 15m absent_over_time(http_requests_total[5m])
|
||||
{} 1
|
||||
|
||||
eval instant at 15m absent_over_time(http_requests[10m])
|
||||
eval instant at 15m absent_over_time(http_requests_total[10m])
|
||||
|
||||
eval instant at 16m absent_over_time(http_requests[6m])
|
||||
eval instant at 16m absent_over_time(http_requests_total[6m])
|
||||
{} 1
|
||||
|
||||
eval instant at 16m absent_over_time(http_requests[16m])
|
||||
eval instant at 16m absent_over_time(http_requests_total[16m])
|
||||
|
||||
eval instant at 16m absent_over_time(httpd_handshake_failures_total[1m])
|
||||
{} 1
|
||||
|
@ -1140,33 +1224,43 @@ eval instant at 5m absent_over_time({job="ingress"}[4m])
|
|||
eval instant at 10m absent_over_time({job="ingress"}[4m])
|
||||
{job="ingress"} 1
|
||||
|
||||
eval instant at 10m absent_over_time(http_requests_histogram[5m])
|
||||
|
||||
eval instant at 10m absent_over_time(rate(http_requests_histogram[5m])[5m:1m])
|
||||
|
||||
eval instant at 20m absent_over_time(http_requests_histogram[5m])
|
||||
{} 1
|
||||
|
||||
eval instant at 20m absent_over_time(rate(http_requests_histogram[5m])[5m:1m])
|
||||
{} 1
|
||||
|
||||
clear
|
||||
|
||||
# Testdata for present_over_time()
|
||||
eval instant at 1m present_over_time(http_requests[5m])
|
||||
eval instant at 1m present_over_time(http_requests_total[5m])
|
||||
|
||||
eval instant at 1m present_over_time(http_requests{handler="/foo"}[5m])
|
||||
eval instant at 1m present_over_time(http_requests_total{handler="/foo"}[5m])
|
||||
|
||||
eval instant at 1m present_over_time(http_requests{handler!="/foo"}[5m])
|
||||
eval instant at 1m present_over_time(http_requests_total{handler!="/foo"}[5m])
|
||||
|
||||
eval instant at 1m present_over_time(http_requests{handler="/foo", handler="/bar", handler="/foobar"}[5m])
|
||||
eval instant at 1m present_over_time(http_requests_total{handler="/foo", handler="/bar", handler="/foobar"}[5m])
|
||||
|
||||
eval instant at 1m present_over_time(rate(nonexistant[5m])[5m:])
|
||||
|
||||
eval instant at 1m present_over_time(http_requests{handler="/foo", handler="/bar", instance="127.0.0.1"}[5m])
|
||||
eval instant at 1m present_over_time(http_requests_total{handler="/foo", handler="/bar", instance="127.0.0.1"}[5m])
|
||||
|
||||
load 1m
|
||||
http_requests{path="/foo",instance="127.0.0.1",job="httpd"} 1+1x10
|
||||
http_requests{path="/bar",instance="127.0.0.1",job="httpd"} 1+1x10
|
||||
http_requests_total{path="/foo",instance="127.0.0.1",job="httpd"} 1+1x10
|
||||
http_requests_total{path="/bar",instance="127.0.0.1",job="httpd"} 1+1x10
|
||||
httpd_handshake_failures_total{instance="127.0.0.1",job="node"} 1+1x15
|
||||
httpd_log_lines_total{instance="127.0.0.1",job="node"} 1
|
||||
ssl_certificate_expiry_seconds{job="ingress"} NaN NaN NaN NaN NaN
|
||||
|
||||
eval instant at 5m present_over_time(http_requests[5m])
|
||||
eval instant at 5m present_over_time(http_requests_total[5m])
|
||||
{instance="127.0.0.1", job="httpd", path="/bar"} 1
|
||||
{instance="127.0.0.1", job="httpd", path="/foo"} 1
|
||||
|
||||
eval instant at 5m present_over_time(rate(http_requests[5m])[5m:1m])
|
||||
eval instant at 5m present_over_time(rate(http_requests_total[5m])[5m:1m])
|
||||
{instance="127.0.0.1", job="httpd", path="/bar"} 1
|
||||
{instance="127.0.0.1", job="httpd", path="/foo"} 1
|
||||
|
||||
|
@ -1175,15 +1269,15 @@ eval instant at 0m present_over_time(httpd_log_lines_total[30s])
|
|||
|
||||
eval instant at 1m present_over_time(httpd_log_lines_total[30s])
|
||||
|
||||
eval instant at 15m present_over_time(http_requests[5m])
|
||||
eval instant at 15m present_over_time(http_requests_total[5m])
|
||||
|
||||
eval instant at 15m present_over_time(http_requests[10m])
|
||||
eval instant at 15m present_over_time(http_requests_total[10m])
|
||||
{instance="127.0.0.1", job="httpd", path="/bar"} 1
|
||||
{instance="127.0.0.1", job="httpd", path="/foo"} 1
|
||||
|
||||
eval instant at 16m present_over_time(http_requests[6m])
|
||||
eval instant at 16m present_over_time(http_requests_total[6m])
|
||||
|
||||
eval instant at 16m present_over_time(http_requests[16m])
|
||||
eval instant at 16m present_over_time(http_requests_total[16m])
|
||||
{instance="127.0.0.1", job="httpd", path="/bar"} 1
|
||||
{instance="127.0.0.1", job="httpd", path="/foo"} 1
|
||||
|
||||
|
@ -1207,11 +1301,16 @@ clear
|
|||
load 5m
|
||||
exp_root_log{l="x"} 10
|
||||
exp_root_log{l="y"} 20
|
||||
exp_root_log_h{l="z"} {{schema:1 sum:1 count:1}}
|
||||
|
||||
eval instant at 1m exp(exp_root_log)
|
||||
{l="x"} 22026.465794806718
|
||||
{l="y"} 485165195.4097903
|
||||
|
||||
eval instant at 1m exp({__name__=~"exp_root_log(_h)?"})
|
||||
{l="x"} 22026.465794806718
|
||||
{l="y"} 485165195.4097903
|
||||
|
||||
eval instant at 1m exp(exp_root_log - 10)
|
||||
{l="y"} 22026.465794806718
|
||||
{l="x"} 1
|
||||
|
@ -1224,6 +1323,10 @@ eval instant at 1m ln(exp_root_log)
|
|||
{l="x"} 2.302585092994046
|
||||
{l="y"} 2.995732273553991
|
||||
|
||||
eval instant at 1m ln({__name__=~"exp_root_log(_h)?"})
|
||||
{l="x"} 2.302585092994046
|
||||
{l="y"} 2.995732273553991
|
||||
|
||||
eval instant at 1m ln(exp_root_log - 10)
|
||||
{l="y"} 2.302585092994046
|
||||
{l="x"} -Inf
|
||||
|
@ -1236,14 +1339,26 @@ eval instant at 1m exp(ln(exp_root_log))
|
|||
{l="y"} 20
|
||||
{l="x"} 10
|
||||
|
||||
eval instant at 1m exp(ln({__name__=~"exp_root_log(_h)?"}))
|
||||
{l="y"} 20
|
||||
{l="x"} 10
|
||||
|
||||
eval instant at 1m sqrt(exp_root_log)
|
||||
{l="x"} 3.1622776601683795
|
||||
{l="y"} 4.47213595499958
|
||||
|
||||
eval instant at 1m sqrt({__name__=~"exp_root_log(_h)?"})
|
||||
{l="x"} 3.1622776601683795
|
||||
{l="y"} 4.47213595499958
|
||||
|
||||
eval instant at 1m log2(exp_root_log)
|
||||
{l="x"} 3.3219280948873626
|
||||
{l="y"} 4.321928094887363
|
||||
|
||||
eval instant at 1m log2({__name__=~"exp_root_log(_h)?"})
|
||||
{l="x"} 3.3219280948873626
|
||||
{l="y"} 4.321928094887363
|
||||
|
||||
eval instant at 1m log2(exp_root_log - 10)
|
||||
{l="y"} 3.3219280948873626
|
||||
{l="x"} -Inf
|
||||
|
@ -1256,6 +1371,10 @@ eval instant at 1m log10(exp_root_log)
|
|||
{l="x"} 1
|
||||
{l="y"} 1.301029995663981
|
||||
|
||||
eval instant at 1m log10({__name__=~"exp_root_log(_h)?"})
|
||||
{l="x"} 1
|
||||
{l="y"} 1.301029995663981
|
||||
|
||||
eval instant at 1m log10(exp_root_log - 10)
|
||||
{l="y"} 1
|
||||
{l="x"} -Inf
|
||||
|
|
8
promql/promqltest/testdata/histograms.test
vendored
8
promql/promqltest/testdata/histograms.test
vendored
|
@ -452,14 +452,14 @@ load 5m
|
|||
nonmonotonic_bucket{le="1000"} 0+9x10
|
||||
nonmonotonic_bucket{le="+Inf"} 0+8x10
|
||||
|
||||
# Nonmonotonic buckets
|
||||
eval instant at 50m histogram_quantile(0.01, nonmonotonic_bucket)
|
||||
# Nonmonotonic buckets, triggering an info annotation.
|
||||
eval_info instant at 50m histogram_quantile(0.01, nonmonotonic_bucket)
|
||||
{} 0.0045
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, nonmonotonic_bucket)
|
||||
eval_info instant at 50m histogram_quantile(0.5, nonmonotonic_bucket)
|
||||
{} 8.5
|
||||
|
||||
eval instant at 50m histogram_quantile(0.99, nonmonotonic_bucket)
|
||||
eval_info instant at 50m histogram_quantile(0.99, nonmonotonic_bucket)
|
||||
{} 979.75
|
||||
|
||||
# Buckets with different representations of the same upper bound.
|
||||
|
|
111
promql/promqltest/testdata/limit.test
vendored
111
promql/promqltest/testdata/limit.test
vendored
|
@ -9,6 +9,8 @@ load 5m
|
|||
http_requests{job="api-server", instance="1", group="canary"} 0+40x10
|
||||
http_requests{job="api-server", instance="2", group="canary"} 0+50x10
|
||||
http_requests{job="api-server", instance="3", group="canary"} 0+60x10
|
||||
http_requests{job="api-server", instance="histogram_1", group="canary"} {{schema:0 sum:10 count:10}}x11
|
||||
http_requests{job="api-server", instance="histogram_2", group="canary"} {{schema:0 sum:20 count:20}}x11
|
||||
|
||||
eval instant at 50m count(limitk by (group) (0, http_requests))
|
||||
# empty
|
||||
|
@ -16,25 +18,56 @@ eval instant at 50m count(limitk by (group) (0, http_requests))
|
|||
eval instant at 50m count(limitk by (group) (-1, http_requests))
|
||||
# empty
|
||||
|
||||
# Exercise k==1 special case (as sample is added before the main series loop
|
||||
# Exercise k==1 special case (as sample is added before the main series loop).
|
||||
eval instant at 50m count(limitk by (group) (1, http_requests) and http_requests)
|
||||
{} 2
|
||||
{} 2
|
||||
|
||||
eval instant at 50m count(limitk by (group) (2, http_requests) and http_requests)
|
||||
{} 4
|
||||
{} 4
|
||||
|
||||
eval instant at 50m count(limitk(100, http_requests) and http_requests)
|
||||
{} 6
|
||||
{} 8
|
||||
|
||||
# Exercise k==1 special case (as sample is added before the main series loop
|
||||
# Exercise k==1 special case (as sample is added before the main series loop).
|
||||
eval instant at 50m count(limitk by (group) (1, http_requests) and http_requests)
|
||||
{} 2
|
||||
{} 2
|
||||
|
||||
eval instant at 50m count(limitk by (group) (2, http_requests) and http_requests)
|
||||
{} 4
|
||||
{} 4
|
||||
|
||||
eval instant at 50m count(limitk(100, http_requests) and http_requests)
|
||||
{} 6
|
||||
{} 8
|
||||
|
||||
# Test for histograms.
|
||||
# k==1: verify that histogram is included in the result.
|
||||
eval instant at 50m limitk(1, http_requests{instance="histogram_1"})
|
||||
{__name__="http_requests", group="canary", instance="histogram_1", job="api-server"} {{count:10 sum:10}}
|
||||
|
||||
eval range from 0 to 50m step 5m limitk(1, http_requests{instance="histogram_1"})
|
||||
{__name__="http_requests", group="canary", instance="histogram_1", job="api-server"} {{count:10 sum:10}}x10
|
||||
|
||||
# Histogram is included with mix of floats as well.
|
||||
eval instant at 50m limitk(8, http_requests{instance=~"(histogram_2|0)"})
|
||||
{__name__="http_requests", group="canary", instance="histogram_2", job="api-server"} {{count:20 sum:20}}
|
||||
{__name__="http_requests", group="production", instance="0", job="api-server"} 100
|
||||
{__name__="http_requests", group="canary", instance="0", job="api-server"} 300
|
||||
|
||||
eval range from 0 to 50m step 5m limitk(8, http_requests{instance=~"(histogram_2|0)"})
|
||||
{__name__="http_requests", group="canary", instance="histogram_2", job="api-server"} {{count:20 sum:20}}x10
|
||||
{__name__="http_requests", group="production", instance="0", job="api-server"} 0+10x10
|
||||
{__name__="http_requests", group="canary", instance="0", job="api-server"} 0+30x10
|
||||
|
||||
eval instant at 50m count(limitk(2, http_requests{instance=~"histogram_[0-9]"}))
|
||||
{} 2
|
||||
|
||||
eval range from 0 to 50m step 5m count(limitk(2, http_requests{instance=~"histogram_[0-9]"}))
|
||||
{} 2+0x10
|
||||
|
||||
eval instant at 50m count(limitk(1000, http_requests{instance=~"histogram_[0-9]"}))
|
||||
{} 2
|
||||
|
||||
eval range from 0 to 50m step 5m count(limitk(1000, http_requests{instance=~"histogram_[0-9]"}))
|
||||
{} 2+0x10
|
||||
|
||||
# limit_ratio
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(0.0, http_requests))
|
||||
|
@ -42,9 +75,9 @@ eval range from 0 to 50m step 5m count(limit_ratio(0.0, http_requests))
|
|||
|
||||
# limitk(2, ...) should always return a 2-count subset of the timeseries (hence the AND'ing)
|
||||
eval range from 0 to 50m step 5m count(limitk(2, http_requests) and http_requests)
|
||||
{} 2+0x10
|
||||
{} 2+0x10
|
||||
|
||||
# Tests for limit_ratio
|
||||
# Tests for limit_ratio.
|
||||
#
|
||||
# NB: below 0.5 ratio will depend on some hashing "luck" (also there's no guarantee that
|
||||
# an integer comes from: total number of series * ratio), as it depends on:
|
||||
|
@ -56,50 +89,50 @@ eval range from 0 to 50m step 5m count(limitk(2, http_requests) and http_request
|
|||
#
|
||||
# See `AddRatioSample()` in promql/engine.go for more details.
|
||||
|
||||
# Half~ish samples: verify we get "near" 3 (of 0.5 * 6)
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(0.5, http_requests) and http_requests) <= bool (3+1)
|
||||
{} 1+0x10
|
||||
# Half~ish samples: verify we get "near" 3 (of 0.5 * 6).
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(0.5, http_requests) and http_requests) <= bool (4+1)
|
||||
{} 1+0x10
|
||||
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(0.5, http_requests) and http_requests) >= bool (3-1)
|
||||
{} 1+0x10
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(0.5, http_requests) and http_requests) >= bool (4-1)
|
||||
{} 1+0x10
|
||||
|
||||
# All samples
|
||||
# All samples.
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(1.0, http_requests) and http_requests)
|
||||
{} 6+0x10
|
||||
{} 8+0x10
|
||||
|
||||
# All samples
|
||||
# All samples.
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(-1.0, http_requests) and http_requests)
|
||||
{} 6+0x10
|
||||
{} 8+0x10
|
||||
|
||||
# Capped to 1.0 -> all samples
|
||||
# Capped to 1.0 -> all samples.
|
||||
eval_warn range from 0 to 50m step 5m count(limit_ratio(1.1, http_requests) and http_requests)
|
||||
{} 6+0x10
|
||||
{} 8+0x10
|
||||
|
||||
# Capped to -1.0 -> all samples
|
||||
# Capped to -1.0 -> all samples.
|
||||
eval_warn range from 0 to 50m step 5m count(limit_ratio(-1.1, http_requests) and http_requests)
|
||||
{} 6+0x10
|
||||
{} 8+0x10
|
||||
|
||||
# Verify that limit_ratio(value) and limit_ratio(1.0-value) return the "complement" of each other
|
||||
# Complement below for [0.2, -0.8]
|
||||
# Verify that limit_ratio(value) and limit_ratio(1.0-value) return the "complement" of each other.
|
||||
# Complement below for [0.2, -0.8].
|
||||
#
|
||||
# Complement 1of2: `or` should return all samples
|
||||
# Complement 1of2: `or` should return all samples.
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(0.2, http_requests) or limit_ratio(-0.8, http_requests))
|
||||
{} 6+0x10
|
||||
{} 8+0x10
|
||||
|
||||
# Complement 2of2: `and` should return no samples
|
||||
# Complement 2of2: `and` should return no samples.
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(0.2, http_requests) and limit_ratio(-0.8, http_requests))
|
||||
# empty
|
||||
|
||||
# Complement below for [0.5, -0.5]
|
||||
# Complement below for [0.5, -0.5].
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(0.5, http_requests) or limit_ratio(-0.5, http_requests))
|
||||
{} 6+0x10
|
||||
{} 8+0x10
|
||||
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(0.5, http_requests) and limit_ratio(-0.5, http_requests))
|
||||
# empty
|
||||
|
||||
# Complement below for [0.8, -0.2]
|
||||
# Complement below for [0.8, -0.2].
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(0.8, http_requests) or limit_ratio(-0.2, http_requests))
|
||||
{} 6+0x10
|
||||
{} 8+0x10
|
||||
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(0.8, http_requests) and limit_ratio(-0.2, http_requests))
|
||||
# empty
|
||||
|
@ -107,13 +140,19 @@ eval range from 0 to 50m step 5m count(limit_ratio(0.8, http_requests) and limit
|
|||
# Complement below for [some_ratio, 1.0 - some_ratio], some_ratio derived from time(),
|
||||
# using a small prime number to avoid rounded ratio values, and a small set of them.
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(time() % 17/17, http_requests) or limit_ratio(1.0 - (time() % 17/17), http_requests))
|
||||
{} 6+0x10
|
||||
{} 8+0x10
|
||||
|
||||
eval range from 0 to 50m step 5m count(limit_ratio(time() % 17/17, http_requests) and limit_ratio(1.0 - (time() % 17/17), http_requests))
|
||||
# empty
|
||||
|
||||
# Poor man's normality check: ok (loaded samples follow a nice linearity over labels and time)
|
||||
# The check giving: 1 (i.e. true)
|
||||
eval range from 0 to 50m step 5m abs(avg(limit_ratio(0.5, http_requests)) - avg(limit_ratio(-0.5, http_requests))) <= bool stddev(http_requests)
|
||||
# Poor man's normality check: ok (loaded samples follow a nice linearity over labels and time).
|
||||
# The check giving: 1 (i.e. true).
|
||||
eval range from 0 to 50m step 5m abs(avg(limit_ratio(0.5, http_requests{instance!~"histogram_[0-9]"})) - avg(limit_ratio(-0.5, http_requests{instance!~"histogram_[0-9]"}))) <= bool stddev(http_requests{instance!~"histogram_[0-9]"})
|
||||
{} 1+0x10
|
||||
|
||||
# All specified histograms are included for r=1.
|
||||
eval instant at 50m limit_ratio(1, http_requests{instance="histogram_1"})
|
||||
{__name__="http_requests", group="canary", instance="histogram_1", job="api-server"} {{count:10 sum:10}}
|
||||
|
||||
eval range from 0 to 50m step 5m limit_ratio(1, http_requests{instance="histogram_1"})
|
||||
{__name__="http_requests", group="canary", instance="histogram_1", job="api-server"} {{count:10 sum:10}}x10
|
||||
|
|
|
@ -1,88 +1,88 @@
|
|||
# Test for __name__ label drop.
|
||||
load 5m
|
||||
metric{env="1"} 0 60 120
|
||||
another_metric{env="1"} 60 120 180
|
||||
metric_total{env="1"} 0 60 120
|
||||
another_metric_total{env="1"} 60 120 180
|
||||
|
||||
# Does not drop __name__ for vector selector
|
||||
eval instant at 10m metric{env="1"}
|
||||
metric{env="1"} 120
|
||||
# Does not drop __name__ for vector selector.
|
||||
eval instant at 10m metric_total{env="1"}
|
||||
metric_total{env="1"} 120
|
||||
|
||||
# Drops __name__ for unary operators
|
||||
eval instant at 10m -metric
|
||||
# Drops __name__ for unary operators.
|
||||
eval instant at 10m -metric_total
|
||||
{env="1"} -120
|
||||
|
||||
# Drops __name__ for binary operators
|
||||
eval instant at 10m metric + another_metric
|
||||
# Drops __name__ for binary operators.
|
||||
eval instant at 10m metric_total + another_metric_total
|
||||
{env="1"} 300
|
||||
|
||||
# Does not drop __name__ for binary comparison operators
|
||||
eval instant at 10m metric <= another_metric
|
||||
metric{env="1"} 120
|
||||
# Does not drop __name__ for binary comparison operators.
|
||||
eval instant at 10m metric_total <= another_metric_total
|
||||
metric_total{env="1"} 120
|
||||
|
||||
# Drops __name__ for binary comparison operators with "bool" modifier
|
||||
eval instant at 10m metric <= bool another_metric
|
||||
# Drops __name__ for binary comparison operators with "bool" modifier.
|
||||
eval instant at 10m metric_total <= bool another_metric_total
|
||||
{env="1"} 1
|
||||
|
||||
# Drops __name__ for vector-scalar operations
|
||||
eval instant at 10m metric * 2
|
||||
# Drops __name__ for vector-scalar operations.
|
||||
eval instant at 10m metric_total * 2
|
||||
{env="1"} 240
|
||||
|
||||
# Drops __name__ for instant-vector functions
|
||||
eval instant at 10m clamp(metric, 0, 100)
|
||||
# Drops __name__ for instant-vector functions.
|
||||
eval instant at 10m clamp(metric_total, 0, 100)
|
||||
{env="1"} 100
|
||||
|
||||
# Drops __name__ for round function
|
||||
eval instant at 10m round(metric)
|
||||
# Drops __name__ for round function.
|
||||
eval instant at 10m round(metric_total)
|
||||
{env="1"} 120
|
||||
|
||||
# Drops __name__ for range-vector functions
|
||||
eval instant at 10m rate(metric{env="1"}[10m])
|
||||
# Drops __name__ for range-vector functions.
|
||||
eval instant at 10m rate(metric_total{env="1"}[10m])
|
||||
{env="1"} 0.2
|
||||
|
||||
# Does not drop __name__ for last_over_time function
|
||||
eval instant at 10m last_over_time(metric{env="1"}[10m])
|
||||
metric{env="1"} 120
|
||||
# Does not drop __name__ for last_over_time function.
|
||||
eval instant at 10m last_over_time(metric_total{env="1"}[10m])
|
||||
metric_total{env="1"} 120
|
||||
|
||||
# Drops name for other _over_time functions
|
||||
eval instant at 10m max_over_time(metric{env="1"}[10m])
|
||||
# Drops name for other _over_time functions.
|
||||
eval instant at 10m max_over_time(metric_total{env="1"}[10m])
|
||||
{env="1"} 120
|
||||
|
||||
# Allows relabeling (to-be-dropped) __name__ via label_replace
|
||||
# Allows relabeling (to-be-dropped) __name__ via label_replace.
|
||||
eval instant at 10m label_replace(rate({env="1"}[10m]), "my_name", "rate_$1", "__name__", "(.+)")
|
||||
{my_name="rate_metric", env="1"} 0.2
|
||||
{my_name="rate_another_metric", env="1"} 0.2
|
||||
{my_name="rate_metric_total", env="1"} 0.2
|
||||
{my_name="rate_another_metric_total", env="1"} 0.2
|
||||
|
||||
# Allows preserving __name__ via label_replace
|
||||
# Allows preserving __name__ via label_replace.
|
||||
eval instant at 10m label_replace(rate({env="1"}[10m]), "__name__", "rate_$1", "__name__", "(.+)")
|
||||
rate_metric{env="1"} 0.2
|
||||
rate_another_metric{env="1"} 0.2
|
||||
rate_metric_total{env="1"} 0.2
|
||||
rate_another_metric_total{env="1"} 0.2
|
||||
|
||||
# Allows relabeling (to-be-dropped) __name__ via label_join
|
||||
# Allows relabeling (to-be-dropped) __name__ via label_join.
|
||||
eval instant at 10m label_join(rate({env="1"}[10m]), "my_name", "_", "__name__")
|
||||
{my_name="metric", env="1"} 0.2
|
||||
{my_name="another_metric", env="1"} 0.2
|
||||
{my_name="metric_total", env="1"} 0.2
|
||||
{my_name="another_metric_total", env="1"} 0.2
|
||||
|
||||
# Allows preserving __name__ via label_join
|
||||
# Allows preserving __name__ via label_join.
|
||||
eval instant at 10m label_join(rate({env="1"}[10m]), "__name__", "_", "__name__", "env")
|
||||
metric_1{env="1"} 0.2
|
||||
another_metric_1{env="1"} 0.2
|
||||
metric_total_1{env="1"} 0.2
|
||||
another_metric_total_1{env="1"} 0.2
|
||||
|
||||
# Does not drop metric names fro aggregation operators
|
||||
eval instant at 10m sum by (__name__, env) (metric{env="1"})
|
||||
metric{env="1"} 120
|
||||
# Does not drop metric names from aggregation operators.
|
||||
eval instant at 10m sum by (__name__, env) (metric_total{env="1"})
|
||||
metric_total{env="1"} 120
|
||||
|
||||
# Aggregation operators by __name__ lead to duplicate labelset errors (aggregation is partitioned by not yet removed __name__ label)
|
||||
# Aggregation operators by __name__ lead to duplicate labelset errors (aggregation is partitioned by not yet removed __name__ label).
|
||||
# This is an accidental side effect of delayed __name__ label dropping
|
||||
eval_fail instant at 10m sum by (__name__) (rate({env="1"}[10m]))
|
||||
|
||||
# Aggregation operators aggregate metrics with same labelset and to-be-dropped names
|
||||
# Aggregation operators aggregate metrics with same labelset and to-be-dropped names.
|
||||
# This is an accidental side effect of delayed __name__ label dropping
|
||||
eval instant at 10m sum(rate({env="1"}[10m])) by (env)
|
||||
{env="1"} 0.4
|
||||
|
||||
# Aggregationk operators propagate __name__ label dropping information
|
||||
eval instant at 10m topk(10, sum by (__name__, env) (metric{env="1"}))
|
||||
metric{env="1"} 120
|
||||
# Aggregationk operators propagate __name__ label dropping information.
|
||||
eval instant at 10m topk(10, sum by (__name__, env) (metric_total{env="1"}))
|
||||
metric_total{env="1"} 120
|
||||
|
||||
eval instant at 10m topk(10, sum by (__name__, env) (rate(metric{env="1"}[10m])))
|
||||
eval instant at 10m topk(10, sum by (__name__, env) (rate(metric_total{env="1"}[10m])))
|
||||
{env="1"} 0.2
|
||||
|
|
232
promql/promqltest/testdata/operators.test
vendored
232
promql/promqltest/testdata/operators.test
vendored
|
@ -1,12 +1,12 @@
|
|||
load 5m
|
||||
http_requests{job="api-server", instance="0", group="production"} 0+10x10
|
||||
http_requests{job="api-server", instance="1", group="production"} 0+20x10
|
||||
http_requests{job="api-server", instance="0", group="canary"} 0+30x10
|
||||
http_requests{job="api-server", instance="1", group="canary"} 0+40x10
|
||||
http_requests{job="app-server", instance="0", group="production"} 0+50x10
|
||||
http_requests{job="app-server", instance="1", group="production"} 0+60x10
|
||||
http_requests{job="app-server", instance="0", group="canary"} 0+70x10
|
||||
http_requests{job="app-server", instance="1", group="canary"} 0+80x10
|
||||
http_requests_total{job="api-server", instance="0", group="production"} 0+10x10
|
||||
http_requests_total{job="api-server", instance="1", group="production"} 0+20x10
|
||||
http_requests_total{job="api-server", instance="0", group="canary"} 0+30x10
|
||||
http_requests_total{job="api-server", instance="1", group="canary"} 0+40x10
|
||||
http_requests_total{job="app-server", instance="0", group="production"} 0+50x10
|
||||
http_requests_total{job="app-server", instance="1", group="production"} 0+60x10
|
||||
http_requests_total{job="app-server", instance="0", group="canary"} 0+70x10
|
||||
http_requests_total{job="app-server", instance="1", group="canary"} 0+80x10
|
||||
http_requests_histogram{job="app-server", instance="1", group="production"} {{schema:1 sum:15 count:10 buckets:[3 2 5 7 9]}}x11
|
||||
|
||||
load 5m
|
||||
|
@ -15,21 +15,21 @@ load 5m
|
|||
vector_matching_b{l="x"} 0+4x25
|
||||
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) - COUNT(http_requests) BY (job)
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) - COUNT(http_requests_total) BY (job)
|
||||
{job="api-server"} 996
|
||||
{job="app-server"} 2596
|
||||
|
||||
eval instant at 50m 2 - SUM(http_requests) BY (job)
|
||||
eval instant at 50m 2 - SUM(http_requests_total) BY (job)
|
||||
{job="api-server"} -998
|
||||
{job="app-server"} -2598
|
||||
|
||||
eval instant at 50m -http_requests{job="api-server",instance="0",group="production"}
|
||||
eval instant at 50m -http_requests_total{job="api-server",instance="0",group="production"}
|
||||
{job="api-server",instance="0",group="production"} -100
|
||||
|
||||
eval instant at 50m +http_requests{job="api-server",instance="0",group="production"}
|
||||
http_requests{job="api-server",instance="0",group="production"} 100
|
||||
eval instant at 50m +http_requests_total{job="api-server",instance="0",group="production"}
|
||||
http_requests_total{job="api-server",instance="0",group="production"} 100
|
||||
|
||||
eval instant at 50m - - - SUM(http_requests) BY (job)
|
||||
eval instant at 50m - - - SUM(http_requests_total) BY (job)
|
||||
{job="api-server"} -1000
|
||||
{job="app-server"} -2600
|
||||
|
||||
|
@ -42,83 +42,83 @@ eval instant at 50m -2^---1*3
|
|||
eval instant at 50m 2/-2^---1*3+2
|
||||
-10
|
||||
|
||||
eval instant at 50m -10^3 * - SUM(http_requests) BY (job) ^ -1
|
||||
eval instant at 50m -10^3 * - SUM(http_requests_total) BY (job) ^ -1
|
||||
{job="api-server"} 1
|
||||
{job="app-server"} 0.38461538461538464
|
||||
|
||||
eval instant at 50m 1000 / SUM(http_requests) BY (job)
|
||||
eval instant at 50m 1000 / SUM(http_requests_total) BY (job)
|
||||
{job="api-server"} 1
|
||||
{job="app-server"} 0.38461538461538464
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) - 2
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) - 2
|
||||
{job="api-server"} 998
|
||||
{job="app-server"} 2598
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) % 3
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) % 3
|
||||
{job="api-server"} 1
|
||||
{job="app-server"} 2
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) % 0.3
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) % 0.3
|
||||
{job="api-server"} 0.1
|
||||
{job="app-server"} 0.2
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) ^ 2
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) ^ 2
|
||||
{job="api-server"} 1000000
|
||||
{job="app-server"} 6760000
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) % 3 ^ 2
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) % 3 ^ 2
|
||||
{job="api-server"} 1
|
||||
{job="app-server"} 8
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) % 2 ^ (3 ^ 2)
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) % 2 ^ (3 ^ 2)
|
||||
{job="api-server"} 488
|
||||
{job="app-server"} 40
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) % 2 ^ 3 ^ 2
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) % 2 ^ 3 ^ 2
|
||||
{job="api-server"} 488
|
||||
{job="app-server"} 40
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) % 2 ^ 3 ^ 2 ^ 2
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) % 2 ^ 3 ^ 2 ^ 2
|
||||
{job="api-server"} 1000
|
||||
{job="app-server"} 2600
|
||||
|
||||
eval instant at 50m COUNT(http_requests) BY (job) ^ COUNT(http_requests) BY (job)
|
||||
eval instant at 50m COUNT(http_requests_total) BY (job) ^ COUNT(http_requests_total) BY (job)
|
||||
{job="api-server"} 256
|
||||
{job="app-server"} 256
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) / 0
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) / 0
|
||||
{job="api-server"} +Inf
|
||||
{job="app-server"} +Inf
|
||||
|
||||
eval instant at 50m http_requests{group="canary", instance="0", job="api-server"} / 0
|
||||
eval instant at 50m http_requests_total{group="canary", instance="0", job="api-server"} / 0
|
||||
{group="canary", instance="0", job="api-server"} +Inf
|
||||
|
||||
eval instant at 50m -1 * http_requests{group="canary", instance="0", job="api-server"} / 0
|
||||
eval instant at 50m -1 * http_requests_total{group="canary", instance="0", job="api-server"} / 0
|
||||
{group="canary", instance="0", job="api-server"} -Inf
|
||||
|
||||
eval instant at 50m 0 * http_requests{group="canary", instance="0", job="api-server"} / 0
|
||||
eval instant at 50m 0 * http_requests_total{group="canary", instance="0", job="api-server"} / 0
|
||||
{group="canary", instance="0", job="api-server"} NaN
|
||||
|
||||
eval instant at 50m 0 * http_requests{group="canary", instance="0", job="api-server"} % 0
|
||||
eval instant at 50m 0 * http_requests_total{group="canary", instance="0", job="api-server"} % 0
|
||||
{group="canary", instance="0", job="api-server"} NaN
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) + SUM(http_requests) BY (job)
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) + SUM(http_requests_total) BY (job)
|
||||
{job="api-server"} 2000
|
||||
{job="app-server"} 5200
|
||||
|
||||
eval instant at 50m (SUM((http_requests)) BY (job)) + SUM(http_requests) BY (job)
|
||||
eval instant at 50m (SUM((http_requests_total)) BY (job)) + SUM(http_requests_total) BY (job)
|
||||
{job="api-server"} 2000
|
||||
{job="app-server"} 5200
|
||||
|
||||
eval instant at 50m http_requests{job="api-server", group="canary"}
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
eval instant at 50m http_requests_total{job="api-server", group="canary"}
|
||||
http_requests_total{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests_total{group="canary", instance="1", job="api-server"} 400
|
||||
|
||||
eval instant at 50m http_requests{job="api-server", group="canary"} + rate(http_requests{job="api-server"}[10m]) * 5 * 60
|
||||
eval instant at 50m http_requests_total{job="api-server", group="canary"} + rate(http_requests_total{job="api-server"}[10m]) * 5 * 60
|
||||
{group="canary", instance="0", job="api-server"} 330
|
||||
{group="canary", instance="1", job="api-server"} 440
|
||||
|
||||
eval instant at 50m rate(http_requests[25m]) * 25 * 60
|
||||
eval instant at 50m rate(http_requests_total[25m]) * 25 * 60
|
||||
{group="canary", instance="0", job="api-server"} 150
|
||||
{group="canary", instance="0", job="app-server"} 350
|
||||
{group="canary", instance="1", job="api-server"} 200
|
||||
|
@ -128,7 +128,7 @@ eval instant at 50m rate(http_requests[25m]) * 25 * 60
|
|||
{group="production", instance="1", job="api-server"} 100
|
||||
{group="production", instance="1", job="app-server"} 300
|
||||
|
||||
eval instant at 50m (rate((http_requests[25m])) * 25) * 60
|
||||
eval instant at 50m (rate((http_requests_total[25m])) * 25) * 60
|
||||
{group="canary", instance="0", job="api-server"} 150
|
||||
{group="canary", instance="0", job="app-server"} 350
|
||||
{group="canary", instance="1", job="api-server"} 200
|
||||
|
@ -139,53 +139,53 @@ eval instant at 50m (rate((http_requests[25m])) * 25) * 60
|
|||
{group="production", instance="1", job="app-server"} 300
|
||||
|
||||
|
||||
eval instant at 50m http_requests{group="canary"} and http_requests{instance="0"}
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
eval instant at 50m http_requests_total{group="canary"} and http_requests_total{instance="0"}
|
||||
http_requests_total{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests_total{group="canary", instance="0", job="app-server"} 700
|
||||
|
||||
eval instant at 50m (http_requests{group="canary"} + 1) and http_requests{instance="0"}
|
||||
eval instant at 50m (http_requests_total{group="canary"} + 1) and http_requests_total{instance="0"}
|
||||
{group="canary", instance="0", job="api-server"} 301
|
||||
{group="canary", instance="0", job="app-server"} 701
|
||||
|
||||
eval instant at 50m (http_requests{group="canary"} + 1) and on(instance, job) http_requests{instance="0", group="production"}
|
||||
eval instant at 50m (http_requests_total{group="canary"} + 1) and on(instance, job) http_requests_total{instance="0", group="production"}
|
||||
{group="canary", instance="0", job="api-server"} 301
|
||||
{group="canary", instance="0", job="app-server"} 701
|
||||
|
||||
eval instant at 50m (http_requests{group="canary"} + 1) and on(instance) http_requests{instance="0", group="production"}
|
||||
eval instant at 50m (http_requests_total{group="canary"} + 1) and on(instance) http_requests_total{instance="0", group="production"}
|
||||
{group="canary", instance="0", job="api-server"} 301
|
||||
{group="canary", instance="0", job="app-server"} 701
|
||||
|
||||
eval instant at 50m (http_requests{group="canary"} + 1) and ignoring(group) http_requests{instance="0", group="production"}
|
||||
eval instant at 50m (http_requests_total{group="canary"} + 1) and ignoring(group) http_requests_total{instance="0", group="production"}
|
||||
{group="canary", instance="0", job="api-server"} 301
|
||||
{group="canary", instance="0", job="app-server"} 701
|
||||
|
||||
eval instant at 50m (http_requests{group="canary"} + 1) and ignoring(group, job) http_requests{instance="0", group="production"}
|
||||
eval instant at 50m (http_requests_total{group="canary"} + 1) and ignoring(group, job) http_requests_total{instance="0", group="production"}
|
||||
{group="canary", instance="0", job="api-server"} 301
|
||||
{group="canary", instance="0", job="app-server"} 701
|
||||
|
||||
eval instant at 50m http_requests{group="canary"} or http_requests{group="production"}
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
http_requests{group="production", instance="0", job="app-server"} 500
|
||||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
eval instant at 50m http_requests_total{group="canary"} or http_requests_total{group="production"}
|
||||
http_requests_total{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests_total{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests_total{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests_total{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests_total{group="production", instance="0", job="api-server"} 100
|
||||
http_requests_total{group="production", instance="0", job="app-server"} 500
|
||||
http_requests_total{group="production", instance="1", job="api-server"} 200
|
||||
http_requests_total{group="production", instance="1", job="app-server"} 600
|
||||
|
||||
# On overlap the rhs samples must be dropped.
|
||||
eval instant at 50m (http_requests{group="canary"} + 1) or http_requests{instance="1"}
|
||||
eval instant at 50m (http_requests_total{group="canary"} + 1) or http_requests_total{instance="1"}
|
||||
{group="canary", instance="0", job="api-server"} 301
|
||||
{group="canary", instance="0", job="app-server"} 701
|
||||
{group="canary", instance="1", job="api-server"} 401
|
||||
{group="canary", instance="1", job="app-server"} 801
|
||||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
http_requests_total{group="production", instance="1", job="api-server"} 200
|
||||
http_requests_total{group="production", instance="1", job="app-server"} 600
|
||||
|
||||
|
||||
# Matching only on instance excludes everything that has instance=0/1 but includes
|
||||
# entries without the instance label.
|
||||
eval instant at 50m (http_requests{group="canary"} + 1) or on(instance) (http_requests or cpu_count or vector_matching_a)
|
||||
eval instant at 50m (http_requests_total{group="canary"} + 1) or on(instance) (http_requests_total or cpu_count or vector_matching_a)
|
||||
{group="canary", instance="0", job="api-server"} 301
|
||||
{group="canary", instance="0", job="app-server"} 701
|
||||
{group="canary", instance="1", job="api-server"} 401
|
||||
|
@ -193,7 +193,7 @@ eval instant at 50m (http_requests{group="canary"} + 1) or on(instance) (http_re
|
|||
vector_matching_a{l="x"} 10
|
||||
vector_matching_a{l="y"} 20
|
||||
|
||||
eval instant at 50m (http_requests{group="canary"} + 1) or ignoring(l, group, job) (http_requests or cpu_count or vector_matching_a)
|
||||
eval instant at 50m (http_requests_total{group="canary"} + 1) or ignoring(l, group, job) (http_requests_total or cpu_count or vector_matching_a)
|
||||
{group="canary", instance="0", job="api-server"} 301
|
||||
{group="canary", instance="0", job="app-server"} 701
|
||||
{group="canary", instance="1", job="api-server"} 401
|
||||
|
@ -201,81 +201,81 @@ eval instant at 50m (http_requests{group="canary"} + 1) or ignoring(l, group, jo
|
|||
vector_matching_a{l="x"} 10
|
||||
vector_matching_a{l="y"} 20
|
||||
|
||||
eval instant at 50m http_requests{group="canary"} unless http_requests{instance="0"}
|
||||
http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
eval instant at 50m http_requests_total{group="canary"} unless http_requests_total{instance="0"}
|
||||
http_requests_total{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests_total{group="canary", instance="1", job="app-server"} 800
|
||||
|
||||
eval instant at 50m http_requests{group="canary"} unless on(job) http_requests{instance="0"}
|
||||
eval instant at 50m http_requests_total{group="canary"} unless on(job) http_requests_total{instance="0"}
|
||||
|
||||
eval instant at 50m http_requests{group="canary"} unless on(job, instance) http_requests{instance="0"}
|
||||
http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
eval instant at 50m http_requests_total{group="canary"} unless on(job, instance) http_requests_total{instance="0"}
|
||||
http_requests_total{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests_total{group="canary", instance="1", job="app-server"} 800
|
||||
|
||||
eval instant at 50m http_requests{group="canary"} / on(instance,job) http_requests{group="production"}
|
||||
eval instant at 50m http_requests_total{group="canary"} / on(instance,job) http_requests_total{group="production"}
|
||||
{instance="0", job="api-server"} 3
|
||||
{instance="0", job="app-server"} 1.4
|
||||
{instance="1", job="api-server"} 2
|
||||
{instance="1", job="app-server"} 1.3333333333333333
|
||||
|
||||
eval instant at 50m http_requests{group="canary"} unless ignoring(group, instance) http_requests{instance="0"}
|
||||
eval instant at 50m http_requests_total{group="canary"} unless ignoring(group, instance) http_requests_total{instance="0"}
|
||||
|
||||
eval instant at 50m http_requests{group="canary"} unless ignoring(group) http_requests{instance="0"}
|
||||
http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
eval instant at 50m http_requests_total{group="canary"} unless ignoring(group) http_requests_total{instance="0"}
|
||||
http_requests_total{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests_total{group="canary", instance="1", job="app-server"} 800
|
||||
|
||||
eval instant at 50m http_requests{group="canary"} / ignoring(group) http_requests{group="production"}
|
||||
eval instant at 50m http_requests_total{group="canary"} / ignoring(group) http_requests_total{group="production"}
|
||||
{instance="0", job="api-server"} 3
|
||||
{instance="0", job="app-server"} 1.4
|
||||
{instance="1", job="api-server"} 2
|
||||
{instance="1", job="app-server"} 1.3333333333333333
|
||||
|
||||
# https://github.com/prometheus/prometheus/issues/1489
|
||||
eval instant at 50m http_requests AND ON (dummy) vector(1)
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
http_requests{group="production", instance="0", job="app-server"} 500
|
||||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
eval instant at 50m http_requests_total AND ON (dummy) vector(1)
|
||||
http_requests_total{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests_total{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests_total{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests_total{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests_total{group="production", instance="0", job="api-server"} 100
|
||||
http_requests_total{group="production", instance="0", job="app-server"} 500
|
||||
http_requests_total{group="production", instance="1", job="api-server"} 200
|
||||
http_requests_total{group="production", instance="1", job="app-server"} 600
|
||||
|
||||
eval instant at 50m http_requests AND IGNORING (group, instance, job) vector(1)
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
http_requests{group="production", instance="0", job="app-server"} 500
|
||||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
eval instant at 50m http_requests_total AND IGNORING (group, instance, job) vector(1)
|
||||
http_requests_total{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests_total{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests_total{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests_total{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests_total{group="production", instance="0", job="api-server"} 100
|
||||
http_requests_total{group="production", instance="0", job="app-server"} 500
|
||||
http_requests_total{group="production", instance="1", job="api-server"} 200
|
||||
http_requests_total{group="production", instance="1", job="app-server"} 600
|
||||
|
||||
|
||||
# Comparisons.
|
||||
eval instant at 50m SUM(http_requests) BY (job) > 1000
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) > 1000
|
||||
{job="app-server"} 2600
|
||||
|
||||
eval instant at 50m 1000 < SUM(http_requests) BY (job)
|
||||
eval instant at 50m 1000 < SUM(http_requests_total) BY (job)
|
||||
{job="app-server"} 2600
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) <= 1000
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) <= 1000
|
||||
{job="api-server"} 1000
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) != 1000
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) != 1000
|
||||
{job="app-server"} 2600
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) == 1000
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) == 1000
|
||||
{job="api-server"} 1000
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) == bool 1000
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) == bool 1000
|
||||
{job="api-server"} 1
|
||||
{job="app-server"} 0
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) == bool SUM(http_requests) BY (job)
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) == bool SUM(http_requests_total) BY (job)
|
||||
{job="api-server"} 1
|
||||
{job="app-server"} 1
|
||||
|
||||
eval instant at 50m SUM(http_requests) BY (job) != bool SUM(http_requests) BY (job)
|
||||
eval instant at 50m SUM(http_requests_total) BY (job) != bool SUM(http_requests_total) BY (job)
|
||||
{job="api-server"} 0
|
||||
{job="app-server"} 0
|
||||
|
||||
|
@ -285,12 +285,12 @@ eval instant at 50m 0 == bool 1
|
|||
eval instant at 50m 1 == bool 1
|
||||
1
|
||||
|
||||
eval instant at 50m http_requests{job="api-server", instance="0", group="production"} == bool 100
|
||||
eval instant at 50m http_requests_total{job="api-server", instance="0", group="production"} == bool 100
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
|
||||
# The histogram is ignored here so the result doesn't change but it has an info annotation now.
|
||||
eval_info instant at 5m {job="app-server"} == 80
|
||||
http_requests{group="canary", instance="1", job="app-server"} 80
|
||||
http_requests_total{group="canary", instance="1", job="app-server"} 80
|
||||
|
||||
eval_info instant at 5m http_requests_histogram != 80
|
||||
|
||||
|
@ -673,7 +673,7 @@ eval_info range from 0 to 24m step 6m left_histograms == 0
|
|||
eval_info range from 0 to 24m step 6m left_histograms != 3
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m left_histograms != 0
|
||||
eval_info range from 0 to 24m step 6m left_histograms != 0
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms > 3
|
||||
|
@ -682,7 +682,7 @@ eval_info range from 0 to 24m step 6m left_histograms > 3
|
|||
eval_info range from 0 to 24m step 6m left_histograms > 0
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m left_histograms >= 3
|
||||
eval_info range from 0 to 24m step 6m left_histograms >= 3
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms >= 0
|
||||
|
@ -697,7 +697,7 @@ eval_info range from 0 to 24m step 6m left_histograms < 0
|
|||
eval_info range from 0 to 24m step 6m left_histograms <= 3
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m left_histograms <= 0
|
||||
eval_info range from 0 to 24m step 6m left_histograms <= 0
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms == bool 3
|
||||
|
@ -770,40 +770,40 @@ eval range from 0 to 60m step 6m NaN == left_floats
|
|||
eval range from 0 to 60m step 6m NaN == bool left_floats
|
||||
{} 0 0 _ _ 0 _ 0 0 0 0 0
|
||||
|
||||
eval range from 0 to 24m step 6m 3 == left_histograms
|
||||
eval_info range from 0 to 24m step 6m 3 == left_histograms
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m 0 == left_histograms
|
||||
eval_info range from 0 to 24m step 6m 0 == left_histograms
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m 3 != left_histograms
|
||||
eval_info range from 0 to 24m step 6m 3 != left_histograms
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m 0 != left_histograms
|
||||
eval_info range from 0 to 24m step 6m 0 != left_histograms
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m 3 < left_histograms
|
||||
eval_info range from 0 to 24m step 6m 3 < left_histograms
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m 0 < left_histograms
|
||||
eval_info range from 0 to 24m step 6m 0 < left_histograms
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m 3 < left_histograms
|
||||
eval_info range from 0 to 24m step 6m 3 < left_histograms
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m 0 < left_histograms
|
||||
eval_info range from 0 to 24m step 6m 0 < left_histograms
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m 3 > left_histograms
|
||||
eval_info range from 0 to 24m step 6m 3 > left_histograms
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m 0 > left_histograms
|
||||
eval_info range from 0 to 24m step 6m 0 > left_histograms
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m 3 >= left_histograms
|
||||
eval_info range from 0 to 24m step 6m 3 >= left_histograms
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m 0 >= left_histograms
|
||||
eval_info range from 0 to 24m step 6m 0 >= left_histograms
|
||||
# No results.
|
||||
|
||||
clear
|
||||
|
|
156
promql/promqltest/testdata/selectors.test
vendored
156
promql/promqltest/testdata/selectors.test
vendored
|
@ -1,109 +1,109 @@
|
|||
load 10s
|
||||
http_requests{job="api-server", instance="0", group="production"} 0+10x1000 100+30x1000
|
||||
http_requests{job="api-server", instance="1", group="production"} 0+20x1000 200+30x1000
|
||||
http_requests{job="api-server", instance="0", group="canary"} 0+30x1000 300+80x1000
|
||||
http_requests{job="api-server", instance="1", group="canary"} 0+40x2000
|
||||
http_requests_total{job="api-server", instance="0", group="production"} 0+10x1000 100+30x1000
|
||||
http_requests_total{job="api-server", instance="1", group="production"} 0+20x1000 200+30x1000
|
||||
http_requests_total{job="api-server", instance="0", group="canary"} 0+30x1000 300+80x1000
|
||||
http_requests_total{job="api-server", instance="1", group="canary"} 0+40x2000
|
||||
|
||||
eval instant at 8000s rate(http_requests[1m])
|
||||
eval instant at 8000s rate(http_requests_total[1m])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
{job="api-server", instance="0", group="canary"} 3
|
||||
{job="api-server", instance="1", group="canary"} 4
|
||||
|
||||
eval instant at 18000s rate(http_requests[1m])
|
||||
eval instant at 18000s rate(http_requests_total[1m])
|
||||
{job="api-server", instance="0", group="production"} 3
|
||||
{job="api-server", instance="1", group="production"} 3
|
||||
{job="api-server", instance="0", group="canary"} 8
|
||||
{job="api-server", instance="1", group="canary"} 4
|
||||
|
||||
eval instant at 8000s rate(http_requests{group=~"pro.*"}[1m])
|
||||
eval instant at 8000s rate(http_requests_total{group=~"pro.*"}[1m])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
|
||||
eval instant at 18000s rate(http_requests{group=~".*ry", instance="1"}[1m])
|
||||
eval instant at 18000s rate(http_requests_total{group=~".*ry", instance="1"}[1m])
|
||||
{job="api-server", instance="1", group="canary"} 4
|
||||
|
||||
eval instant at 18000s rate(http_requests{instance!="3"}[1m] offset 10000s)
|
||||
eval instant at 18000s rate(http_requests_total{instance!="3"}[1m] offset 10000s)
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
{job="api-server", instance="0", group="canary"} 3
|
||||
{job="api-server", instance="1", group="canary"} 4
|
||||
|
||||
eval instant at 4000s rate(http_requests{instance!="3"}[1m] offset -4000s)
|
||||
eval instant at 4000s rate(http_requests_total{instance!="3"}[1m] offset -4000s)
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
{job="api-server", instance="0", group="canary"} 3
|
||||
{job="api-server", instance="1", group="canary"} 4
|
||||
|
||||
eval instant at 18000s rate(http_requests[40s]) - rate(http_requests[1m] offset 10000s)
|
||||
eval instant at 18000s rate(http_requests_total[40s]) - rate(http_requests_total[1m] offset 10000s)
|
||||
{job="api-server", instance="0", group="production"} 2
|
||||
{job="api-server", instance="1", group="production"} 1
|
||||
{job="api-server", instance="0", group="canary"} 5
|
||||
{job="api-server", instance="1", group="canary"} 0
|
||||
|
||||
# https://github.com/prometheus/prometheus/issues/3575
|
||||
eval instant at 0s http_requests{foo!="bar"}
|
||||
http_requests{job="api-server", instance="0", group="production"} 0
|
||||
http_requests{job="api-server", instance="1", group="production"} 0
|
||||
http_requests{job="api-server", instance="0", group="canary"} 0
|
||||
http_requests{job="api-server", instance="1", group="canary"} 0
|
||||
eval instant at 0s http_requests_total{foo!="bar"}
|
||||
http_requests_total{job="api-server", instance="0", group="production"} 0
|
||||
http_requests_total{job="api-server", instance="1", group="production"} 0
|
||||
http_requests_total{job="api-server", instance="0", group="canary"} 0
|
||||
http_requests_total{job="api-server", instance="1", group="canary"} 0
|
||||
|
||||
eval instant at 0s http_requests{foo!="bar", job="api-server"}
|
||||
http_requests{job="api-server", instance="0", group="production"} 0
|
||||
http_requests{job="api-server", instance="1", group="production"} 0
|
||||
http_requests{job="api-server", instance="0", group="canary"} 0
|
||||
http_requests{job="api-server", instance="1", group="canary"} 0
|
||||
eval instant at 0s http_requests_total{foo!="bar", job="api-server"}
|
||||
http_requests_total{job="api-server", instance="0", group="production"} 0
|
||||
http_requests_total{job="api-server", instance="1", group="production"} 0
|
||||
http_requests_total{job="api-server", instance="0", group="canary"} 0
|
||||
http_requests_total{job="api-server", instance="1", group="canary"} 0
|
||||
|
||||
eval instant at 0s http_requests{foo!~"bar", job="api-server"}
|
||||
http_requests{job="api-server", instance="0", group="production"} 0
|
||||
http_requests{job="api-server", instance="1", group="production"} 0
|
||||
http_requests{job="api-server", instance="0", group="canary"} 0
|
||||
http_requests{job="api-server", instance="1", group="canary"} 0
|
||||
eval instant at 0s http_requests_total{foo!~"bar", job="api-server"}
|
||||
http_requests_total{job="api-server", instance="0", group="production"} 0
|
||||
http_requests_total{job="api-server", instance="1", group="production"} 0
|
||||
http_requests_total{job="api-server", instance="0", group="canary"} 0
|
||||
http_requests_total{job="api-server", instance="1", group="canary"} 0
|
||||
|
||||
eval instant at 0s http_requests{foo!~"bar", job="api-server", instance="1", x!="y", z="", group!=""}
|
||||
http_requests{job="api-server", instance="1", group="production"} 0
|
||||
http_requests{job="api-server", instance="1", group="canary"} 0
|
||||
eval instant at 0s http_requests_total{foo!~"bar", job="api-server", instance="1", x!="y", z="", group!=""}
|
||||
http_requests_total{job="api-server", instance="1", group="production"} 0
|
||||
http_requests_total{job="api-server", instance="1", group="canary"} 0
|
||||
|
||||
# https://github.com/prometheus/prometheus/issues/7994
|
||||
eval instant at 8000s rate(http_requests{group=~"(?i:PRO).*"}[1m])
|
||||
eval instant at 8000s rate(http_requests_total{group=~"(?i:PRO).*"}[1m])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
|
||||
eval instant at 8000s rate(http_requests{group=~".*?(?i:PRO).*"}[1m])
|
||||
eval instant at 8000s rate(http_requests_total{group=~".*?(?i:PRO).*"}[1m])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
|
||||
eval instant at 8000s rate(http_requests{group=~".*(?i:DUC).*"}[1m])
|
||||
eval instant at 8000s rate(http_requests_total{group=~".*(?i:DUC).*"}[1m])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
|
||||
eval instant at 8000s rate(http_requests{group=~".*(?i:TION)"}[1m])
|
||||
eval instant at 8000s rate(http_requests_total{group=~".*(?i:TION)"}[1m])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
|
||||
eval instant at 8000s rate(http_requests{group=~".*(?i:TION).*?"}[1m])
|
||||
eval instant at 8000s rate(http_requests_total{group=~".*(?i:TION).*?"}[1m])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
|
||||
|
||||
eval instant at 8000s rate(http_requests{group=~"((?i)PRO).*"}[1m])
|
||||
eval instant at 8000s rate(http_requests_total{group=~"((?i)PRO).*"}[1m])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
|
||||
eval instant at 8000s rate(http_requests{group=~".*((?i)DUC).*"}[1m])
|
||||
eval instant at 8000s rate(http_requests_total{group=~".*((?i)DUC).*"}[1m])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
|
||||
eval instant at 8000s rate(http_requests{group=~".*((?i)TION)"}[1m])
|
||||
eval instant at 8000s rate(http_requests_total{group=~".*((?i)TION)"}[1m])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
|
||||
|
||||
eval instant at 8000s rate(http_requests{group=~"(?i:PRODUCTION)"}[1m])
|
||||
eval instant at 8000s rate(http_requests_total{group=~"(?i:PRODUCTION)"}[1m])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
|
||||
eval instant at 8000s rate(http_requests{group=~".*(?i:C).*"}[1m])
|
||||
eval instant at 8000s rate(http_requests_total{group=~".*(?i:C).*"}[1m])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
{job="api-server", instance="0", group="canary"} 3
|
||||
|
@ -133,14 +133,14 @@ load 5m
|
|||
label_grouping_test{a="a", b="abb"} 0+20x10
|
||||
|
||||
load 5m
|
||||
http_requests{job="api-server", instance="0", group="production"} 0+10x10
|
||||
http_requests{job="api-server", instance="1", group="production"} 0+20x10
|
||||
http_requests{job="api-server", instance="0", group="canary"} 0+30x10
|
||||
http_requests{job="api-server", instance="1", group="canary"} 0+40x10
|
||||
http_requests{job="app-server", instance="0", group="production"} 0+50x10
|
||||
http_requests{job="app-server", instance="1", group="production"} 0+60x10
|
||||
http_requests{job="app-server", instance="0", group="canary"} 0+70x10
|
||||
http_requests{job="app-server", instance="1", group="canary"} 0+80x10
|
||||
http_requests_total{job="api-server", instance="0", group="production"} 0+10x10
|
||||
http_requests_total{job="api-server", instance="1", group="production"} 0+20x10
|
||||
http_requests_total{job="api-server", instance="0", group="canary"} 0+30x10
|
||||
http_requests_total{job="api-server", instance="1", group="canary"} 0+40x10
|
||||
http_requests_total{job="app-server", instance="0", group="production"} 0+50x10
|
||||
http_requests_total{job="app-server", instance="1", group="production"} 0+60x10
|
||||
http_requests_total{job="app-server", instance="0", group="canary"} 0+70x10
|
||||
http_requests_total{job="app-server", instance="1", group="canary"} 0+80x10
|
||||
|
||||
# Single-letter label names and values.
|
||||
eval instant at 50m x{y="testvalue"}
|
||||
|
@ -148,14 +148,14 @@ eval instant at 50m x{y="testvalue"}
|
|||
|
||||
# Basic Regex
|
||||
eval instant at 50m {__name__=~".+"}
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
http_requests{group="production", instance="0", job="app-server"} 500
|
||||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
http_requests_total{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests_total{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests_total{group="canary", instance="1", job="api-server"} 400
|
||||
http_requests_total{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests_total{group="production", instance="0", job="api-server"} 100
|
||||
http_requests_total{group="production", instance="0", job="app-server"} 500
|
||||
http_requests_total{group="production", instance="1", job="api-server"} 200
|
||||
http_requests_total{group="production", instance="1", job="app-server"} 600
|
||||
x{y="testvalue"} 100
|
||||
label_grouping_test{a="a", b="abb"} 200
|
||||
label_grouping_test{a="aa", b="bb"} 100
|
||||
|
@ -164,34 +164,34 @@ eval instant at 50m {__name__=~".+"}
|
|||
cpu_count{instance="0", type="numa"} 300
|
||||
|
||||
eval instant at 50m {job=~".+-server", job!~"api-.+"}
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests{group="production", instance="0", job="app-server"} 500
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
http_requests_total{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests_total{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests_total{group="production", instance="0", job="app-server"} 500
|
||||
http_requests_total{group="production", instance="1", job="app-server"} 600
|
||||
|
||||
eval instant at 50m http_requests{group!="canary"}
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
http_requests{group="production", instance="0", job="app-server"} 500
|
||||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
eval instant at 50m http_requests_total{group!="canary"}
|
||||
http_requests_total{group="production", instance="1", job="app-server"} 600
|
||||
http_requests_total{group="production", instance="0", job="app-server"} 500
|
||||
http_requests_total{group="production", instance="1", job="api-server"} 200
|
||||
http_requests_total{group="production", instance="0", job="api-server"} 100
|
||||
|
||||
eval instant at 50m http_requests{job=~".+-server",group!="canary"}
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
http_requests{group="production", instance="0", job="app-server"} 500
|
||||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
eval instant at 50m http_requests_total{job=~".+-server",group!="canary"}
|
||||
http_requests_total{group="production", instance="1", job="app-server"} 600
|
||||
http_requests_total{group="production", instance="0", job="app-server"} 500
|
||||
http_requests_total{group="production", instance="1", job="api-server"} 200
|
||||
http_requests_total{group="production", instance="0", job="api-server"} 100
|
||||
|
||||
eval instant at 50m http_requests{job!~"api-.+",group!="canary"}
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
http_requests{group="production", instance="0", job="app-server"} 500
|
||||
eval instant at 50m http_requests_total{job!~"api-.+",group!="canary"}
|
||||
http_requests_total{group="production", instance="1", job="app-server"} 600
|
||||
http_requests_total{group="production", instance="0", job="app-server"} 500
|
||||
|
||||
eval instant at 50m http_requests{group="production",job=~"api-.+"}
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
eval instant at 50m http_requests_total{group="production",job=~"api-.+"}
|
||||
http_requests_total{group="production", instance="0", job="api-server"} 100
|
||||
http_requests_total{group="production", instance="1", job="api-server"} 200
|
||||
|
||||
eval instant at 50m http_requests{group="production",job="api-server"} offset 5m
|
||||
http_requests{group="production", instance="0", job="api-server"} 90
|
||||
http_requests{group="production", instance="1", job="api-server"} 180
|
||||
eval instant at 50m http_requests_total{group="production",job="api-server"} offset 5m
|
||||
http_requests_total{group="production", instance="0", job="api-server"} 90
|
||||
http_requests_total{group="production", instance="1", job="api-server"} 180
|
||||
|
||||
clear
|
||||
|
||||
|
|
84
promql/promqltest/testdata/subquery.test
vendored
84
promql/promqltest/testdata/subquery.test
vendored
|
@ -1,41 +1,41 @@
|
|||
load 10s
|
||||
metric 1 2
|
||||
metric_total 1 2
|
||||
|
||||
# Evaluation before 0s gets no sample.
|
||||
eval instant at 10s sum_over_time(metric[50s:10s])
|
||||
eval instant at 10s sum_over_time(metric_total[50s:10s])
|
||||
{} 3
|
||||
|
||||
eval instant at 10s sum_over_time(metric[50s:5s])
|
||||
eval instant at 10s sum_over_time(metric_total[50s:5s])
|
||||
{} 4
|
||||
|
||||
# Every evaluation yields the last value, i.e. 2
|
||||
eval instant at 5m sum_over_time(metric[50s:10s])
|
||||
eval instant at 5m sum_over_time(metric_total[50s:10s])
|
||||
{} 10
|
||||
|
||||
# Series becomes stale at 5m10s (5m after last sample)
|
||||
# Series becomes stale at 5m10s (5m after last sample).
|
||||
# Hence subquery gets a single sample at 5m10s.
|
||||
eval instant at 5m59s sum_over_time(metric[60s:10s])
|
||||
eval instant at 5m59s sum_over_time(metric_total[60s:10s])
|
||||
{} 2
|
||||
|
||||
eval instant at 10s rate(metric[20s:10s])
|
||||
eval instant at 10s rate(metric_total[20s:10s])
|
||||
{} 0.1
|
||||
|
||||
eval instant at 20s rate(metric[20s:5s])
|
||||
eval instant at 20s rate(metric_total[20s:5s])
|
||||
{} 0.06666666666666667
|
||||
|
||||
clear
|
||||
|
||||
load 10s
|
||||
http_requests{job="api-server", instance="1", group="production"} 0+20x1000 200+30x1000
|
||||
http_requests{job="api-server", instance="0", group="production"} 0+10x1000 100+30x1000
|
||||
http_requests{job="api-server", instance="0", group="canary"} 0+30x1000 300+80x1000
|
||||
http_requests{job="api-server", instance="1", group="canary"} 0+40x2000
|
||||
http_requests_total{job="api-server", instance="1", group="production"} 0+20x1000 200+30x1000
|
||||
http_requests_total{job="api-server", instance="0", group="production"} 0+10x1000 100+30x1000
|
||||
http_requests_total{job="api-server", instance="0", group="canary"} 0+30x1000 300+80x1000
|
||||
http_requests_total{job="api-server", instance="1", group="canary"} 0+40x2000
|
||||
|
||||
eval instant at 8000s rate(http_requests{group=~"pro.*"}[1m:10s])
|
||||
eval instant at 8000s rate(http_requests_total{group=~"pro.*"}[1m:10s])
|
||||
{job="api-server", instance="0", group="production"} 1
|
||||
{job="api-server", instance="1", group="production"} 2
|
||||
|
||||
eval instant at 20000s avg_over_time(rate(http_requests[1m])[1m:1s])
|
||||
eval instant at 20000s avg_over_time(rate(http_requests_total[1m])[1m:1s])
|
||||
{job="api-server", instance="0", group="canary"} 8
|
||||
{job="api-server", instance="1", group="canary"} 4
|
||||
{job="api-server", instance="1", group="production"} 3
|
||||
|
@ -44,64 +44,64 @@ eval instant at 20000s avg_over_time(rate(http_requests[1m])[1m:1s])
|
|||
clear
|
||||
|
||||
load 10s
|
||||
metric1 0+1x1000
|
||||
metric2 0+2x1000
|
||||
metric3 0+3x1000
|
||||
metric1_total 0+1x1000
|
||||
metric2_total 0+2x1000
|
||||
metric3_total 0+3x1000
|
||||
|
||||
eval instant at 1000s sum_over_time(metric1[30s:10s])
|
||||
eval instant at 1000s sum_over_time(metric1_total[30s:10s])
|
||||
{} 297
|
||||
|
||||
# This is (97 + 98*2 + 99*2 + 100), because other than 97@975s and 100@1000s,
|
||||
# everything else is repeated with the 5s step.
|
||||
eval instant at 1000s sum_over_time(metric1[30s:5s])
|
||||
eval instant at 1000s sum_over_time(metric1_total[30s:5s])
|
||||
{} 591
|
||||
|
||||
# Offset is aligned with the step, so this is from [98@980s, 99@990s, 100@1000s].
|
||||
eval instant at 1010s sum_over_time(metric1[30s:10s] offset 10s)
|
||||
eval instant at 1010s sum_over_time(metric1_total[30s:10s] offset 10s)
|
||||
{} 297
|
||||
|
||||
# Same result for different offsets due to step alignment.
|
||||
eval instant at 1010s sum_over_time(metric1[30s:10s] offset 9s)
|
||||
eval instant at 1010s sum_over_time(metric1_total[30s:10s] offset 9s)
|
||||
{} 297
|
||||
|
||||
eval instant at 1010s sum_over_time(metric1[30s:10s] offset 7s)
|
||||
eval instant at 1010s sum_over_time(metric1_total[30s:10s] offset 7s)
|
||||
{} 297
|
||||
|
||||
eval instant at 1010s sum_over_time(metric1[30s:10s] offset 5s)
|
||||
eval instant at 1010s sum_over_time(metric1_total[30s:10s] offset 5s)
|
||||
{} 297
|
||||
|
||||
eval instant at 1010s sum_over_time(metric1[30s:10s] offset 3s)
|
||||
eval instant at 1010s sum_over_time(metric1_total[30s:10s] offset 3s)
|
||||
{} 297
|
||||
|
||||
eval instant at 1010s sum_over_time((metric1)[30s:10s] offset 3s)
|
||||
eval instant at 1010s sum_over_time((metric1_total)[30s:10s] offset 3s)
|
||||
{} 297
|
||||
|
||||
eval instant at 1010s sum_over_time(metric1[30:10] offset 3)
|
||||
eval instant at 1010s sum_over_time(metric1_total[30:10] offset 3)
|
||||
{} 297
|
||||
|
||||
eval instant at 1010s sum_over_time((metric1)[30:10s] offset 3s)
|
||||
eval instant at 1010s sum_over_time((metric1_total)[30:10s] offset 3s)
|
||||
{} 297
|
||||
|
||||
eval instant at 1010s sum_over_time((metric1)[30:10s] offset 3s)
|
||||
eval instant at 1010s sum_over_time((metric1_total)[30:10s] offset 3s)
|
||||
{} 297
|
||||
|
||||
eval instant at 1010s sum_over_time((metric1)[30:10] offset 3s)
|
||||
eval instant at 1010s sum_over_time((metric1_total)[30:10] offset 3s)
|
||||
{} 297
|
||||
|
||||
eval instant at 1010s sum_over_time((metric1)[30:10] offset 3)
|
||||
eval instant at 1010s sum_over_time((metric1_total)[30:10] offset 3)
|
||||
{} 297
|
||||
|
||||
# Nested subqueries
|
||||
eval instant at 1000s rate(sum_over_time(metric1[30s:10s])[50s:10s])
|
||||
# Nested subqueries.
|
||||
eval instant at 1000s rate(sum_over_time(metric1_total[30s:10s])[50s:10s])
|
||||
{} 0.30000000000000004
|
||||
|
||||
eval instant at 1000s rate(sum_over_time(metric2[30s:10s])[50s:10s])
|
||||
eval instant at 1000s rate(sum_over_time(metric2_total[30s:10s])[50s:10s])
|
||||
{} 0.6000000000000001
|
||||
|
||||
eval instant at 1000s rate(sum_over_time(metric3[30s:10s])[50s:10s])
|
||||
eval instant at 1000s rate(sum_over_time(metric3_total[30s:10s])[50s:10s])
|
||||
{} 0.9
|
||||
|
||||
eval instant at 1000s rate(sum_over_time((metric1+metric2+metric3)[30s:10s])[30s:10s])
|
||||
eval instant at 1000s rate(sum_over_time((metric1_total+metric2_total+metric3_total)[30s:10s])[30s:10s])
|
||||
{} 1.8
|
||||
|
||||
clear
|
||||
|
@ -109,28 +109,28 @@ clear
|
|||
# Fibonacci sequence, to ensure the rate is not constant.
|
||||
# Additional note: using subqueries unnecessarily is unwise.
|
||||
load 7s
|
||||
metric 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155 165580141 267914296 433494437 701408733 1134903170 1836311903 2971215073 4807526976 7778742049 12586269025 20365011074 32951280099 53316291173 86267571272 139583862445 225851433717 365435296162 591286729879 956722026041 1548008755920 2504730781961 4052739537881 6557470319842 10610209857723 17167680177565 27777890035288 44945570212853 72723460248141 117669030460994 190392490709135 308061521170129 498454011879264 806515533049393 1304969544928657 2111485077978050 3416454622906707 5527939700884757 8944394323791464 14472334024676221 23416728348467685 37889062373143906 61305790721611591 99194853094755497 160500643816367088 259695496911122585 420196140727489673 679891637638612258 1100087778366101931 1779979416004714189 2880067194370816120 4660046610375530309 7540113804746346429 12200160415121876738 19740274219868223167 31940434634990099905 51680708854858323072 83621143489848422977 135301852344706746049 218922995834555169026 354224848179261915075 573147844013817084101 927372692193078999176 1500520536206896083277 2427893228399975082453 3928413764606871165730 6356306993006846248183 10284720757613717413913 16641027750620563662096 26925748508234281076009 43566776258854844738105 70492524767089125814114 114059301025943970552219 184551825793033096366333 298611126818977066918552 483162952612010163284885 781774079430987230203437 1264937032042997393488322 2046711111473984623691759 3311648143516982017180081 5358359254990966640871840 8670007398507948658051921 14028366653498915298923761 22698374052006863956975682 36726740705505779255899443 59425114757512643212875125 96151855463018422468774568 155576970220531065681649693 251728825683549488150424261 407305795904080553832073954 659034621587630041982498215 1066340417491710595814572169 1725375039079340637797070384 2791715456571051233611642553 4517090495650391871408712937 7308805952221443105020355490 11825896447871834976429068427 19134702400093278081449423917 30960598847965113057878492344 50095301248058391139327916261 81055900096023504197206408605 131151201344081895336534324866 212207101440105399533740733471 343358302784187294870275058337 555565404224292694404015791808 898923707008479989274290850145 1454489111232772683678306641953 2353412818241252672952597492098 3807901929474025356630904134051 6161314747715278029583501626149 9969216677189303386214405760200 16130531424904581415797907386349 26099748102093884802012313146549 42230279526998466217810220532898 68330027629092351019822533679447 110560307156090817237632754212345 178890334785183168257455287891792 289450641941273985495088042104137 468340976726457153752543329995929 757791618667731139247631372100066 1226132595394188293000174702095995 1983924214061919432247806074196061 3210056809456107725247980776292056 5193981023518027157495786850488117 8404037832974134882743767626780173 13598018856492162040239554477268290 22002056689466296922983322104048463 35600075545958458963222876581316753 57602132235424755886206198685365216 93202207781383214849429075266681969 150804340016807970735635273952047185 244006547798191185585064349218729154 394810887814999156320699623170776339 638817435613190341905763972389505493 1033628323428189498226463595560281832 1672445759041379840132227567949787325 2706074082469569338358691163510069157 4378519841510949178490918731459856482 7084593923980518516849609894969925639 11463113765491467695340528626429782121 18547707689471986212190138521399707760
|
||||
metric_total 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155 165580141 267914296 433494437 701408733 1134903170 1836311903 2971215073 4807526976 7778742049 12586269025 20365011074 32951280099 53316291173 86267571272 139583862445 225851433717 365435296162 591286729879 956722026041 1548008755920 2504730781961 4052739537881 6557470319842 10610209857723 17167680177565 27777890035288 44945570212853 72723460248141 117669030460994 190392490709135 308061521170129 498454011879264 806515533049393 1304969544928657 2111485077978050 3416454622906707 5527939700884757 8944394323791464 14472334024676221 23416728348467685 37889062373143906 61305790721611591 99194853094755497 160500643816367088 259695496911122585 420196140727489673 679891637638612258 1100087778366101931 1779979416004714189 2880067194370816120 4660046610375530309 7540113804746346429 12200160415121876738 19740274219868223167 31940434634990099905 51680708854858323072 83621143489848422977 135301852344706746049 218922995834555169026 354224848179261915075 573147844013817084101 927372692193078999176 1500520536206896083277 2427893228399975082453 3928413764606871165730 6356306993006846248183 10284720757613717413913 16641027750620563662096 26925748508234281076009 43566776258854844738105 70492524767089125814114 114059301025943970552219 184551825793033096366333 298611126818977066918552 483162952612010163284885 781774079430987230203437 1264937032042997393488322 2046711111473984623691759 3311648143516982017180081 5358359254990966640871840 8670007398507948658051921 14028366653498915298923761 22698374052006863956975682 36726740705505779255899443 59425114757512643212875125 96151855463018422468774568 155576970220531065681649693 251728825683549488150424261 407305795904080553832073954 659034621587630041982498215 1066340417491710595814572169 1725375039079340637797070384 2791715456571051233611642553 4517090495650391871408712937 7308805952221443105020355490 11825896447871834976429068427 19134702400093278081449423917 30960598847965113057878492344 50095301248058391139327916261 81055900096023504197206408605 131151201344081895336534324866 212207101440105399533740733471 343358302784187294870275058337 555565404224292694404015791808 898923707008479989274290850145 1454489111232772683678306641953 2353412818241252672952597492098 3807901929474025356630904134051 6161314747715278029583501626149 9969216677189303386214405760200 16130531424904581415797907386349 26099748102093884802012313146549 42230279526998466217810220532898 68330027629092351019822533679447 110560307156090817237632754212345 178890334785183168257455287891792 289450641941273985495088042104137 468340976726457153752543329995929 757791618667731139247631372100066 1226132595394188293000174702095995 1983924214061919432247806074196061 3210056809456107725247980776292056 5193981023518027157495786850488117 8404037832974134882743767626780173 13598018856492162040239554477268290 22002056689466296922983322104048463 35600075545958458963222876581316753 57602132235424755886206198685365216 93202207781383214849429075266681969 150804340016807970735635273952047185 244006547798191185585064349218729154 394810887814999156320699623170776339 638817435613190341905763972389505493 1033628323428189498226463595560281832 1672445759041379840132227567949787325 2706074082469569338358691163510069157 4378519841510949178490918731459856482 7084593923980518516849609894969925639 11463113765491467695340528626429782121 18547707689471986212190138521399707760
|
||||
|
||||
# Extrapolated from [3@21, 144@77]: (144 - 3) / (77 - 21)
|
||||
eval instant at 80s rate(metric[1m])
|
||||
eval instant at 80s rate(metric_total[1m])
|
||||
{} 2.517857143
|
||||
|
||||
# Extrapolated to range start for counter, [2@20, 144@80]: (144 - 2) / (80 - 20)
|
||||
eval instant at 80s rate(metric[1m500ms:10s])
|
||||
eval instant at 80s rate(metric_total[1m500ms:10s])
|
||||
{} 2.3666666666666667
|
||||
|
||||
# Extrapolated to zero value for counter, [2@20, 144@80]: (144 - 0) / 61
|
||||
eval instant at 80s rate(metric[1m1s:10s])
|
||||
eval instant at 80s rate(metric_total[1m1s:10s])
|
||||
{} 2.360655737704918
|
||||
|
||||
# Only one value between 10s and 20s, 2@14
|
||||
eval instant at 20s min_over_time(metric[10s])
|
||||
eval instant at 20s min_over_time(metric_total[10s])
|
||||
{} 2
|
||||
|
||||
# min(2@20)
|
||||
eval instant at 20s min_over_time(metric[15s:10s])
|
||||
eval instant at 20s min_over_time(metric_total[15s:10s])
|
||||
{} 1
|
||||
|
||||
eval instant at 20m min_over_time(rate(metric[5m])[20m:1m])
|
||||
eval instant at 20m min_over_time(rate(metric_total[5m])[20m:1m])
|
||||
{} 0.12119047619047618
|
||||
|
||||
|
|
|
@ -51,20 +51,22 @@ var excludedLabels = []string{
|
|||
labels.BucketLabel,
|
||||
}
|
||||
|
||||
type bucket struct {
|
||||
upperBound float64
|
||||
count float64
|
||||
// Bucket represents a bucket of a classic histogram. It is used internally by the promql
|
||||
// package, but it is nevertheless exported for potential use in other PromQL engines.
|
||||
type Bucket struct {
|
||||
UpperBound float64
|
||||
Count float64
|
||||
}
|
||||
|
||||
// buckets implements sort.Interface.
|
||||
type buckets []bucket
|
||||
// Buckets implements sort.Interface.
|
||||
type Buckets []Bucket
|
||||
|
||||
type metricWithBuckets struct {
|
||||
metric labels.Labels
|
||||
buckets buckets
|
||||
buckets Buckets
|
||||
}
|
||||
|
||||
// bucketQuantile calculates the quantile 'q' based on the given buckets. The
|
||||
// BucketQuantile calculates the quantile 'q' based on the given buckets. The
|
||||
// buckets will be sorted by upperBound by this function (i.e. no sorting
|
||||
// needed before calling this function). The quantile value is interpolated
|
||||
// assuming a linear distribution within a bucket. However, if the quantile
|
||||
|
@ -95,7 +97,14 @@ type metricWithBuckets struct {
|
|||
// and another bool to indicate if small differences between buckets (that
|
||||
// are likely artifacts of floating point precision issues) have been
|
||||
// ignored.
|
||||
func bucketQuantile(q float64, buckets buckets) (float64, bool, bool) {
|
||||
//
|
||||
// Generically speaking, BucketQuantile is for calculating the
|
||||
// histogram_quantile() of classic histograms. See also: HistogramQuantile
|
||||
// for native histograms.
|
||||
//
|
||||
// BucketQuantile is exported as a useful quantile function over a set of
|
||||
// given buckets. It may be used by other PromQL engine implementations.
|
||||
func BucketQuantile(q float64, buckets Buckets) (float64, bool, bool) {
|
||||
if math.IsNaN(q) {
|
||||
return math.NaN(), false, false
|
||||
}
|
||||
|
@ -105,17 +114,17 @@ func bucketQuantile(q float64, buckets buckets) (float64, bool, bool) {
|
|||
if q > 1 {
|
||||
return math.Inf(+1), false, false
|
||||
}
|
||||
slices.SortFunc(buckets, func(a, b bucket) int {
|
||||
slices.SortFunc(buckets, func(a, b Bucket) int {
|
||||
// We don't expect the bucket boundary to be a NaN.
|
||||
if a.upperBound < b.upperBound {
|
||||
if a.UpperBound < b.UpperBound {
|
||||
return -1
|
||||
}
|
||||
if a.upperBound > b.upperBound {
|
||||
if a.UpperBound > b.UpperBound {
|
||||
return +1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
if !math.IsInf(buckets[len(buckets)-1].upperBound, +1) {
|
||||
if !math.IsInf(buckets[len(buckets)-1].UpperBound, +1) {
|
||||
return math.NaN(), false, false
|
||||
}
|
||||
|
||||
|
@ -125,33 +134,33 @@ func bucketQuantile(q float64, buckets buckets) (float64, bool, bool) {
|
|||
if len(buckets) < 2 {
|
||||
return math.NaN(), false, false
|
||||
}
|
||||
observations := buckets[len(buckets)-1].count
|
||||
observations := buckets[len(buckets)-1].Count
|
||||
if observations == 0 {
|
||||
return math.NaN(), false, false
|
||||
}
|
||||
rank := q * observations
|
||||
b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].count >= rank })
|
||||
b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].Count >= rank })
|
||||
|
||||
if b == len(buckets)-1 {
|
||||
return buckets[len(buckets)-2].upperBound, forcedMonotonic, fixedPrecision
|
||||
return buckets[len(buckets)-2].UpperBound, forcedMonotonic, fixedPrecision
|
||||
}
|
||||
if b == 0 && buckets[0].upperBound <= 0 {
|
||||
return buckets[0].upperBound, forcedMonotonic, fixedPrecision
|
||||
if b == 0 && buckets[0].UpperBound <= 0 {
|
||||
return buckets[0].UpperBound, forcedMonotonic, fixedPrecision
|
||||
}
|
||||
var (
|
||||
bucketStart float64
|
||||
bucketEnd = buckets[b].upperBound
|
||||
count = buckets[b].count
|
||||
bucketEnd = buckets[b].UpperBound
|
||||
count = buckets[b].Count
|
||||
)
|
||||
if b > 0 {
|
||||
bucketStart = buckets[b-1].upperBound
|
||||
count -= buckets[b-1].count
|
||||
rank -= buckets[b-1].count
|
||||
bucketStart = buckets[b-1].UpperBound
|
||||
count -= buckets[b-1].Count
|
||||
rank -= buckets[b-1].Count
|
||||
}
|
||||
return bucketStart + (bucketEnd-bucketStart)*(rank/count), forcedMonotonic, fixedPrecision
|
||||
}
|
||||
|
||||
// histogramQuantile calculates the quantile 'q' based on the given histogram.
|
||||
// HistogramQuantile calculates the quantile 'q' based on the given histogram.
|
||||
//
|
||||
// For custom buckets, the result is interpolated linearly, i.e. it is assumed
|
||||
// the observations are uniformly distributed within each bucket. (This is a
|
||||
|
@ -186,7 +195,13 @@ func bucketQuantile(q float64, buckets buckets) (float64, bool, bool) {
|
|||
// If q>1, +Inf is returned.
|
||||
//
|
||||
// If q is NaN, NaN is returned.
|
||||
func histogramQuantile(q float64, h *histogram.FloatHistogram) float64 {
|
||||
//
|
||||
// HistogramQuantile is for calculating the histogram_quantile() of native
|
||||
// histograms. See also: BucketQuantile for classic histograms.
|
||||
//
|
||||
// HistogramQuantile is exported as it may be used by other PromQL engine
|
||||
// implementations.
|
||||
func HistogramQuantile(q float64, h *histogram.FloatHistogram) float64 {
|
||||
if q < 0 {
|
||||
return math.Inf(-1)
|
||||
}
|
||||
|
@ -297,11 +312,11 @@ func histogramQuantile(q float64, h *histogram.FloatHistogram) float64 {
|
|||
return -math.Exp2(logUpper + (logLower-logUpper)*(1-fraction))
|
||||
}
|
||||
|
||||
// histogramFraction calculates the fraction of observations between the
|
||||
// HistogramFraction calculates the fraction of observations between the
|
||||
// provided lower and upper bounds, based on the provided histogram.
|
||||
//
|
||||
// histogramFraction is in a certain way the inverse of histogramQuantile. If
|
||||
// histogramQuantile(0.9, h) returns 123.4, then histogramFraction(-Inf, 123.4, h)
|
||||
// HistogramFraction is in a certain way the inverse of histogramQuantile. If
|
||||
// HistogramQuantile(0.9, h) returns 123.4, then HistogramFraction(-Inf, 123.4, h)
|
||||
// returns 0.9.
|
||||
//
|
||||
// The same notes with regard to interpolation and assumptions about the zero
|
||||
|
@ -328,7 +343,10 @@ func histogramQuantile(q float64, h *histogram.FloatHistogram) float64 {
|
|||
// If lower or upper is NaN, NaN is returned.
|
||||
//
|
||||
// If lower >= upper and the histogram has at least 1 observation, zero is returned.
|
||||
func histogramFraction(lower, upper float64, h *histogram.FloatHistogram) float64 {
|
||||
//
|
||||
// HistogramFraction is exported as it may be used by other PromQL engine
|
||||
// implementations.
|
||||
func HistogramFraction(lower, upper float64, h *histogram.FloatHistogram) float64 {
|
||||
if h.Count == 0 || math.IsNaN(lower) || math.IsNaN(upper) {
|
||||
return math.NaN()
|
||||
}
|
||||
|
@ -434,12 +452,12 @@ func histogramFraction(lower, upper float64, h *histogram.FloatHistogram) float6
|
|||
// coalesceBuckets merges buckets with the same upper bound.
|
||||
//
|
||||
// The input buckets must be sorted.
|
||||
func coalesceBuckets(buckets buckets) buckets {
|
||||
func coalesceBuckets(buckets Buckets) Buckets {
|
||||
last := buckets[0]
|
||||
i := 0
|
||||
for _, b := range buckets[1:] {
|
||||
if b.upperBound == last.upperBound {
|
||||
last.count += b.count
|
||||
if b.UpperBound == last.UpperBound {
|
||||
last.Count += b.Count
|
||||
} else {
|
||||
buckets[i] = last
|
||||
last = b
|
||||
|
@ -476,11 +494,11 @@ func coalesceBuckets(buckets buckets) buckets {
|
|||
//
|
||||
// We return a bool to indicate if this monotonicity was forced or not, and
|
||||
// another bool to indicate if small deltas were ignored or not.
|
||||
func ensureMonotonicAndIgnoreSmallDeltas(buckets buckets, tolerance float64) (bool, bool) {
|
||||
func ensureMonotonicAndIgnoreSmallDeltas(buckets Buckets, tolerance float64) (bool, bool) {
|
||||
var forcedMonotonic, fixedPrecision bool
|
||||
prev := buckets[0].count
|
||||
prev := buckets[0].Count
|
||||
for i := 1; i < len(buckets); i++ {
|
||||
curr := buckets[i].count // Assumed always positive.
|
||||
curr := buckets[i].Count // Assumed always positive.
|
||||
if curr == prev {
|
||||
// No correction needed if the counts are identical between buckets.
|
||||
continue
|
||||
|
@ -489,14 +507,14 @@ func ensureMonotonicAndIgnoreSmallDeltas(buckets buckets, tolerance float64) (bo
|
|||
// Silently correct numerically insignificant differences from floating
|
||||
// point precision errors, regardless of direction.
|
||||
// Do not update the 'prev' value as we are ignoring the difference.
|
||||
buckets[i].count = prev
|
||||
buckets[i].Count = prev
|
||||
fixedPrecision = true
|
||||
continue
|
||||
}
|
||||
if curr < prev {
|
||||
// Force monotonicity by removing any decreases regardless of magnitude.
|
||||
// Do not update the 'prev' value as we are ignoring the decrease.
|
||||
buckets[i].count = prev
|
||||
buckets[i].Count = prev
|
||||
forcedMonotonic = true
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -24,29 +24,29 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
|||
eps := 1e-12
|
||||
|
||||
for name, tc := range map[string]struct {
|
||||
getInput func() buckets // The buckets can be modified in-place so return a new one each time.
|
||||
getInput func() Buckets // The buckets can be modified in-place so return a new one each time.
|
||||
expectedForced bool
|
||||
expectedFixed bool
|
||||
expectedValues map[float64]float64
|
||||
}{
|
||||
"simple - monotonic": {
|
||||
getInput: func() buckets {
|
||||
return buckets{
|
||||
getInput: func() Buckets {
|
||||
return Buckets{
|
||||
{
|
||||
upperBound: 10,
|
||||
count: 10,
|
||||
UpperBound: 10,
|
||||
Count: 10,
|
||||
}, {
|
||||
upperBound: 15,
|
||||
count: 15,
|
||||
UpperBound: 15,
|
||||
Count: 15,
|
||||
}, {
|
||||
upperBound: 20,
|
||||
count: 15,
|
||||
UpperBound: 20,
|
||||
Count: 15,
|
||||
}, {
|
||||
upperBound: 30,
|
||||
count: 15,
|
||||
UpperBound: 30,
|
||||
Count: 15,
|
||||
}, {
|
||||
upperBound: math.Inf(1),
|
||||
count: 15,
|
||||
UpperBound: math.Inf(1),
|
||||
Count: 15,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -60,23 +60,23 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
|||
},
|
||||
},
|
||||
"simple - non-monotonic middle": {
|
||||
getInput: func() buckets {
|
||||
return buckets{
|
||||
getInput: func() Buckets {
|
||||
return Buckets{
|
||||
{
|
||||
upperBound: 10,
|
||||
count: 10,
|
||||
UpperBound: 10,
|
||||
Count: 10,
|
||||
}, {
|
||||
upperBound: 15,
|
||||
count: 15,
|
||||
UpperBound: 15,
|
||||
Count: 15,
|
||||
}, {
|
||||
upperBound: 20,
|
||||
count: 15.00000000001, // Simulate the case there's a small imprecision in float64.
|
||||
UpperBound: 20,
|
||||
Count: 15.00000000001, // Simulate the case there's a small imprecision in float64.
|
||||
}, {
|
||||
upperBound: 30,
|
||||
count: 15,
|
||||
UpperBound: 30,
|
||||
Count: 15,
|
||||
}, {
|
||||
upperBound: math.Inf(1),
|
||||
count: 15,
|
||||
UpperBound: math.Inf(1),
|
||||
Count: 15,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -90,41 +90,41 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
|||
},
|
||||
},
|
||||
"real example - monotonic": {
|
||||
getInput: func() buckets {
|
||||
return buckets{
|
||||
getInput: func() Buckets {
|
||||
return Buckets{
|
||||
{
|
||||
upperBound: 1,
|
||||
count: 6454661.3014166197,
|
||||
UpperBound: 1,
|
||||
Count: 6454661.3014166197,
|
||||
}, {
|
||||
upperBound: 5,
|
||||
count: 8339611.2001912938,
|
||||
UpperBound: 5,
|
||||
Count: 8339611.2001912938,
|
||||
}, {
|
||||
upperBound: 10,
|
||||
count: 14118319.2444762159,
|
||||
UpperBound: 10,
|
||||
Count: 14118319.2444762159,
|
||||
}, {
|
||||
upperBound: 25,
|
||||
count: 14130031.5272856522,
|
||||
UpperBound: 25,
|
||||
Count: 14130031.5272856522,
|
||||
}, {
|
||||
upperBound: 50,
|
||||
count: 46001270.3030008152,
|
||||
UpperBound: 50,
|
||||
Count: 46001270.3030008152,
|
||||
}, {
|
||||
upperBound: 64,
|
||||
count: 46008473.8585563600,
|
||||
UpperBound: 64,
|
||||
Count: 46008473.8585563600,
|
||||
}, {
|
||||
upperBound: 80,
|
||||
count: 46008473.8585563600,
|
||||
UpperBound: 80,
|
||||
Count: 46008473.8585563600,
|
||||
}, {
|
||||
upperBound: 100,
|
||||
count: 46008473.8585563600,
|
||||
UpperBound: 100,
|
||||
Count: 46008473.8585563600,
|
||||
}, {
|
||||
upperBound: 250,
|
||||
count: 46008473.8585563600,
|
||||
UpperBound: 250,
|
||||
Count: 46008473.8585563600,
|
||||
}, {
|
||||
upperBound: 1000,
|
||||
count: 46008473.8585563600,
|
||||
UpperBound: 1000,
|
||||
Count: 46008473.8585563600,
|
||||
}, {
|
||||
upperBound: math.Inf(1),
|
||||
count: 46008473.8585563600,
|
||||
UpperBound: math.Inf(1),
|
||||
Count: 46008473.8585563600,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -138,41 +138,41 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
|||
},
|
||||
},
|
||||
"real example - non-monotonic": {
|
||||
getInput: func() buckets {
|
||||
return buckets{
|
||||
getInput: func() Buckets {
|
||||
return Buckets{
|
||||
{
|
||||
upperBound: 1,
|
||||
count: 6454661.3014166225,
|
||||
UpperBound: 1,
|
||||
Count: 6454661.3014166225,
|
||||
}, {
|
||||
upperBound: 5,
|
||||
count: 8339611.2001912957,
|
||||
UpperBound: 5,
|
||||
Count: 8339611.2001912957,
|
||||
}, {
|
||||
upperBound: 10,
|
||||
count: 14118319.2444762159,
|
||||
UpperBound: 10,
|
||||
Count: 14118319.2444762159,
|
||||
}, {
|
||||
upperBound: 25,
|
||||
count: 14130031.5272856504,
|
||||
UpperBound: 25,
|
||||
Count: 14130031.5272856504,
|
||||
}, {
|
||||
upperBound: 50,
|
||||
count: 46001270.3030008227,
|
||||
UpperBound: 50,
|
||||
Count: 46001270.3030008227,
|
||||
}, {
|
||||
upperBound: 64,
|
||||
count: 46008473.8585563824,
|
||||
UpperBound: 64,
|
||||
Count: 46008473.8585563824,
|
||||
}, {
|
||||
upperBound: 80,
|
||||
count: 46008473.8585563898,
|
||||
UpperBound: 80,
|
||||
Count: 46008473.8585563898,
|
||||
}, {
|
||||
upperBound: 100,
|
||||
count: 46008473.8585563824,
|
||||
UpperBound: 100,
|
||||
Count: 46008473.8585563824,
|
||||
}, {
|
||||
upperBound: 250,
|
||||
count: 46008473.8585563824,
|
||||
UpperBound: 250,
|
||||
Count: 46008473.8585563824,
|
||||
}, {
|
||||
upperBound: 1000,
|
||||
count: 46008473.8585563898,
|
||||
UpperBound: 1000,
|
||||
Count: 46008473.8585563898,
|
||||
}, {
|
||||
upperBound: math.Inf(1),
|
||||
count: 46008473.8585563824,
|
||||
UpperBound: math.Inf(1),
|
||||
Count: 46008473.8585563824,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -186,53 +186,53 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
|||
},
|
||||
},
|
||||
"real example 2 - monotonic": {
|
||||
getInput: func() buckets {
|
||||
return buckets{
|
||||
getInput: func() Buckets {
|
||||
return Buckets{
|
||||
{
|
||||
upperBound: 0.005,
|
||||
count: 9.6,
|
||||
UpperBound: 0.005,
|
||||
Count: 9.6,
|
||||
}, {
|
||||
upperBound: 0.01,
|
||||
count: 9.688888889,
|
||||
UpperBound: 0.01,
|
||||
Count: 9.688888889,
|
||||
}, {
|
||||
upperBound: 0.025,
|
||||
count: 9.755555556,
|
||||
UpperBound: 0.025,
|
||||
Count: 9.755555556,
|
||||
}, {
|
||||
upperBound: 0.05,
|
||||
count: 9.844444444,
|
||||
UpperBound: 0.05,
|
||||
Count: 9.844444444,
|
||||
}, {
|
||||
upperBound: 0.1,
|
||||
count: 9.888888889,
|
||||
UpperBound: 0.1,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 0.25,
|
||||
count: 9.888888889,
|
||||
UpperBound: 0.25,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 0.5,
|
||||
count: 9.888888889,
|
||||
UpperBound: 0.5,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 1,
|
||||
count: 9.888888889,
|
||||
UpperBound: 1,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 2.5,
|
||||
count: 9.888888889,
|
||||
UpperBound: 2.5,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 5,
|
||||
count: 9.888888889,
|
||||
UpperBound: 5,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 10,
|
||||
count: 9.888888889,
|
||||
UpperBound: 10,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 25,
|
||||
count: 9.888888889,
|
||||
UpperBound: 25,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 50,
|
||||
count: 9.888888889,
|
||||
UpperBound: 50,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 100,
|
||||
count: 9.888888889,
|
||||
UpperBound: 100,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: math.Inf(1),
|
||||
count: 9.888888889,
|
||||
UpperBound: math.Inf(1),
|
||||
Count: 9.888888889,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -246,53 +246,53 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
|||
},
|
||||
},
|
||||
"real example 2 - non-monotonic": {
|
||||
getInput: func() buckets {
|
||||
return buckets{
|
||||
getInput: func() Buckets {
|
||||
return Buckets{
|
||||
{
|
||||
upperBound: 0.005,
|
||||
count: 9.6,
|
||||
UpperBound: 0.005,
|
||||
Count: 9.6,
|
||||
}, {
|
||||
upperBound: 0.01,
|
||||
count: 9.688888889,
|
||||
UpperBound: 0.01,
|
||||
Count: 9.688888889,
|
||||
}, {
|
||||
upperBound: 0.025,
|
||||
count: 9.755555556,
|
||||
UpperBound: 0.025,
|
||||
Count: 9.755555556,
|
||||
}, {
|
||||
upperBound: 0.05,
|
||||
count: 9.844444444,
|
||||
UpperBound: 0.05,
|
||||
Count: 9.844444444,
|
||||
}, {
|
||||
upperBound: 0.1,
|
||||
count: 9.888888889,
|
||||
UpperBound: 0.1,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 0.25,
|
||||
count: 9.888888889,
|
||||
UpperBound: 0.25,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 0.5,
|
||||
count: 9.888888889,
|
||||
UpperBound: 0.5,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 1,
|
||||
count: 9.888888889,
|
||||
UpperBound: 1,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 2.5,
|
||||
count: 9.888888889,
|
||||
UpperBound: 2.5,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 5,
|
||||
count: 9.888888889,
|
||||
UpperBound: 5,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 10,
|
||||
count: 9.888888889001, // Simulate the case there's a small imprecision in float64.
|
||||
UpperBound: 10,
|
||||
Count: 9.888888889001, // Simulate the case there's a small imprecision in float64.
|
||||
}, {
|
||||
upperBound: 25,
|
||||
count: 9.888888889,
|
||||
UpperBound: 25,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: 50,
|
||||
count: 9.888888888999, // Simulate the case there's a small imprecision in float64.
|
||||
UpperBound: 50,
|
||||
Count: 9.888888888999, // Simulate the case there's a small imprecision in float64.
|
||||
}, {
|
||||
upperBound: 100,
|
||||
count: 9.888888889,
|
||||
UpperBound: 100,
|
||||
Count: 9.888888889,
|
||||
}, {
|
||||
upperBound: math.Inf(1),
|
||||
count: 9.888888889,
|
||||
UpperBound: math.Inf(1),
|
||||
Count: 9.888888889,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -308,7 +308,7 @@ func TestBucketQuantile_ForcedMonotonicity(t *testing.T) {
|
|||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
for q, v := range tc.expectedValues {
|
||||
res, forced, fixed := bucketQuantile(q, tc.getInput())
|
||||
res, forced, fixed := BucketQuantile(q, tc.getInput())
|
||||
require.Equal(t, tc.expectedForced, forced)
|
||||
require.Equal(t, tc.expectedFixed, fixed)
|
||||
require.InEpsilon(t, v, res, eps)
|
||||
|
|
|
@ -99,9 +99,13 @@ type GroupOptions struct {
|
|||
|
||||
// NewGroup makes a new Group with the given name, options, and rules.
|
||||
func NewGroup(o GroupOptions) *Group {
|
||||
metrics := o.Opts.Metrics
|
||||
opts := o.Opts
|
||||
if opts == nil {
|
||||
opts = &ManagerOptions{}
|
||||
}
|
||||
metrics := opts.Metrics
|
||||
if metrics == nil {
|
||||
metrics = NewGroupMetrics(o.Opts.Registerer)
|
||||
metrics = NewGroupMetrics(opts.Registerer)
|
||||
}
|
||||
|
||||
key := GroupKey(o.File, o.Name)
|
||||
|
@ -120,13 +124,13 @@ func NewGroup(o GroupOptions) *Group {
|
|||
evalIterationFunc = DefaultEvalIterationFunc
|
||||
}
|
||||
|
||||
concurrencyController := o.Opts.RuleConcurrencyController
|
||||
concurrencyController := opts.RuleConcurrencyController
|
||||
if concurrencyController == nil {
|
||||
concurrencyController = sequentialRuleEvalController{}
|
||||
}
|
||||
|
||||
if o.Opts.Logger == nil {
|
||||
promslog.NewNopLogger()
|
||||
if opts.Logger == nil {
|
||||
opts.Logger = promslog.NewNopLogger()
|
||||
}
|
||||
|
||||
return &Group{
|
||||
|
@ -137,12 +141,12 @@ func NewGroup(o GroupOptions) *Group {
|
|||
limit: o.Limit,
|
||||
rules: o.Rules,
|
||||
shouldRestore: o.ShouldRestore,
|
||||
opts: o.Opts,
|
||||
opts: opts,
|
||||
seriesInPreviousEval: make([]map[string]labels.Labels, len(o.Rules)),
|
||||
done: make(chan struct{}),
|
||||
managerDone: o.done,
|
||||
terminated: make(chan struct{}),
|
||||
logger: o.Opts.Logger.With("file", o.File, "group", o.Name),
|
||||
logger: opts.Logger.With("file", o.File, "group", o.Name),
|
||||
metrics: metrics,
|
||||
evalIterationFunc: evalIterationFunc,
|
||||
concurrencyController: concurrencyController,
|
||||
|
|
|
@ -17,9 +17,18 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/common/promslog"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewGroup(t *testing.T) {
|
||||
g := NewGroup(GroupOptions{
|
||||
File: "test-file",
|
||||
Name: "test-name",
|
||||
})
|
||||
require.Equal(t, promslog.NewNopLogger().With("file", "test-file", "group", "test-name"), g.logger)
|
||||
}
|
||||
|
||||
func TestGroup_Equals(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
first *Group
|
||||
|
|
|
@ -38,6 +38,8 @@ import (
|
|||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
|
||||
"github.com/prometheus/prometheus/model/timestamp"
|
||||
|
||||
"github.com/prometheus/prometheus/config"
|
||||
|
@ -468,10 +470,8 @@ func TestPopulateLabels(t *testing.T) {
|
|||
func loadConfiguration(t testing.TB, c string) *config.Config {
|
||||
t.Helper()
|
||||
|
||||
cfg := &config.Config{}
|
||||
err := yaml.UnmarshalStrict([]byte(c), cfg)
|
||||
require.NoError(t, err, "Unable to load YAML config.")
|
||||
|
||||
cfg, err := config.Load(c, promslog.NewNopLogger())
|
||||
require.NoError(t, err)
|
||||
return cfg
|
||||
}
|
||||
|
||||
|
@ -724,33 +724,6 @@ scrape_configs:
|
|||
require.ElementsMatch(t, []string{"job1", "job3"}, scrapeManager.ScrapePools())
|
||||
}
|
||||
|
||||
func setupScrapeManager(t *testing.T, honorTimestamps, enableCTZeroIngestion bool) (*collectResultAppender, *Manager) {
|
||||
app := &collectResultAppender{}
|
||||
scrapeManager, err := NewManager(
|
||||
&Options{
|
||||
EnableCreatedTimestampZeroIngestion: enableCTZeroIngestion,
|
||||
skipOffsetting: true,
|
||||
},
|
||||
promslog.New(&promslog.Config{}),
|
||||
nil,
|
||||
&collectResultAppendable{app},
|
||||
prometheus.NewRegistry(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, scrapeManager.ApplyConfig(&config.Config{
|
||||
GlobalConfig: config.GlobalConfig{
|
||||
// Disable regular scrapes.
|
||||
ScrapeInterval: model.Duration(9999 * time.Minute),
|
||||
ScrapeTimeout: model.Duration(5 * time.Second),
|
||||
ScrapeProtocols: []config.ScrapeProtocol{config.OpenMetricsText1_0_0, config.PrometheusProto},
|
||||
},
|
||||
ScrapeConfigs: []*config.ScrapeConfig{{JobName: "test", HonorTimestamps: honorTimestamps}},
|
||||
}))
|
||||
|
||||
return app, scrapeManager
|
||||
}
|
||||
|
||||
func setupTestServer(t *testing.T, typ string, toWrite []byte) *httptest.Server {
|
||||
once := sync.Once{}
|
||||
|
||||
|
@ -789,6 +762,9 @@ func TestManagerCTZeroIngestion(t *testing.T) {
|
|||
t.Run(fmt.Sprintf("withCT=%v", testWithCT), func(t *testing.T) {
|
||||
for _, testCTZeroIngest := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("ctZeroIngest=%v", testCTZeroIngest), func(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
sampleTs := time.Now()
|
||||
ctTs := time.Time{}
|
||||
if testWithCT {
|
||||
|
@ -797,10 +773,45 @@ func TestManagerCTZeroIngestion(t *testing.T) {
|
|||
|
||||
// TODO(bwplotka): Add more types than just counter?
|
||||
encoded := prepareTestEncodedCounter(t, testFormat, expectedMetricName, expectedSampleValue, sampleTs, ctTs)
|
||||
app, scrapeManager := setupScrapeManager(t, true, testCTZeroIngest)
|
||||
|
||||
// Perform the test.
|
||||
doOneScrape(t, scrapeManager, app, setupTestServer(t, config.ScrapeProtocolsHeaders[testFormat], encoded))
|
||||
app := &collectResultAppender{}
|
||||
discoveryManager, scrapeManager := runManagers(t, ctx, &Options{
|
||||
EnableCreatedTimestampZeroIngestion: testCTZeroIngest,
|
||||
skipOffsetting: true,
|
||||
}, &collectResultAppendable{app})
|
||||
defer scrapeManager.Stop()
|
||||
|
||||
server := setupTestServer(t, config.ScrapeProtocolsHeaders[testFormat], encoded)
|
||||
serverURL, err := url.Parse(server.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
testConfig := fmt.Sprintf(`
|
||||
global:
|
||||
# Disable regular scrapes.
|
||||
scrape_interval: 9999m
|
||||
scrape_timeout: 5s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: test
|
||||
honor_timestamps: true
|
||||
static_configs:
|
||||
- targets: ['%s']
|
||||
`, serverURL.Host)
|
||||
applyConfig(t, testConfig, scrapeManager, discoveryManager)
|
||||
|
||||
// Wait for one scrape.
|
||||
ctx, cancel = context.WithTimeout(ctx, 1*time.Minute)
|
||||
defer cancel()
|
||||
require.NoError(t, runutil.Retry(100*time.Millisecond, ctx.Done(), func() error {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
// Check if scrape happened and grab the relevant samples.
|
||||
if len(app.resultFloats) > 0 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("expected some float samples, got none")
|
||||
}), "after 1 minute")
|
||||
|
||||
// Verify results.
|
||||
// Verify what we got vs expectations around CT injection.
|
||||
|
@ -871,39 +882,6 @@ func prepareTestEncodedCounter(t *testing.T, format config.ScrapeProtocol, mName
|
|||
}
|
||||
}
|
||||
|
||||
func doOneScrape(t *testing.T, manager *Manager, appender *collectResultAppender, server *httptest.Server) {
|
||||
t.Helper()
|
||||
|
||||
serverURL, err := url.Parse(server.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add fake target directly into tsets + reload
|
||||
manager.updateTsets(map[string][]*targetgroup.Group{
|
||||
"test": {{
|
||||
Targets: []model.LabelSet{{
|
||||
model.SchemeLabel: model.LabelValue(serverURL.Scheme),
|
||||
model.AddressLabel: model.LabelValue(serverURL.Host),
|
||||
}},
|
||||
}},
|
||||
})
|
||||
manager.reload()
|
||||
|
||||
// Wait for one scrape.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
|
||||
defer cancel()
|
||||
require.NoError(t, runutil.Retry(100*time.Millisecond, ctx.Done(), func() error {
|
||||
appender.mtx.Lock()
|
||||
defer appender.mtx.Unlock()
|
||||
|
||||
// Check if scrape happened and grab the relevant samples.
|
||||
if len(appender.resultFloats) > 0 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("expected some float samples, got none")
|
||||
}), "after 1 minute")
|
||||
manager.Stop()
|
||||
}
|
||||
|
||||
func findSamplesForMetric(floats []floatSample, metricName string) (ret []floatSample) {
|
||||
for _, f := range floats {
|
||||
if f.metric.Get(model.MetricNameLabel) == metricName {
|
||||
|
@ -978,37 +956,22 @@ func TestManagerCTZeroIngestionHistogram(t *testing.T) {
|
|||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
app := &collectResultAppender{}
|
||||
scrapeManager, err := NewManager(
|
||||
&Options{
|
||||
EnableCreatedTimestampZeroIngestion: tc.enableCTZeroIngestion,
|
||||
EnableNativeHistogramsIngestion: true,
|
||||
skipOffsetting: true,
|
||||
},
|
||||
promslog.New(&promslog.Config{}),
|
||||
nil,
|
||||
&collectResultAppendable{app},
|
||||
prometheus.NewRegistry(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
require.NoError(t, scrapeManager.ApplyConfig(&config.Config{
|
||||
GlobalConfig: config.GlobalConfig{
|
||||
// Disable regular scrapes.
|
||||
ScrapeInterval: model.Duration(9999 * time.Minute),
|
||||
ScrapeTimeout: model.Duration(5 * time.Second),
|
||||
// Ensure the proto is chosen. We need proto as it's the only protocol
|
||||
// with the CT parsing support.
|
||||
ScrapeProtocols: []config.ScrapeProtocol{config.PrometheusProto},
|
||||
},
|
||||
ScrapeConfigs: []*config.ScrapeConfig{{JobName: "test"}},
|
||||
}))
|
||||
app := &collectResultAppender{}
|
||||
discoveryManager, scrapeManager := runManagers(t, ctx, &Options{
|
||||
EnableCreatedTimestampZeroIngestion: tc.enableCTZeroIngestion,
|
||||
EnableNativeHistogramsIngestion: true,
|
||||
skipOffsetting: true,
|
||||
}, &collectResultAppendable{app})
|
||||
defer scrapeManager.Stop()
|
||||
|
||||
once := sync.Once{}
|
||||
// Start fake HTTP target to that allow one scrape only.
|
||||
server := httptest.NewServer(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fail := true // TODO(bwplotka): Kill or use?
|
||||
fail := true
|
||||
once.Do(func() {
|
||||
fail = false
|
||||
w.Header().Set("Content-Type", `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`)
|
||||
|
@ -1031,22 +994,23 @@ func TestManagerCTZeroIngestionHistogram(t *testing.T) {
|
|||
serverURL, err := url.Parse(server.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add fake target directly into tsets + reload. Normally users would use
|
||||
// Manager.Run and wait for minimum 5s refresh interval.
|
||||
scrapeManager.updateTsets(map[string][]*targetgroup.Group{
|
||||
"test": {{
|
||||
Targets: []model.LabelSet{{
|
||||
model.SchemeLabel: model.LabelValue(serverURL.Scheme),
|
||||
model.AddressLabel: model.LabelValue(serverURL.Host),
|
||||
}},
|
||||
}},
|
||||
})
|
||||
scrapeManager.reload()
|
||||
testConfig := fmt.Sprintf(`
|
||||
global:
|
||||
# Disable regular scrapes.
|
||||
scrape_interval: 9999m
|
||||
scrape_timeout: 5s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: test
|
||||
static_configs:
|
||||
- targets: ['%s']
|
||||
`, serverURL.Host)
|
||||
applyConfig(t, testConfig, scrapeManager, discoveryManager)
|
||||
|
||||
var got []histogramSample
|
||||
|
||||
// Wait for one scrape.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
|
||||
ctx, cancel = context.WithTimeout(ctx, 1*time.Minute)
|
||||
defer cancel()
|
||||
require.NoError(t, runutil.Retry(100*time.Millisecond, ctx.Done(), func() error {
|
||||
app.mtx.Lock()
|
||||
|
@ -1064,7 +1028,6 @@ func TestManagerCTZeroIngestionHistogram(t *testing.T) {
|
|||
}
|
||||
return errors.New("expected some histogram samples, got none")
|
||||
}), "after 1 minute")
|
||||
scrapeManager.Stop()
|
||||
|
||||
// Check for zero samples, assuming we only injected always one histogram sample.
|
||||
// Did it contain CT to inject? If yes, was CT zero enabled?
|
||||
|
@ -1118,9 +1081,17 @@ func applyConfig(
|
|||
require.NoError(t, discoveryManager.ApplyConfig(c))
|
||||
}
|
||||
|
||||
func runManagers(t *testing.T, ctx context.Context) (*discovery.Manager, *Manager) {
|
||||
func runManagers(t *testing.T, ctx context.Context, opts *Options, app storage.Appendable) (*discovery.Manager, *Manager) {
|
||||
t.Helper()
|
||||
|
||||
if opts == nil {
|
||||
opts = &Options{}
|
||||
}
|
||||
opts.DiscoveryReloadInterval = model.Duration(100 * time.Millisecond)
|
||||
if app == nil {
|
||||
app = nopAppendable{}
|
||||
}
|
||||
|
||||
reg := prometheus.NewRegistry()
|
||||
sdMetrics, err := discovery.RegisterSDMetrics(reg, discovery.NewRefreshMetrics(reg))
|
||||
require.NoError(t, err)
|
||||
|
@ -1132,10 +1103,10 @@ func runManagers(t *testing.T, ctx context.Context) (*discovery.Manager, *Manage
|
|||
discovery.Updatert(100*time.Millisecond),
|
||||
)
|
||||
scrapeManager, err := NewManager(
|
||||
&Options{DiscoveryReloadInterval: model.Duration(100 * time.Millisecond)},
|
||||
opts,
|
||||
nil,
|
||||
nil,
|
||||
nopAppendable{},
|
||||
app,
|
||||
prometheus.NewRegistry(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
@ -1213,7 +1184,7 @@ scrape_configs:
|
|||
- files: ['%s']
|
||||
`
|
||||
|
||||
discoveryManager, scrapeManager := runManagers(t, ctx)
|
||||
discoveryManager, scrapeManager := runManagers(t, ctx, nil, nil)
|
||||
defer scrapeManager.Stop()
|
||||
|
||||
applyConfig(
|
||||
|
@ -1312,7 +1283,7 @@ scrape_configs:
|
|||
file_sd_configs:
|
||||
- files: ['%s', '%s']
|
||||
`
|
||||
discoveryManager, scrapeManager := runManagers(t, ctx)
|
||||
discoveryManager, scrapeManager := runManagers(t, ctx, nil, nil)
|
||||
defer scrapeManager.Stop()
|
||||
|
||||
applyConfig(
|
||||
|
@ -1372,7 +1343,7 @@ scrape_configs:
|
|||
file_sd_configs:
|
||||
- files: ['%s']
|
||||
`
|
||||
discoveryManager, scrapeManager := runManagers(t, ctx)
|
||||
discoveryManager, scrapeManager := runManagers(t, ctx, nil, nil)
|
||||
defer scrapeManager.Stop()
|
||||
|
||||
applyConfig(
|
||||
|
@ -1439,7 +1410,7 @@ scrape_configs:
|
|||
- targets: ['%s']
|
||||
`
|
||||
|
||||
discoveryManager, scrapeManager := runManagers(t, ctx)
|
||||
discoveryManager, scrapeManager := runManagers(t, ctx, nil, nil)
|
||||
defer scrapeManager.Stop()
|
||||
|
||||
// Apply the initial config with an existing file
|
||||
|
|
|
@ -330,6 +330,8 @@ func (sp *scrapePool) restartLoops(reuseCache bool) {
|
|||
trackTimestampsStaleness = sp.config.TrackTimestampsStaleness
|
||||
mrc = sp.config.MetricRelabelConfigs
|
||||
fallbackScrapeProtocol = sp.config.ScrapeFallbackProtocol.HeaderMediaType()
|
||||
alwaysScrapeClassicHist = sp.config.AlwaysScrapeClassicHistograms
|
||||
convertClassicHistToNHCB = sp.config.ConvertClassicHistogramsToNHCB
|
||||
)
|
||||
|
||||
validationScheme := model.UTF8Validation
|
||||
|
@ -350,15 +352,16 @@ func (sp *scrapePool) restartLoops(reuseCache bool) {
|
|||
}
|
||||
|
||||
t := sp.activeTargets[fp]
|
||||
interval, timeout, err := t.intervalAndTimeout(interval, timeout)
|
||||
targetInterval, targetTimeout, err := t.intervalAndTimeout(interval, timeout)
|
||||
var (
|
||||
s = &targetScraper{
|
||||
Target: t,
|
||||
client: sp.client,
|
||||
timeout: timeout,
|
||||
timeout: targetTimeout,
|
||||
bodySizeLimit: bodySizeLimit,
|
||||
acceptHeader: acceptHeader(sp.config.ScrapeProtocols, validationScheme),
|
||||
acceptEncodingHeader: acceptEncodingHeader(enableCompression),
|
||||
metrics: sp.metrics,
|
||||
}
|
||||
newLoop = sp.newLoop(scrapeLoopOptions{
|
||||
target: t,
|
||||
|
@ -373,10 +376,12 @@ func (sp *scrapePool) restartLoops(reuseCache bool) {
|
|||
trackTimestampsStaleness: trackTimestampsStaleness,
|
||||
mrc: mrc,
|
||||
cache: cache,
|
||||
interval: interval,
|
||||
timeout: timeout,
|
||||
interval: targetInterval,
|
||||
timeout: targetTimeout,
|
||||
validationScheme: validationScheme,
|
||||
fallbackScrapeProtocol: fallbackScrapeProtocol,
|
||||
alwaysScrapeClassicHist: alwaysScrapeClassicHist,
|
||||
convertClassicHistToNHCB: convertClassicHistToNHCB,
|
||||
})
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -1421,7 +1426,7 @@ func (sl *scrapeLoop) scrapeAndReport(last, appendTime time.Time, errc chan<- er
|
|||
sl.l.Debug("Scrape failed", "err", scrapeErr)
|
||||
sl.scrapeFailureLoggerMtx.RLock()
|
||||
if sl.scrapeFailureLogger != nil {
|
||||
sl.scrapeFailureLogger.Error(scrapeErr.Error())
|
||||
sl.scrapeFailureLogger.Log(context.Background(), slog.LevelError, scrapeErr.Error())
|
||||
}
|
||||
sl.scrapeFailureLoggerMtx.RUnlock()
|
||||
if errc != nil {
|
||||
|
|
|
@ -65,10 +65,15 @@ func TestMain(m *testing.M) {
|
|||
testutil.TolerantVerifyLeak(m)
|
||||
}
|
||||
|
||||
func newTestScrapeMetrics(t testing.TB) *scrapeMetrics {
|
||||
func newTestRegistryAndScrapeMetrics(t testing.TB) (*prometheus.Registry, *scrapeMetrics) {
|
||||
reg := prometheus.NewRegistry()
|
||||
metrics, err := newScrapeMetrics(reg)
|
||||
require.NoError(t, err)
|
||||
return reg, metrics
|
||||
}
|
||||
|
||||
func newTestScrapeMetrics(t testing.TB) *scrapeMetrics {
|
||||
_, metrics := newTestRegistryAndScrapeMetrics(t)
|
||||
return metrics
|
||||
}
|
||||
|
||||
|
@ -370,6 +375,7 @@ func TestScrapePoolReload(t *testing.T) {
|
|||
return l
|
||||
}
|
||||
|
||||
reg, metrics := newTestRegistryAndScrapeMetrics(t)
|
||||
sp := &scrapePool{
|
||||
appendable: &nopAppendable{},
|
||||
activeTargets: map[uint64]*Target{},
|
||||
|
@ -377,7 +383,7 @@ func TestScrapePoolReload(t *testing.T) {
|
|||
newLoop: newLoop,
|
||||
logger: nil,
|
||||
client: http.DefaultClient,
|
||||
metrics: newTestScrapeMetrics(t),
|
||||
metrics: metrics,
|
||||
symbolTable: labels.NewSymbolTable(),
|
||||
}
|
||||
|
||||
|
@ -432,6 +438,12 @@ func TestScrapePoolReload(t *testing.T) {
|
|||
|
||||
require.Equal(t, sp.activeTargets, beforeTargets, "Reloading affected target states unexpectedly")
|
||||
require.Len(t, sp.loops, numTargets, "Unexpected number of stopped loops after reload")
|
||||
|
||||
got, err := gatherLabels(reg, "prometheus_target_reload_length_seconds")
|
||||
require.NoError(t, err)
|
||||
expectedName, expectedValue := "interval", "3s"
|
||||
require.Equal(t, [][]*dto.LabelPair{{{Name: &expectedName, Value: &expectedValue}}}, got)
|
||||
require.Equal(t, 1.0, prom_testutil.ToFloat64(sp.metrics.targetScrapePoolReloads))
|
||||
}
|
||||
|
||||
func TestScrapePoolReloadPreserveRelabeledIntervalTimeout(t *testing.T) {
|
||||
|
@ -447,6 +459,7 @@ func TestScrapePoolReloadPreserveRelabeledIntervalTimeout(t *testing.T) {
|
|||
}
|
||||
return l
|
||||
}
|
||||
reg, metrics := newTestRegistryAndScrapeMetrics(t)
|
||||
sp := &scrapePool{
|
||||
appendable: &nopAppendable{},
|
||||
activeTargets: map[uint64]*Target{
|
||||
|
@ -460,7 +473,7 @@ func TestScrapePoolReloadPreserveRelabeledIntervalTimeout(t *testing.T) {
|
|||
newLoop: newLoop,
|
||||
logger: nil,
|
||||
client: http.DefaultClient,
|
||||
metrics: newTestScrapeMetrics(t),
|
||||
metrics: metrics,
|
||||
symbolTable: labels.NewSymbolTable(),
|
||||
}
|
||||
|
||||
|
@ -468,6 +481,30 @@ func TestScrapePoolReloadPreserveRelabeledIntervalTimeout(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("unable to reload configuration: %s", err)
|
||||
}
|
||||
// Check that the reload metric is labeled with the pool interval, not the overridden interval.
|
||||
got, err := gatherLabels(reg, "prometheus_target_reload_length_seconds")
|
||||
require.NoError(t, err)
|
||||
expectedName, expectedValue := "interval", "3s"
|
||||
require.Equal(t, [][]*dto.LabelPair{{{Name: &expectedName, Value: &expectedValue}}}, got)
|
||||
}
|
||||
|
||||
// Gather metrics from the provided Gatherer with specified familyName,
|
||||
// and return all sets of name/value pairs.
|
||||
func gatherLabels(g prometheus.Gatherer, familyName string) ([][]*dto.LabelPair, error) {
|
||||
families, err := g.Gather()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make([][]*dto.LabelPair, 0)
|
||||
for _, f := range families {
|
||||
if f.GetName() == familyName {
|
||||
for _, m := range f.GetMetric() {
|
||||
ret = append(ret, m.GetLabel())
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func TestScrapePoolTargetLimit(t *testing.T) {
|
||||
|
@ -2609,7 +2646,7 @@ func TestTargetScraperScrapeOK(t *testing.T) {
|
|||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
accept := r.Header.Get("Accept")
|
||||
if allowUTF8 {
|
||||
require.Truef(t, strings.Contains(accept, "escaping=allow-utf-8"), "Expected Accept header to allow utf8, got %q", accept)
|
||||
require.Containsf(t, accept, "escaping=allow-utf-8", "Expected Accept header to allow utf8, got %q", accept)
|
||||
}
|
||||
if protobufParsing {
|
||||
require.True(t, strings.HasPrefix(accept, "application/vnd.google.protobuf;"),
|
||||
|
@ -4794,3 +4831,44 @@ func newScrapableServer(scrapeText string) (s *httptest.Server, scrapedTwice cha
|
|||
}
|
||||
})), scrapedTwice
|
||||
}
|
||||
|
||||
// Regression test for the panic fixed in https://github.com/prometheus/prometheus/pull/15523.
|
||||
func TestScrapePoolScrapeAfterReload(t *testing.T) {
|
||||
h := httptest.NewServer(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte{0x42, 0x42})
|
||||
},
|
||||
))
|
||||
t.Cleanup(h.Close)
|
||||
|
||||
cfg := &config.ScrapeConfig{
|
||||
BodySizeLimit: 1,
|
||||
JobName: "test",
|
||||
Scheme: "http",
|
||||
ScrapeInterval: model.Duration(100 * time.Millisecond),
|
||||
ScrapeTimeout: model.Duration(100 * time.Millisecond),
|
||||
EnableCompression: false,
|
||||
ServiceDiscoveryConfigs: discovery.Configs{
|
||||
&discovery.StaticConfig{
|
||||
{
|
||||
Targets: []model.LabelSet{{model.AddressLabel: model.LabelValue(h.URL)}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
p, err := newScrapePool(cfg, &nopAppendable{}, 0, nil, nil, &Options{}, newTestScrapeMetrics(t))
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(p.stop)
|
||||
|
||||
p.Sync([]*targetgroup.Group{
|
||||
{
|
||||
Targets: []model.LabelSet{{model.AddressLabel: model.LabelValue(strings.TrimPrefix(h.URL, "http://"))}},
|
||||
Source: "test",
|
||||
},
|
||||
})
|
||||
|
||||
require.NoError(t, p.reload(cfg))
|
||||
|
||||
<-time.After(1 * time.Second)
|
||||
}
|
||||
|
|
|
@ -354,6 +354,80 @@ func TestTargetsFromGroup(t *testing.T) {
|
|||
require.EqualError(t, failures[0], expectedError)
|
||||
}
|
||||
|
||||
// TestTargetsFromGroupWithLabelKeepDrop aims to demonstrate and reinforce the current behavior: relabeling's "labelkeep" and "labeldrop"
|
||||
// are applied to all labels of a target, including internal ones (labels starting with "__" such as "__address__").
|
||||
// This will be helpful for cases like https://github.com/prometheus/prometheus/issues/12355.
|
||||
func TestTargetsFromGroupWithLabelKeepDrop(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
cfgText string
|
||||
targets []model.LabelSet
|
||||
shouldDropTarget bool
|
||||
}{
|
||||
{
|
||||
name: "no relabeling",
|
||||
cfgText: `
|
||||
global:
|
||||
metric_name_validation_scheme: legacy
|
||||
scrape_configs:
|
||||
- job_name: job1
|
||||
static_configs:
|
||||
- targets: ["localhost:9090"]
|
||||
`,
|
||||
targets: []model.LabelSet{{model.AddressLabel: "localhost:9090"}},
|
||||
},
|
||||
{
|
||||
name: "labelkeep",
|
||||
cfgText: `
|
||||
global:
|
||||
metric_name_validation_scheme: legacy
|
||||
scrape_configs:
|
||||
- job_name: job1
|
||||
static_configs:
|
||||
- targets: ["localhost:9090"]
|
||||
relabel_configs:
|
||||
- regex: 'foo'
|
||||
action: labelkeep
|
||||
`,
|
||||
targets: []model.LabelSet{{model.AddressLabel: "localhost:9090"}},
|
||||
shouldDropTarget: true,
|
||||
},
|
||||
{
|
||||
name: "labeldrop",
|
||||
cfgText: `
|
||||
global:
|
||||
metric_name_validation_scheme: legacy
|
||||
scrape_configs:
|
||||
- job_name: job1
|
||||
static_configs:
|
||||
- targets: ["localhost:9090"]
|
||||
relabel_configs:
|
||||
- regex: '__address__'
|
||||
action: labeldrop
|
||||
`,
|
||||
targets: []model.LabelSet{{model.AddressLabel: "localhost:9090"}},
|
||||
shouldDropTarget: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
config := loadConfiguration(t, tt.cfgText)
|
||||
lb := labels.NewBuilder(labels.EmptyLabels())
|
||||
targets, failures := TargetsFromGroup(&targetgroup.Group{Targets: tt.targets}, config.ScrapeConfigs[0], nil, lb)
|
||||
|
||||
if tt.shouldDropTarget {
|
||||
require.Len(t, failures, 1)
|
||||
require.EqualError(t, failures[0], "instance 0 in group : no address")
|
||||
require.Empty(t, targets)
|
||||
} else {
|
||||
require.Empty(t, failures)
|
||||
require.Len(t, targets, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTargetsFromGroup(b *testing.B) {
|
||||
// Simulate Kubernetes service-discovery and use subset of rules from typical Prometheus config.
|
||||
cfgText := `
|
||||
|
|
|
@ -36,4 +36,4 @@ jobs:
|
|||
uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1
|
||||
with:
|
||||
args: --verbose
|
||||
version: v1.61.0
|
||||
version: v1.62.0
|
||||
|
|
|
@ -163,6 +163,11 @@ func TestWriteV2RequestFixture(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestValidateLabelsAndMetricName(t *testing.T) {
|
||||
oldScheme := model.NameValidationScheme
|
||||
model.NameValidationScheme = model.LegacyValidation
|
||||
defer func() {
|
||||
model.NameValidationScheme = oldScheme
|
||||
}()
|
||||
tests := []struct {
|
||||
input []prompb.Label
|
||||
expectedErr string
|
||||
|
@ -526,7 +531,7 @@ func TestFromQueryResultWithDuplicates(t *testing.T) {
|
|||
|
||||
require.True(t, isErrSeriesSet, "Expected resulting series to be an errSeriesSet")
|
||||
errMessage := errSeries.Err().Error()
|
||||
require.Equal(t, "duplicate label with name: foo", errMessage, fmt.Sprintf("Expected error to be from duplicate label, but got: %s", errMessage))
|
||||
require.Equalf(t, "duplicate label with name: foo", errMessage, "Expected error to be from duplicate label, but got: %s", errMessage)
|
||||
}
|
||||
|
||||
func TestNegotiateResponseType(t *testing.T) {
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
package remote
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -33,7 +32,7 @@ func TestIntern(t *testing.T) {
|
|||
interned, ok := interner.pool[testString]
|
||||
|
||||
require.True(t, ok)
|
||||
require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load()))
|
||||
require.Equalf(t, int64(1), interned.refs.Load(), "expected refs to be 1 but it was %d", interned.refs.Load())
|
||||
}
|
||||
|
||||
func TestIntern_MultiRef(t *testing.T) {
|
||||
|
@ -44,13 +43,13 @@ func TestIntern_MultiRef(t *testing.T) {
|
|||
interned, ok := interner.pool[testString]
|
||||
|
||||
require.True(t, ok)
|
||||
require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load()))
|
||||
require.Equalf(t, int64(1), interned.refs.Load(), "expected refs to be 1 but it was %d", interned.refs.Load())
|
||||
|
||||
interner.intern(testString)
|
||||
interned, ok = interner.pool[testString]
|
||||
|
||||
require.True(t, ok)
|
||||
require.Equal(t, int64(2), interned.refs.Load(), fmt.Sprintf("expected refs to be 2 but it was %d", interned.refs.Load()))
|
||||
require.Equalf(t, int64(2), interned.refs.Load(), "expected refs to be 2 but it was %d", interned.refs.Load())
|
||||
}
|
||||
|
||||
func TestIntern_DeleteRef(t *testing.T) {
|
||||
|
@ -61,7 +60,7 @@ func TestIntern_DeleteRef(t *testing.T) {
|
|||
interned, ok := interner.pool[testString]
|
||||
|
||||
require.True(t, ok)
|
||||
require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load()))
|
||||
require.Equalf(t, int64(1), interned.refs.Load(), "expected refs to be 1 but it was %d", interned.refs.Load())
|
||||
|
||||
interner.release(testString)
|
||||
_, ok = interner.pool[testString]
|
||||
|
@ -75,7 +74,7 @@ func TestIntern_MultiRef_Concurrent(t *testing.T) {
|
|||
interner.intern(testString)
|
||||
interned, ok := interner.pool[testString]
|
||||
require.True(t, ok)
|
||||
require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load()))
|
||||
require.Equalf(t, int64(1), interned.refs.Load(), "expected refs to be 1 but it was %d", interned.refs.Load())
|
||||
|
||||
go interner.release(testString)
|
||||
|
||||
|
@ -87,5 +86,5 @@ func TestIntern_MultiRef_Concurrent(t *testing.T) {
|
|||
interned, ok = interner.pool[testString]
|
||||
interner.mtx.RUnlock()
|
||||
require.True(t, ok)
|
||||
require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load()))
|
||||
require.Equalf(t, int64(1), interned.refs.Load(), "expected refs to be 1 but it was %d", interned.refs.Load())
|
||||
}
|
||||
|
|
|
@ -29,15 +29,15 @@ import (
|
|||
//
|
||||
// Labels that start with non-letter rune will be prefixed with "key_".
|
||||
// An exception is made for double-underscores which are allowed.
|
||||
func NormalizeLabel(label string, allowUTF8 bool) string {
|
||||
// Trivial case
|
||||
if len(label) == 0 || allowUTF8 {
|
||||
func NormalizeLabel(label string) string {
|
||||
// Trivial case.
|
||||
if len(label) == 0 {
|
||||
return label
|
||||
}
|
||||
|
||||
label = strutil.SanitizeLabelName(label)
|
||||
|
||||
// If label starts with a number, prepend with "key_"
|
||||
// If label starts with a number, prepend with "key_".
|
||||
if unicode.IsDigit(rune(label[0])) {
|
||||
label = "key_" + label
|
||||
} else if strings.HasPrefix(label, "_") && !strings.HasPrefix(label, "__") {
|
||||
|
|
|
@ -22,27 +22,24 @@ import (
|
|||
|
||||
func TestNormalizeLabel(t *testing.T) {
|
||||
tests := []struct {
|
||||
label string
|
||||
expected string
|
||||
expectedUTF8 string
|
||||
label string
|
||||
expected string
|
||||
}{
|
||||
{"", "", ""},
|
||||
{"label:with:colons", "label_with_colons", "label:with:colons"}, // Without UTF-8 support, colons are only allowed in metric names
|
||||
{"LabelWithCapitalLetters", "LabelWithCapitalLetters", "LabelWithCapitalLetters"},
|
||||
{"label!with&special$chars)", "label_with_special_chars_", "label!with&special$chars)"},
|
||||
{"label_with_foreign_characters_字符", "label_with_foreign_characters___", "label_with_foreign_characters_字符"},
|
||||
{"label.with.dots", "label_with_dots", "label.with.dots"},
|
||||
{"123label", "key_123label", "123label"},
|
||||
{"_label_starting_with_underscore", "key_label_starting_with_underscore", "_label_starting_with_underscore"},
|
||||
{"__label_starting_with_2underscores", "__label_starting_with_2underscores", "__label_starting_with_2underscores"},
|
||||
{"", ""},
|
||||
{"label:with:colons", "label_with_colons"},
|
||||
{"LabelWithCapitalLetters", "LabelWithCapitalLetters"},
|
||||
{"label!with&special$chars)", "label_with_special_chars_"},
|
||||
{"label_with_foreign_characters_字符", "label_with_foreign_characters___"},
|
||||
{"label.with.dots", "label_with_dots"},
|
||||
{"123label", "key_123label"},
|
||||
{"_label_starting_with_underscore", "key_label_starting_with_underscore"},
|
||||
{"__label_starting_with_2underscores", "__label_starting_with_2underscores"},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) {
|
||||
result := NormalizeLabel(test.label, false)
|
||||
result := NormalizeLabel(test.label)
|
||||
require.Equal(t, test.expected, result)
|
||||
uTF8result := NormalizeLabel(test.label, true)
|
||||
require.Equal(t, test.expectedUTF8, uTF8result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,17 +122,22 @@ func BuildCompliantName(metric pmetric.Metric, namespace string, addMetricSuffix
|
|||
|
||||
// Build a normalized name for the specified metric.
|
||||
func normalizeName(metric pmetric.Metric, namespace string, allowUTF8 bool) string {
|
||||
var translationFunc func(rune) bool
|
||||
var nameTokens []string
|
||||
var separators []string
|
||||
if !allowUTF8 {
|
||||
nonTokenMetricCharRE := regexp.MustCompile(`[^a-zA-Z0-9:]`)
|
||||
translationFunc = func(r rune) bool { return nonTokenMetricCharRE.MatchString(string(r)) }
|
||||
// Split metric name into "tokens" (of supported metric name runes).
|
||||
// Note that this has the side effect of replacing multiple consecutive underscores with a single underscore.
|
||||
// This is part of the OTel to Prometheus specification: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.38.0/specification/compatibility/prometheus_and_openmetrics.md#otlp-metric-points-to-prometheus.
|
||||
nameTokens = strings.FieldsFunc(
|
||||
metric.Name(),
|
||||
func(r rune) bool { return nonTokenMetricCharRE.MatchString(string(r)) },
|
||||
)
|
||||
} else {
|
||||
translationFunc = func(r rune) bool { return !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != ':' }
|
||||
translationFunc := func(r rune) bool { return !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != ':' }
|
||||
// Split metric name into "tokens" (of supported metric name runes).
|
||||
nameTokens, separators = fieldsFunc(metric.Name(), translationFunc)
|
||||
}
|
||||
// Split metric name into "tokens" (of supported metric name runes).
|
||||
// Note that this has the side effect of replacing multiple consecutive underscores with a single underscore.
|
||||
// This is part of the OTel to Prometheus specification: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.38.0/specification/compatibility/prometheus_and_openmetrics.md#otlp-metric-points-to-prometheus.
|
||||
nameTokens, separators := fieldsFunc(metric.Name(), translationFunc)
|
||||
|
||||
// Split unit at the '/' if any
|
||||
unitTokens := strings.SplitN(metric.Unit(), "/", 2)
|
||||
|
@ -201,12 +206,14 @@ func normalizeName(metric pmetric.Metric, namespace string, allowUTF8 bool) stri
|
|||
nameTokens = append([]string{namespace}, nameTokens...)
|
||||
}
|
||||
|
||||
// Build the string from the tokens + separators.
|
||||
// If UTF-8 isn't allowed, we'll use underscores as separators.
|
||||
var normalizedName string
|
||||
if !allowUTF8 {
|
||||
separators = []string{}
|
||||
// Build the string from the tokens, separated with underscores
|
||||
normalizedName = strings.Join(nameTokens, "_")
|
||||
} else {
|
||||
// Build the string from the tokens + separators.
|
||||
normalizedName = join(nameTokens, separators, "_")
|
||||
}
|
||||
normalizedName := join(nameTokens, separators, "_")
|
||||
|
||||
// Metric name cannot start with a digit, so prefix it with "_" in this case
|
||||
if normalizedName != "" && unicode.IsDigit(rune(normalizedName[0])) {
|
||||
|
|
|
@ -157,7 +157,10 @@ func createAttributes(resource pcommon.Resource, attributes pcommon.Map, setting
|
|||
// map ensures no duplicate label names.
|
||||
l := make(map[string]string, maxLabelCount)
|
||||
for _, label := range labels {
|
||||
var finalKey = prometheustranslator.NormalizeLabel(label.Name, settings.AllowUTF8)
|
||||
finalKey := label.Name
|
||||
if !settings.AllowUTF8 {
|
||||
finalKey = prometheustranslator.NormalizeLabel(finalKey)
|
||||
}
|
||||
if existingValue, alreadyExists := l[finalKey]; alreadyExists {
|
||||
l[finalKey] = existingValue + ";" + label.Value
|
||||
} else {
|
||||
|
@ -166,7 +169,10 @@ func createAttributes(resource pcommon.Resource, attributes pcommon.Map, setting
|
|||
}
|
||||
|
||||
for _, lbl := range promotedAttrs {
|
||||
normalized := prometheustranslator.NormalizeLabel(lbl.Name, settings.AllowUTF8)
|
||||
normalized := lbl.Name
|
||||
if !settings.AllowUTF8 {
|
||||
normalized = prometheustranslator.NormalizeLabel(normalized)
|
||||
}
|
||||
if _, exists := l[normalized]; !exists {
|
||||
l[normalized] = lbl.Value
|
||||
}
|
||||
|
@ -204,8 +210,8 @@ func createAttributes(resource pcommon.Resource, attributes pcommon.Map, setting
|
|||
log.Println("label " + name + " is overwritten. Check if Prometheus reserved labels are used.")
|
||||
}
|
||||
// internal labels should be maintained
|
||||
if !(len(name) > 4 && name[:2] == "__" && name[len(name)-2:] == "__") {
|
||||
name = prometheustranslator.NormalizeLabel(name, settings.AllowUTF8)
|
||||
if !settings.AllowUTF8 && !(len(name) > 4 && name[:2] == "__" && name[len(name)-2:] == "__") {
|
||||
name = prometheustranslator.NormalizeLabel(name)
|
||||
}
|
||||
l[name] = extras[i+1]
|
||||
}
|
||||
|
@ -600,6 +606,10 @@ func addResourceTargetInfo(resource pcommon.Resource, settings Settings, timesta
|
|||
}
|
||||
|
||||
settings.PromoteResourceAttributes = nil
|
||||
if settings.KeepIdentifyingResourceAttributes {
|
||||
// Do not pass identifying attributes as ignoreAttrs below.
|
||||
identifyingAttrs = nil
|
||||
}
|
||||
labels := createAttributes(resource, attributes, settings, identifyingAttrs, false, model.MetricNameLabel, name)
|
||||
haveIdentifier := false
|
||||
for _, l := range labels {
|
||||
|
|
|
@ -49,15 +49,44 @@ func TestCreateAttributes(t *testing.T) {
|
|||
}
|
||||
attrs := pcommon.NewMap()
|
||||
attrs.PutStr("metric-attr", "metric value")
|
||||
attrs.PutStr("metric-attr-other", "metric value other")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
promoteResourceAttributes []string
|
||||
ignoreAttrs []string
|
||||
expectedLabels []prompb.Label
|
||||
}{
|
||||
{
|
||||
name: "Successful conversion without resource attribute promotion",
|
||||
promoteResourceAttributes: nil,
|
||||
expectedLabels: []prompb.Label{
|
||||
{
|
||||
Name: "__name__",
|
||||
Value: "test_metric",
|
||||
},
|
||||
{
|
||||
Name: "instance",
|
||||
Value: "service ID",
|
||||
},
|
||||
{
|
||||
Name: "job",
|
||||
Value: "service name",
|
||||
},
|
||||
{
|
||||
Name: "metric_attr",
|
||||
Value: "metric value",
|
||||
},
|
||||
{
|
||||
Name: "metric_attr_other",
|
||||
Value: "metric value other",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Successful conversion with some attributes ignored",
|
||||
promoteResourceAttributes: nil,
|
||||
ignoreAttrs: []string{"metric-attr-other"},
|
||||
expectedLabels: []prompb.Label{
|
||||
{
|
||||
Name: "__name__",
|
||||
|
@ -97,6 +126,10 @@ func TestCreateAttributes(t *testing.T) {
|
|||
Name: "metric_attr",
|
||||
Value: "metric value",
|
||||
},
|
||||
{
|
||||
Name: "metric_attr_other",
|
||||
Value: "metric value other",
|
||||
},
|
||||
{
|
||||
Name: "existent_attr",
|
||||
Value: "resource value",
|
||||
|
@ -127,6 +160,10 @@ func TestCreateAttributes(t *testing.T) {
|
|||
Name: "metric_attr",
|
||||
Value: "metric value",
|
||||
},
|
||||
{
|
||||
Name: "metric_attr_other",
|
||||
Value: "metric value other",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -153,6 +190,10 @@ func TestCreateAttributes(t *testing.T) {
|
|||
Name: "metric_attr",
|
||||
Value: "metric value",
|
||||
},
|
||||
{
|
||||
Name: "metric_attr_other",
|
||||
Value: "metric value other",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -161,7 +202,7 @@ func TestCreateAttributes(t *testing.T) {
|
|||
settings := Settings{
|
||||
PromoteResourceAttributes: tc.promoteResourceAttributes,
|
||||
}
|
||||
lbls := createAttributes(resource, attrs, settings, nil, false, model.MetricNameLabel, "test_metric")
|
||||
lbls := createAttributes(resource, attrs, settings, tc.ignoreAttrs, false, model.MetricNameLabel, "test_metric")
|
||||
|
||||
assert.ElementsMatch(t, lbls, tc.expectedLabels)
|
||||
})
|
||||
|
|
|
@ -32,13 +32,14 @@ import (
|
|||
)
|
||||
|
||||
type Settings struct {
|
||||
Namespace string
|
||||
ExternalLabels map[string]string
|
||||
DisableTargetInfo bool
|
||||
ExportCreatedMetric bool
|
||||
AddMetricSuffixes bool
|
||||
AllowUTF8 bool
|
||||
PromoteResourceAttributes []string
|
||||
Namespace string
|
||||
ExternalLabels map[string]string
|
||||
DisableTargetInfo bool
|
||||
ExportCreatedMetric bool
|
||||
AddMetricSuffixes bool
|
||||
AllowUTF8 bool
|
||||
PromoteResourceAttributes []string
|
||||
KeepIdentifyingResourceAttributes bool
|
||||
}
|
||||
|
||||
// PrometheusConverter converts from OTel write format to Prometheus remote write format.
|
||||
|
|
|
@ -28,41 +28,72 @@ import (
|
|||
"go.opentelemetry.io/collector/pdata/pmetric"
|
||||
"go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp"
|
||||
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
prometheustranslator "github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheus"
|
||||
)
|
||||
|
||||
func TestFromMetrics(t *testing.T) {
|
||||
t.Run("successful", func(t *testing.T) {
|
||||
converter := NewPrometheusConverter()
|
||||
payload := createExportRequest(5, 128, 128, 2, 0)
|
||||
var expMetadata []prompb.MetricMetadata
|
||||
resourceMetricsSlice := payload.Metrics().ResourceMetrics()
|
||||
for i := 0; i < resourceMetricsSlice.Len(); i++ {
|
||||
scopeMetricsSlice := resourceMetricsSlice.At(i).ScopeMetrics()
|
||||
for j := 0; j < scopeMetricsSlice.Len(); j++ {
|
||||
metricSlice := scopeMetricsSlice.At(j).Metrics()
|
||||
for k := 0; k < metricSlice.Len(); k++ {
|
||||
metric := metricSlice.At(k)
|
||||
promName := prometheustranslator.BuildCompliantName(metric, "", false, false)
|
||||
expMetadata = append(expMetadata, prompb.MetricMetadata{
|
||||
Type: otelMetricTypeToPromMetricType(metric),
|
||||
MetricFamilyName: promName,
|
||||
Help: metric.Description(),
|
||||
Unit: metric.Unit(),
|
||||
})
|
||||
for _, keepIdentifyingResourceAttributes := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("successful/keepIdentifyingAttributes=%v", keepIdentifyingResourceAttributes), func(t *testing.T) {
|
||||
converter := NewPrometheusConverter()
|
||||
payload := createExportRequest(5, 128, 128, 2, 0)
|
||||
var expMetadata []prompb.MetricMetadata
|
||||
resourceMetricsSlice := payload.Metrics().ResourceMetrics()
|
||||
for i := 0; i < resourceMetricsSlice.Len(); i++ {
|
||||
scopeMetricsSlice := resourceMetricsSlice.At(i).ScopeMetrics()
|
||||
for j := 0; j < scopeMetricsSlice.Len(); j++ {
|
||||
metricSlice := scopeMetricsSlice.At(j).Metrics()
|
||||
for k := 0; k < metricSlice.Len(); k++ {
|
||||
metric := metricSlice.At(k)
|
||||
promName := prometheustranslator.BuildCompliantName(metric, "", false, false)
|
||||
expMetadata = append(expMetadata, prompb.MetricMetadata{
|
||||
Type: otelMetricTypeToPromMetricType(metric),
|
||||
MetricFamilyName: promName,
|
||||
Help: metric.Description(),
|
||||
Unit: metric.Unit(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
annots, err := converter.FromMetrics(context.Background(), payload.Metrics(), Settings{})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, annots)
|
||||
annots, err := converter.FromMetrics(
|
||||
context.Background(),
|
||||
payload.Metrics(),
|
||||
Settings{KeepIdentifyingResourceAttributes: keepIdentifyingResourceAttributes},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, annots)
|
||||
|
||||
if diff := cmp.Diff(expMetadata, converter.Metadata()); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
if diff := cmp.Diff(expMetadata, converter.Metadata()); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
ts := converter.TimeSeries()
|
||||
require.Len(t, ts, 1408+1) // +1 for the target_info.
|
||||
|
||||
target_info_count := 0
|
||||
for _, s := range ts {
|
||||
b := labels.NewScratchBuilder(2)
|
||||
lbls := s.ToLabels(&b, nil)
|
||||
if lbls.Get(labels.MetricName) == "target_info" {
|
||||
target_info_count++
|
||||
require.Equal(t, "test-namespace/test-service", lbls.Get("job"))
|
||||
require.Equal(t, "id1234", lbls.Get("instance"))
|
||||
if keepIdentifyingResourceAttributes {
|
||||
require.Equal(t, "test-service", lbls.Get("service_name"))
|
||||
require.Equal(t, "test-namespace", lbls.Get("service_namespace"))
|
||||
require.Equal(t, "id1234", lbls.Get("service_instance_id"))
|
||||
} else {
|
||||
require.False(t, lbls.Has("service_name"))
|
||||
require.False(t, lbls.Has("service_namespace"))
|
||||
require.False(t, lbls.Has("service_instance_id"))
|
||||
}
|
||||
}
|
||||
}
|
||||
require.Equal(t, 1, target_info_count)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("context cancellation", func(t *testing.T) {
|
||||
converter := NewPrometheusConverter()
|
||||
|
@ -169,6 +200,15 @@ func createExportRequest(resourceAttributeCount, histogramCount, nonHistogramCou
|
|||
rm := request.Metrics().ResourceMetrics().AppendEmpty()
|
||||
generateAttributes(rm.Resource().Attributes(), "resource", resourceAttributeCount)
|
||||
|
||||
// Fake some resource attributes.
|
||||
for k, v := range map[string]string{
|
||||
"service.name": "test-service",
|
||||
"service.namespace": "test-namespace",
|
||||
"service.instance.id": "id1234",
|
||||
} {
|
||||
rm.Resource().Attributes().PutStr(k, v)
|
||||
}
|
||||
|
||||
metrics := rm.ScopeMetrics().AppendEmpty().Metrics()
|
||||
ts := pcommon.NewTimestampFromTime(time.Now())
|
||||
|
||||
|
|
|
@ -957,13 +957,6 @@ func (t *QueueManager) Stop() {
|
|||
if t.mcfg.Send {
|
||||
t.metadataWatcher.Stop()
|
||||
}
|
||||
|
||||
// On shutdown, release the strings in the labels from the intern pool.
|
||||
t.seriesMtx.Lock()
|
||||
for _, labels := range t.seriesLabels {
|
||||
t.releaseLabels(labels)
|
||||
}
|
||||
t.seriesMtx.Unlock()
|
||||
t.metrics.unregister()
|
||||
}
|
||||
|
||||
|
@ -985,14 +978,6 @@ func (t *QueueManager) StoreSeries(series []record.RefSeries, index int) {
|
|||
continue
|
||||
}
|
||||
lbls := t.builder.Labels()
|
||||
t.internLabels(lbls)
|
||||
|
||||
// We should not ever be replacing a series labels in the map, but just
|
||||
// in case we do we need to ensure we do not leak the replaced interned
|
||||
// strings.
|
||||
if orig, ok := t.seriesLabels[s.Ref]; ok {
|
||||
t.releaseLabels(orig)
|
||||
}
|
||||
t.seriesLabels[s.Ref] = lbls
|
||||
}
|
||||
}
|
||||
|
@ -1037,7 +1022,6 @@ func (t *QueueManager) SeriesReset(index int) {
|
|||
for k, v := range t.seriesSegmentIndexes {
|
||||
if v < index {
|
||||
delete(t.seriesSegmentIndexes, k)
|
||||
t.releaseLabels(t.seriesLabels[k])
|
||||
delete(t.seriesLabels, k)
|
||||
delete(t.seriesMetadata, k)
|
||||
delete(t.droppedSeries, k)
|
||||
|
@ -1059,14 +1043,6 @@ func (t *QueueManager) client() WriteClient {
|
|||
return t.storeClient
|
||||
}
|
||||
|
||||
func (t *QueueManager) internLabels(lbls labels.Labels) {
|
||||
lbls.InternStrings(t.interner.intern)
|
||||
}
|
||||
|
||||
func (t *QueueManager) releaseLabels(ls labels.Labels) {
|
||||
ls.ReleaseStrings(t.interner.release)
|
||||
}
|
||||
|
||||
// processExternalLabels merges externalLabels into b. If b contains
|
||||
// a label in externalLabels, the value in b wins.
|
||||
func processExternalLabels(b *labels.Builder, externalLabels []labels.Label) {
|
||||
|
@ -1712,7 +1688,7 @@ func (s *shards) updateMetrics(_ context.Context, err error, sampleCount, exempl
|
|||
s.enqueuedHistograms.Sub(int64(histogramCount))
|
||||
}
|
||||
|
||||
// sendSamples to the remote storage with backoff for recoverable errors.
|
||||
// sendSamplesWithBackoff to the remote storage with backoff for recoverable errors.
|
||||
func (s *shards) sendSamplesWithBackoff(ctx context.Context, samples []prompb.TimeSeries, sampleCount, exemplarCount, histogramCount, metadataCount int, pBuf *proto.Buffer, buf *[]byte, enc Compression) (WriteResponseStats, error) {
|
||||
// Build the WriteRequest with no metadata.
|
||||
req, highest, lowest, err := buildWriteRequest(s.qm.logger, samples, nil, pBuf, buf, nil, enc)
|
||||
|
@ -1826,7 +1802,7 @@ func (s *shards) sendSamplesWithBackoff(ctx context.Context, samples []prompb.Ti
|
|||
return accumulatedStats, err
|
||||
}
|
||||
|
||||
// sendV2Samples to the remote storage with backoff for recoverable errors.
|
||||
// sendV2SamplesWithBackoff to the remote storage with backoff for recoverable errors.
|
||||
func (s *shards) sendV2SamplesWithBackoff(ctx context.Context, samples []writev2.TimeSeries, labels []string, sampleCount, exemplarCount, histogramCount, metadataCount int, pBuf, buf *[]byte, enc Compression) (WriteResponseStats, error) {
|
||||
// Build the WriteRequest with no metadata.
|
||||
req, highest, lowest, err := buildV2WriteRequest(s.qm.logger, samples, labels, pBuf, buf, nil, enc)
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"math"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"runtime/pprof"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -48,6 +49,7 @@ import (
|
|||
"github.com/prometheus/prometheus/scrape"
|
||||
"github.com/prometheus/prometheus/tsdb/chunks"
|
||||
"github.com/prometheus/prometheus/tsdb/record"
|
||||
"github.com/prometheus/prometheus/tsdb/wlog"
|
||||
"github.com/prometheus/prometheus/util/runutil"
|
||||
"github.com/prometheus/prometheus/util/testutil"
|
||||
)
|
||||
|
@ -1407,12 +1409,12 @@ func BenchmarkStartup(b *testing.B) {
|
|||
|
||||
// Find the second largest segment; we will replay up to this.
|
||||
// (Second largest as WALWatcher will start tailing the largest).
|
||||
dirents, err := os.ReadDir(dir)
|
||||
dirents, err := os.ReadDir(path.Join(dir, "wal"))
|
||||
require.NoError(b, err)
|
||||
|
||||
var segments []int
|
||||
for _, dirent := range dirents {
|
||||
if i, err := strconv.Atoi(dirent.Name()); err != nil {
|
||||
if i, err := strconv.Atoi(dirent.Name()); err == nil {
|
||||
segments = append(segments, i)
|
||||
}
|
||||
}
|
||||
|
@ -1424,13 +1426,15 @@ func BenchmarkStartup(b *testing.B) {
|
|||
mcfg := config.DefaultMetadataConfig
|
||||
for n := 0; n < b.N; n++ {
|
||||
metrics := newQueueManagerMetrics(nil, "", "")
|
||||
watcherMetrics := wlog.NewWatcherMetrics(nil)
|
||||
c := NewTestBlockedWriteClient()
|
||||
// todo: test with new proto type(s)
|
||||
m := NewQueueManager(metrics, nil, nil, logger, dir,
|
||||
m := NewQueueManager(metrics, watcherMetrics, nil, logger, dir,
|
||||
newEWMARate(ewmaWeight, shardUpdateDuration),
|
||||
cfg, mcfg, labels.EmptyLabels(), nil, c, 1*time.Minute, newPool(), newHighestTimestampMetric(), nil, false, false, config.RemoteWriteProtoMsgV1)
|
||||
m.watcher.SetStartTime(timestamp.Time(math.MaxInt64))
|
||||
m.watcher.MaxSegment = segments[len(segments)-2]
|
||||
m.watcher.SetMetrics()
|
||||
err := m.watcher.Run()
|
||||
require.NoError(b, err)
|
||||
}
|
||||
|
|
|
@ -512,9 +512,10 @@ func (h *otlpWriteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
converter := otlptranslator.NewPrometheusConverter()
|
||||
annots, err := converter.FromMetrics(r.Context(), req.Metrics(), otlptranslator.Settings{
|
||||
AddMetricSuffixes: true,
|
||||
AllowUTF8: otlpCfg.TranslationStrategy == config.NoUTF8EscapingWithSuffixes,
|
||||
PromoteResourceAttributes: otlpCfg.PromoteResourceAttributes,
|
||||
AddMetricSuffixes: true,
|
||||
AllowUTF8: otlpCfg.TranslationStrategy == config.NoUTF8EscapingWithSuffixes,
|
||||
PromoteResourceAttributes: otlpCfg.PromoteResourceAttributes,
|
||||
KeepIdentifyingResourceAttributes: otlpCfg.KeepIdentifyingResourceAttributes,
|
||||
})
|
||||
if err != nil {
|
||||
h.logger.Warn("Error translating OTLP metrics to Prometheus write request", "err", err)
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
|
@ -612,36 +611,36 @@ func testHistogramsSeriesToChunks(t *testing.T, test histogramTest) {
|
|||
encodedSample := encodedSamples[i]
|
||||
switch expectedSample := s.(type) {
|
||||
case hSample:
|
||||
require.Equal(t, chunkenc.ValHistogram, encodedSample.Type(), "expect histogram", fmt.Sprintf("at idx %d", i))
|
||||
require.Equalf(t, chunkenc.ValHistogram, encodedSample.Type(), "expect histogram at idx %d", i)
|
||||
h := encodedSample.H()
|
||||
// Ignore counter reset if not gauge here, will check on chunk level.
|
||||
if expectedSample.h.CounterResetHint != histogram.GaugeType {
|
||||
h.CounterResetHint = histogram.UnknownCounterReset
|
||||
}
|
||||
if value.IsStaleNaN(expectedSample.h.Sum) {
|
||||
require.True(t, value.IsStaleNaN(h.Sum), fmt.Sprintf("at idx %d", i))
|
||||
require.Truef(t, value.IsStaleNaN(h.Sum), "at idx %d", i)
|
||||
continue
|
||||
}
|
||||
require.Equal(t, *expectedSample.h, *h, fmt.Sprintf("at idx %d", i))
|
||||
require.Equalf(t, *expectedSample.h, *h, "at idx %d", i)
|
||||
case fhSample:
|
||||
require.Equal(t, chunkenc.ValFloatHistogram, encodedSample.Type(), "expect float histogram", fmt.Sprintf("at idx %d", i))
|
||||
require.Equalf(t, chunkenc.ValFloatHistogram, encodedSample.Type(), "expect float histogram at idx %d", i)
|
||||
fh := encodedSample.FH()
|
||||
// Ignore counter reset if not gauge here, will check on chunk level.
|
||||
if expectedSample.fh.CounterResetHint != histogram.GaugeType {
|
||||
fh.CounterResetHint = histogram.UnknownCounterReset
|
||||
}
|
||||
if value.IsStaleNaN(expectedSample.fh.Sum) {
|
||||
require.True(t, value.IsStaleNaN(fh.Sum), fmt.Sprintf("at idx %d", i))
|
||||
require.Truef(t, value.IsStaleNaN(fh.Sum), "at idx %d", i)
|
||||
continue
|
||||
}
|
||||
require.Equal(t, *expectedSample.fh, *fh, fmt.Sprintf("at idx %d", i))
|
||||
require.Equalf(t, *expectedSample.fh, *fh, "at idx %d", i)
|
||||
default:
|
||||
t.Error("internal error, unexpected type")
|
||||
}
|
||||
}
|
||||
|
||||
for i, expectedCounterResetHint := range test.expectedCounterResetHeaders {
|
||||
require.Equal(t, expectedCounterResetHint, getCounterResetHint(chks[i]), fmt.Sprintf("chunk at index %d", i))
|
||||
require.Equalf(t, expectedCounterResetHint, getCounterResetHint(chks[i]), "chunk at index %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -976,6 +976,7 @@ func (it *floatHistogramIterator) Reset(b []byte) {
|
|||
it.atFloatHistogramCalled = false
|
||||
it.pBuckets, it.nBuckets = nil, nil
|
||||
it.pSpans, it.nSpans = nil, nil
|
||||
it.customValues = nil
|
||||
} else {
|
||||
it.pBuckets, it.nBuckets = it.pBuckets[:0], it.nBuckets[:0]
|
||||
}
|
||||
|
@ -1071,7 +1072,7 @@ func (it *floatHistogramIterator) Next() ValueType {
|
|||
// The case for the 2nd sample with single deltas is implicitly handled correctly with the double delta code,
|
||||
// so we don't need a separate single delta logic for the 2nd sample.
|
||||
|
||||
// Recycle bucket and span slices that have not been returned yet. Otherwise, copy them.
|
||||
// Recycle bucket, span and custom value slices that have not been returned yet. Otherwise, copy them.
|
||||
// We can always recycle the slices for leading and trailing bits as they are
|
||||
// never returned to the caller.
|
||||
if it.atFloatHistogramCalled {
|
||||
|
@ -1104,6 +1105,13 @@ func (it *floatHistogramIterator) Next() ValueType {
|
|||
} else {
|
||||
it.nSpans = nil
|
||||
}
|
||||
if len(it.customValues) > 0 {
|
||||
newCustomValues := make([]float64, len(it.customValues))
|
||||
copy(newCustomValues, it.customValues)
|
||||
it.customValues = newCustomValues
|
||||
} else {
|
||||
it.customValues = nil
|
||||
}
|
||||
}
|
||||
|
||||
tDod, err := readVarbitInt(&it.br)
|
||||
|
|
|
@ -1368,6 +1368,52 @@ func TestFloatHistogramUniqueSpansAfterNext(t *testing.T) {
|
|||
require.NotSame(t, &rh1.NegativeSpans[0], &rh2.NegativeSpans[0], "NegativeSpans should be unique between histograms")
|
||||
}
|
||||
|
||||
func TestFloatHistogramUniqueCustomValuesAfterNext(t *testing.T) {
|
||||
// Create two histograms with the same schema and custom values.
|
||||
h1 := &histogram.FloatHistogram{
|
||||
Schema: -53,
|
||||
Count: 10,
|
||||
Sum: 15.0,
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 2},
|
||||
{Offset: 1, Length: 2},
|
||||
},
|
||||
PositiveBuckets: []float64{1, 2, 3, 4},
|
||||
CustomValues: []float64{10, 11, 12, 13},
|
||||
}
|
||||
|
||||
h2 := h1.Copy()
|
||||
|
||||
// Create a chunk and append both histograms.
|
||||
c := NewFloatHistogramChunk()
|
||||
app, err := c.Appender()
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, _, err = app.AppendFloatHistogram(nil, 0, h1, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, _, err = app.AppendFloatHistogram(nil, 1, h2, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create an iterator and advance to the first histogram.
|
||||
it := c.Iterator(nil)
|
||||
require.Equal(t, ValFloatHistogram, it.Next())
|
||||
_, rh1 := it.AtFloatHistogram(nil)
|
||||
|
||||
// Advance to the second histogram and retrieve it.
|
||||
require.Equal(t, ValFloatHistogram, it.Next())
|
||||
_, rh2 := it.AtFloatHistogram(nil)
|
||||
|
||||
require.Equal(t, rh1.PositiveSpans, h1.PositiveSpans, "Returned positive spans are as expected")
|
||||
require.Equal(t, rh1.CustomValues, h1.CustomValues, "Returned custom values are as expected")
|
||||
require.Equal(t, rh2.PositiveSpans, h1.PositiveSpans, "Returned positive spans are as expected")
|
||||
require.Equal(t, rh2.CustomValues, h1.CustomValues, "Returned custom values are as expected")
|
||||
|
||||
// Check that the spans and custom values for h1 and h2 are unique slices.
|
||||
require.NotSame(t, &rh1.PositiveSpans[0], &rh2.PositiveSpans[0], "PositiveSpans should be unique between histograms")
|
||||
require.NotSame(t, &rh1.CustomValues[0], &rh2.CustomValues[0], "CustomValues should be unique between histograms")
|
||||
}
|
||||
|
||||
func assertFirstFloatHistogramSampleHint(t *testing.T, chunk Chunk, expected histogram.CounterResetHint) {
|
||||
it := chunk.Iterator(nil)
|
||||
require.Equal(t, ValFloatHistogram, it.Next())
|
||||
|
|
|
@ -1075,6 +1075,7 @@ func (it *histogramIterator) Reset(b []byte) {
|
|||
it.atHistogramCalled = false
|
||||
it.pBuckets, it.nBuckets = nil, nil
|
||||
it.pSpans, it.nSpans = nil, nil
|
||||
it.customValues = nil
|
||||
} else {
|
||||
it.pBuckets = it.pBuckets[:0]
|
||||
it.nBuckets = it.nBuckets[:0]
|
||||
|
@ -1082,6 +1083,7 @@ func (it *histogramIterator) Reset(b []byte) {
|
|||
if it.atFloatHistogramCalled {
|
||||
it.atFloatHistogramCalled = false
|
||||
it.pFloatBuckets, it.nFloatBuckets = nil, nil
|
||||
it.customValues = nil
|
||||
} else {
|
||||
it.pFloatBuckets = it.pFloatBuckets[:0]
|
||||
it.nFloatBuckets = it.nFloatBuckets[:0]
|
||||
|
@ -1187,8 +1189,7 @@ func (it *histogramIterator) Next() ValueType {
|
|||
// The case for the 2nd sample with single deltas is implicitly handled correctly with the double delta code,
|
||||
// so we don't need a separate single delta logic for the 2nd sample.
|
||||
|
||||
// Recycle bucket and span slices that have not been returned yet. Otherwise, copy them.
|
||||
// copy them.
|
||||
// Recycle bucket, span and custom value slices that have not been returned yet. Otherwise, copy them.
|
||||
if it.atFloatHistogramCalled || it.atHistogramCalled {
|
||||
if len(it.pSpans) > 0 {
|
||||
newSpans := make([]histogram.Span, len(it.pSpans))
|
||||
|
@ -1204,6 +1205,13 @@ func (it *histogramIterator) Next() ValueType {
|
|||
} else {
|
||||
it.nSpans = nil
|
||||
}
|
||||
if len(it.customValues) > 0 {
|
||||
newCustomValues := make([]float64, len(it.customValues))
|
||||
copy(newCustomValues, it.customValues)
|
||||
it.customValues = newCustomValues
|
||||
} else {
|
||||
it.customValues = nil
|
||||
}
|
||||
}
|
||||
|
||||
if it.atHistogramCalled {
|
||||
|
|
|
@ -1497,7 +1497,7 @@ func TestHistogramAppendOnlyErrors(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestHistogramUniqueSpansAfterNext(t *testing.T) {
|
||||
func TestHistogramUniqueSpansAfterNextWithAtHistogram(t *testing.T) {
|
||||
// Create two histograms with the same schema and spans.
|
||||
h1 := &histogram.Histogram{
|
||||
Schema: 1,
|
||||
|
@ -1599,6 +1599,98 @@ func TestHistogramUniqueSpansAfterNextWithAtFloatHistogram(t *testing.T) {
|
|||
require.NotSame(t, &rh1.NegativeSpans[0], &rh2.NegativeSpans[0], "NegativeSpans should be unique between histograms")
|
||||
}
|
||||
|
||||
func TestHistogramUniqueCustomValuesAfterNextWithAtHistogram(t *testing.T) {
|
||||
// Create two histograms with the same schema and custom values.
|
||||
h1 := &histogram.Histogram{
|
||||
Schema: -53,
|
||||
Count: 10,
|
||||
Sum: 15.0,
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 2},
|
||||
{Offset: 1, Length: 2},
|
||||
},
|
||||
PositiveBuckets: []int64{1, 2, 3, 4},
|
||||
CustomValues: []float64{10, 11, 12, 13},
|
||||
}
|
||||
|
||||
h2 := h1.Copy()
|
||||
|
||||
// Create a chunk and append both histograms.
|
||||
c := NewHistogramChunk()
|
||||
app, err := c.Appender()
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, _, err = app.AppendHistogram(nil, 0, h1, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, _, err = app.AppendHistogram(nil, 1, h2, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create an iterator and advance to the first histogram.
|
||||
it := c.Iterator(nil)
|
||||
require.Equal(t, ValHistogram, it.Next())
|
||||
_, rh1 := it.AtHistogram(nil)
|
||||
|
||||
// Advance to the second histogram and retrieve it.
|
||||
require.Equal(t, ValHistogram, it.Next())
|
||||
_, rh2 := it.AtHistogram(nil)
|
||||
|
||||
require.Equal(t, rh1.PositiveSpans, h1.PositiveSpans, "Returned positive spans are as expected")
|
||||
require.Equal(t, rh1.CustomValues, h1.CustomValues, "Returned custom values are as expected")
|
||||
require.Equal(t, rh2.PositiveSpans, h1.PositiveSpans, "Returned positive spans are as expected")
|
||||
require.Equal(t, rh2.CustomValues, h1.CustomValues, "Returned custom values are as expected")
|
||||
|
||||
// Check that the spans and custom values for h1 and h2 are unique slices.
|
||||
require.NotSame(t, &rh1.PositiveSpans[0], &rh2.PositiveSpans[0], "PositiveSpans should be unique between histograms")
|
||||
require.NotSame(t, &rh1.CustomValues[0], &rh2.CustomValues[0], "CustomValues should be unique between histograms")
|
||||
}
|
||||
|
||||
func TestHistogramUniqueCustomValuesAfterNextWithAtFloatHistogram(t *testing.T) {
|
||||
// Create two histograms with the same schema and custom values.
|
||||
h1 := &histogram.Histogram{
|
||||
Schema: -53,
|
||||
Count: 10,
|
||||
Sum: 15.0,
|
||||
PositiveSpans: []histogram.Span{
|
||||
{Offset: 0, Length: 2},
|
||||
{Offset: 1, Length: 2},
|
||||
},
|
||||
PositiveBuckets: []int64{1, 2, 3, 4},
|
||||
CustomValues: []float64{10, 11, 12, 13},
|
||||
}
|
||||
|
||||
h2 := h1.Copy()
|
||||
|
||||
// Create a chunk and append both histograms.
|
||||
c := NewHistogramChunk()
|
||||
app, err := c.Appender()
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, _, err = app.AppendHistogram(nil, 0, h1, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, _, err = app.AppendHistogram(nil, 1, h2, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create an iterator and advance to the first histogram.
|
||||
it := c.Iterator(nil)
|
||||
require.Equal(t, ValHistogram, it.Next())
|
||||
_, rh1 := it.AtFloatHistogram(nil)
|
||||
|
||||
// Advance to the second histogram and retrieve it.
|
||||
require.Equal(t, ValHistogram, it.Next())
|
||||
_, rh2 := it.AtFloatHistogram(nil)
|
||||
|
||||
require.Equal(t, rh1.PositiveSpans, h1.PositiveSpans, "Returned positive spans are as expected")
|
||||
require.Equal(t, rh1.CustomValues, h1.CustomValues, "Returned custom values are as expected")
|
||||
require.Equal(t, rh2.PositiveSpans, h1.PositiveSpans, "Returned positive spans are as expected")
|
||||
require.Equal(t, rh2.CustomValues, h1.CustomValues, "Returned custom values are as expected")
|
||||
|
||||
// Check that the spans and custom values for h1 and h2 are unique slices.
|
||||
require.NotSame(t, &rh1.PositiveSpans[0], &rh2.PositiveSpans[0], "PositiveSpans should be unique between histograms")
|
||||
require.NotSame(t, &rh1.CustomValues[0], &rh2.CustomValues[0], "CustomValues should be unique between histograms")
|
||||
}
|
||||
|
||||
func BenchmarkAppendable(b *testing.B) {
|
||||
// Create a histogram with a bunch of spans and buckets.
|
||||
const (
|
||||
|
|
|
@ -25,20 +25,20 @@ in-file offset (lower 4 bytes) and segment sequence number (upper 4 bytes).
|
|||
└──────────────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
# Chunk
|
||||
|
||||
```
|
||||
┌───────────────┬───────────────────┬─────────────┬────────────────┐
|
||||
│ len <uvarint> │ encoding <1 byte> │ data <data> │ CRC32 <4 byte> │
|
||||
└───────────────┴───────────────────┴─────────────┴────────────────┘
|
||||
┌───────────────┬───────────────────┬─────────────┬───────────────────┐
|
||||
│ len <uvarint> │ encoding <1 byte> │ data <data> │ checksum <4 byte> │
|
||||
└───────────────┴───────────────────┴─────────────┴───────────────────┘
|
||||
```
|
||||
|
||||
Notes:
|
||||
* `<uvarint>` has 1 to 10 bytes.
|
||||
* `encoding`: Currently either `XOR`, `histogram`, or `floathistogram`, see
|
||||
[code for numerical values](https://github.com/prometheus/prometheus/blob/02d0de9987ad99dee5de21853715954fadb3239f/tsdb/chunkenc/chunk.go#L28-L47).
|
||||
|
||||
* `len`: Chunk size in bytes. 1 to 10 bytes long using the [`<uvarint>` encoding](https://go.dev/src/encoding/binary/varint.go).
|
||||
* `encoding`: Currently either `XOR`, `histogram`, or `floathistogram`, see [code for numerical values](https://github.com/prometheus/prometheus/blob/02d0de9987ad99dee5de21853715954fadb3239f/tsdb/chunkenc/chunk.go#L28-L47).
|
||||
* `data`: See below for each encoding.
|
||||
* `checksum`: Checksum of `encoding` and `data`. It's a [cyclic redudancy check](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) with the Castagnoli polynomial, serialised as an unsigned 32 bits big endian number. Can be refered as a `CRC-32C`.
|
||||
|
||||
## XOR chunk data
|
||||
|
||||
|
@ -177,7 +177,6 @@ the encoding will therefore result in a short varbit representation. The upper
|
|||
bound of 33554430 is picked so that the varbit encoded value will take at most
|
||||
4 bytes.
|
||||
|
||||
|
||||
## Float histogram chunk data
|
||||
|
||||
Float histograms have the same layout as histograms apart from the encoding of samples.
|
||||
|
|
|
@ -1,15 +1,32 @@
|
|||
# WAL Disk Format
|
||||
|
||||
This document describes the official Prometheus WAL format.
|
||||
|
||||
The write ahead log operates in segments that are numbered and sequential,
|
||||
e.g. `000000`, `000001`, `000002`, etc., and are limited to 128MB by default.
|
||||
A segment is written to in pages of 32KB. Only the last page of the most recent segment
|
||||
and are limited to 128MB by default.
|
||||
|
||||
## Segment filename
|
||||
|
||||
The sequence number is captured in the segment filename,
|
||||
e.g. `000000`, `000001`, `000002`, etc. The first unsigned integer represents
|
||||
the sequence number of the segment, typically encoded with six digits.
|
||||
|
||||
## Segment encoding
|
||||
|
||||
This section describes the segment encoding.
|
||||
|
||||
A segment encodes an array of records. It does not contain any header. A segment
|
||||
is written to pages of 32KB. Only the last page of the most recent segment
|
||||
may be partial. A WAL record is an opaque byte slice that gets split up into sub-records
|
||||
should it exceed the remaining space of the current page. Records are never split across
|
||||
segment boundaries. If a single record exceeds the default segment size, a segment with
|
||||
a larger size will be created.
|
||||
|
||||
The encoding of pages is largely borrowed from [LevelDB's/RocksDB's write ahead log.](https://github.com/facebook/rocksdb/wiki/Write-Ahead-Log-File-Format)
|
||||
|
||||
Notable deviations are that the record fragment is encoded as:
|
||||
### Records encoding
|
||||
|
||||
Each record fragment is encoded as:
|
||||
|
||||
```
|
||||
┌───────────┬──────────┬────────────┬──────────────┐
|
||||
|
@ -17,7 +34,16 @@ Notable deviations are that the record fragment is encoded as:
|
|||
└───────────┴──────────┴────────────┴──────────────┘
|
||||
```
|
||||
|
||||
The type flag has the following states:
|
||||
The initial type byte is made up of three components: a 3-bit reserved field,
|
||||
a 1-bit zstd compression flag, a 1-bit snappy compression flag, and a 3-bit type flag.
|
||||
|
||||
```
|
||||
┌─────────────────┬──────────────────┬────────────────────┬──────────────────┐
|
||||
│ reserved <3bit> │ zstd_flag <1bit> │ snappy_flag <1bit> │ type_flag <3bit> │
|
||||
└─────────────────┴──────────────────┴────────────────────┴──────────────────┘
|
||||
```
|
||||
|
||||
The lowest 3 bits within the type flag represent the record type as follows:
|
||||
|
||||
* `0`: rest of page will be empty
|
||||
* `1`: a full record encoded in a single fragment
|
||||
|
@ -25,11 +51,16 @@ The type flag has the following states:
|
|||
* `3`: middle fragment of a record
|
||||
* `4`: final fragment of a record
|
||||
|
||||
## Record encoding
|
||||
After the type byte, 2-byte length and then 4-byte checksum of the following data are encoded.
|
||||
|
||||
The records written to the write ahead log are encoded as follows:
|
||||
All float values are represented using the [IEEE 754 format](https://en.wikipedia.org/wiki/IEEE_754).
|
||||
|
||||
### Series records
|
||||
### Record types
|
||||
|
||||
In the following sections, all the known record types are described. New types,
|
||||
can be added in the future.
|
||||
|
||||
#### Series records
|
||||
|
||||
Series records encode the labels that identifies a series and its unique ID.
|
||||
|
||||
|
@ -50,7 +81,7 @@ Series records encode the labels that identifies a series and its unique ID.
|
|||
└────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Sample records
|
||||
#### Sample records
|
||||
|
||||
Sample records encode samples as a list of triples `(series_id, timestamp, value)`.
|
||||
Series reference and timestamp are encoded as deltas w.r.t the first sample.
|
||||
|
@ -71,7 +102,7 @@ The first sample record begins at the second row.
|
|||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Tombstone records
|
||||
#### Tombstone records
|
||||
|
||||
Tombstone records encode tombstones as a list of triples `(series_id, min_time, max_time)`
|
||||
and specify an interval for which samples of a series got deleted.
|
||||
|
@ -87,9 +118,9 @@ and specify an interval for which samples of a series got deleted.
|
|||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Exemplar records
|
||||
#### Exemplar records
|
||||
|
||||
Exemplar records encode exemplars as a list of triples `(series_id, timestamp, value)`
|
||||
Exemplar records encode exemplars as a list of triples `(series_id, timestamp, value)`
|
||||
plus the length of the labels list, and all the labels.
|
||||
The first row stores the starting id and the starting timestamp.
|
||||
Series reference and timestamp are encoded as deltas w.r.t the first exemplar.
|
||||
|
@ -119,7 +150,7 @@ See: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/Op
|
|||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Metadata records
|
||||
#### Metadata records
|
||||
|
||||
Metadata records encode the metadata updates associated with a series.
|
||||
|
||||
|
@ -148,3 +179,90 @@ Metadata records encode the metadata updates associated with a series.
|
|||
└────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Histogram records
|
||||
|
||||
Histogram records encode the integer and float native histogram samples.
|
||||
|
||||
A record with the integer native histograms with the exponential bucketing:
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────────┐
|
||||
│ type = 7 <1b> │
|
||||
├───────────────────────────────────────────────────────────────────────┤
|
||||
│ ┌────────────────────┬───────────────────────────┐ │
|
||||
│ │ id <8b> │ timestamp <8b> │ │
|
||||
│ └────────────────────┴───────────────────────────┘ │
|
||||
│ ┌────────────────────┬──────────────────────────────────────────────┐ │
|
||||
│ │ id_delta <uvarint> │ timestamp_delta <uvarint> │ │
|
||||
│ ├────────────────────┴────┬─────────────────────────────────────────┤ │
|
||||
│ │ counter_reset_hint <1b> │ schema <varint> │ │
|
||||
│ ├─────────────────────────┴────┬────────────────────────────────────┤ │
|
||||
│ │ zero_threshold (float) <8b> │ zero_count <uvarint> │ │
|
||||
│ ├─────────────────┬────────────┴────────────────────────────────────┤ │
|
||||
│ │ count <uvarint> │ sum (float) <8b> │ │
|
||||
│ ├─────────────────┴─────────────────────────────────────────────────┤ │
|
||||
│ │ positive_spans_num <uvarint> │ │
|
||||
│ ├─────────────────────────────────┬─────────────────────────────────┤ │
|
||||
│ │ positive_span_offset_1 <varint> │ positive_span_len_1 <uvarint32> │ │
|
||||
│ ├─────────────────────────────────┴─────────────────────────────────┤ │
|
||||
│ │ . . . │ │
|
||||
│ ├───────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ negative_spans_num <uvarint> │ │
|
||||
│ ├───────────────────────────────┬───────────────────────────────────┤ │
|
||||
│ │ negative_span_offset <varint> │ negative_span_len <uvarint32> │ │
|
||||
│ ├───────────────────────────────┴───────────────────────────────────┤ │
|
||||
│ │ . . . │ │
|
||||
│ ├───────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ positive_bkts_num <uvarint> │ │
|
||||
│ ├─────────────────────────┬───────┬─────────────────────────────────┤ │
|
||||
│ │ positive_bkt_1 <varint> │ . . . │ positive_bkt_n <varint> │ │
|
||||
│ ├─────────────────────────┴───────┴─────────────────────────────────┤ │
|
||||
│ │ negative_bkts_num <uvarint> │ │
|
||||
│ ├─────────────────────────┬───────┬─────────────────────────────────┤ │
|
||||
│ │ negative_bkt_1 <varint> │ . . . │ negative_bkt_n <varint> │ │
|
||||
│ └─────────────────────────┴───────┴─────────────────────────────────┘ │
|
||||
│ . . . │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
A records with the Float histograms:
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────────┐
|
||||
│ type = 8 <1b> │
|
||||
├───────────────────────────────────────────────────────────────────────┤
|
||||
│ ┌────────────────────┬───────────────────────────┐ │
|
||||
│ │ id <8b> │ timestamp <8b> │ │
|
||||
│ └────────────────────┴───────────────────────────┘ │
|
||||
│ ┌────────────────────┬──────────────────────────────────────────────┐ │
|
||||
│ │ id_delta <uvarint> │ timestamp_delta <uvarint> │ │
|
||||
│ ├────────────────────┴────┬─────────────────────────────────────────┤ │
|
||||
│ │ counter_reset_hint <1b> │ schema <varint> │ │
|
||||
│ ├─────────────────────────┴────┬────────────────────────────────────┤ │
|
||||
│ │ zero_threshold (float) <8b> │ zero_count (float) <8b> │ │
|
||||
│ ├────────────────────┬─────────┴────────────────────────────────────┤ │
|
||||
│ │ count (float) <8b> │ sum (float) <8b> │ │
|
||||
│ ├────────────────────┴──────────────────────────────────────────────┤ │
|
||||
│ │ positive_spans_num <uvarint> │ │
|
||||
│ ├─────────────────────────────────┬─────────────────────────────────┤ │
|
||||
│ │ positive_span_offset_1 <varint> │ positive_span_len_1 <uvarint32> │ │
|
||||
│ ├─────────────────────────────────┴─────────────────────────────────┤ │
|
||||
│ │ . . . │ │
|
||||
│ ├───────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ negative_spans_num <uvarint> │ │
|
||||
│ ├───────────────────────────────┬───────────────────────────────────┤ │
|
||||
│ │ negative_span_offset <varint> │ negative_span_len <uvarint32> │ │
|
||||
│ ├───────────────────────────────┴───────────────────────────────────┤ │
|
||||
│ │ . . . │ │
|
||||
│ ├───────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ positive_bkts_num <uvarint> │ │
|
||||
│ ├─────────────────────────────┬───────┬─────────────────────────────┤ │
|
||||
│ │ positive_bkt_1 (float) <8b> │ . . . │ positive_bkt_n (float) <8b> │ │
|
||||
│ ├─────────────────────────────┴───────┴─────────────────────────────┤ │
|
||||
│ │ negative_bkts_num <uvarint> │ │
|
||||
│ ├─────────────────────────────┬───────┬─────────────────────────────┤ │
|
||||
│ │ negative_bkt_1 (float) <8b> │ . . . │ negative_bkt_n (float) <8b> │ │
|
||||
│ └─────────────────────────────┴───────┴─────────────────────────────┘ │
|
||||
│ . . . │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
|
|
@ -656,32 +656,15 @@ func (h *Head) loadWBL(r *wlog.Reader, syms *labels.SymbolTable, multiRef map[ch
|
|||
concurrency = h.opts.WALReplayConcurrency
|
||||
processors = make([]wblSubsetProcessor, concurrency)
|
||||
|
||||
dec record.Decoder
|
||||
shards = make([][]record.RefSample, concurrency)
|
||||
histogramShards = make([][]histogramRecord, concurrency)
|
||||
|
||||
decodedCh = make(chan interface{}, 10)
|
||||
decodeErr error
|
||||
samplesPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return []record.RefSample{}
|
||||
},
|
||||
}
|
||||
markersPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return []record.RefMmapMarker{}
|
||||
},
|
||||
}
|
||||
histogramSamplesPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return []record.RefHistogramSample{}
|
||||
},
|
||||
}
|
||||
floatHistogramSamplesPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return []record.RefFloatHistogramSample{}
|
||||
},
|
||||
}
|
||||
decodedCh = make(chan interface{}, 10)
|
||||
decodeErr error
|
||||
samplesPool zeropool.Pool[[]record.RefSample]
|
||||
markersPool zeropool.Pool[[]record.RefMmapMarker]
|
||||
histogramSamplesPool zeropool.Pool[[]record.RefHistogramSample]
|
||||
floatHistogramSamplesPool zeropool.Pool[[]record.RefFloatHistogramSample]
|
||||
)
|
||||
|
||||
defer func() {
|
||||
|
@ -711,11 +694,13 @@ func (h *Head) loadWBL(r *wlog.Reader, syms *labels.SymbolTable, multiRef map[ch
|
|||
|
||||
go func() {
|
||||
defer close(decodedCh)
|
||||
var err error
|
||||
dec := record.NewDecoder(syms)
|
||||
for r.Next() {
|
||||
rec := r.Record()
|
||||
switch dec.Type(rec) {
|
||||
case record.Samples:
|
||||
samples := samplesPool.Get().([]record.RefSample)[:0]
|
||||
samples := samplesPool.Get()[:0]
|
||||
samples, err = dec.Samples(rec, samples)
|
||||
if err != nil {
|
||||
decodeErr = &wlog.CorruptionErr{
|
||||
|
@ -727,7 +712,7 @@ func (h *Head) loadWBL(r *wlog.Reader, syms *labels.SymbolTable, multiRef map[ch
|
|||
}
|
||||
decodedCh <- samples
|
||||
case record.MmapMarkers:
|
||||
markers := markersPool.Get().([]record.RefMmapMarker)[:0]
|
||||
markers := markersPool.Get()[:0]
|
||||
markers, err = dec.MmapMarkers(rec, markers)
|
||||
if err != nil {
|
||||
decodeErr = &wlog.CorruptionErr{
|
||||
|
@ -739,7 +724,7 @@ func (h *Head) loadWBL(r *wlog.Reader, syms *labels.SymbolTable, multiRef map[ch
|
|||
}
|
||||
decodedCh <- markers
|
||||
case record.HistogramSamples:
|
||||
hists := histogramSamplesPool.Get().([]record.RefHistogramSample)[:0]
|
||||
hists := histogramSamplesPool.Get()[:0]
|
||||
hists, err = dec.HistogramSamples(rec, hists)
|
||||
if err != nil {
|
||||
decodeErr = &wlog.CorruptionErr{
|
||||
|
@ -751,7 +736,7 @@ func (h *Head) loadWBL(r *wlog.Reader, syms *labels.SymbolTable, multiRef map[ch
|
|||
}
|
||||
decodedCh <- hists
|
||||
case record.FloatHistogramSamples:
|
||||
hists := floatHistogramSamplesPool.Get().([]record.RefFloatHistogramSample)[:0]
|
||||
hists := floatHistogramSamplesPool.Get()[:0]
|
||||
hists, err = dec.FloatHistogramSamples(rec, hists)
|
||||
if err != nil {
|
||||
decodeErr = &wlog.CorruptionErr{
|
||||
|
@ -802,7 +787,7 @@ func (h *Head) loadWBL(r *wlog.Reader, syms *labels.SymbolTable, multiRef map[ch
|
|||
}
|
||||
samples = samples[m:]
|
||||
}
|
||||
samplesPool.Put(d)
|
||||
samplesPool.Put(v)
|
||||
case []record.RefMmapMarker:
|
||||
markers := v
|
||||
for _, rm := range markers {
|
||||
|
@ -857,7 +842,7 @@ func (h *Head) loadWBL(r *wlog.Reader, syms *labels.SymbolTable, multiRef map[ch
|
|||
}
|
||||
samples = samples[m:]
|
||||
}
|
||||
histogramSamplesPool.Put(v) //nolint:staticcheck
|
||||
histogramSamplesPool.Put(v)
|
||||
case []record.RefFloatHistogramSample:
|
||||
samples := v
|
||||
// We split up the samples into chunks of 5000 samples or less.
|
||||
|
@ -889,7 +874,7 @@ func (h *Head) loadWBL(r *wlog.Reader, syms *labels.SymbolTable, multiRef map[ch
|
|||
}
|
||||
samples = samples[m:]
|
||||
}
|
||||
floatHistogramSamplesPool.Put(v) //nolint:staticcheck
|
||||
floatHistogramSamplesPool.Put(v)
|
||||
default:
|
||||
panic(fmt.Errorf("unexpected decodedCh type: %T", d))
|
||||
}
|
||||
|
|
|
@ -110,12 +110,6 @@ func newCRC32() hash.Hash32 {
|
|||
return crc32.New(castagnoliTable)
|
||||
}
|
||||
|
||||
type symbolCacheEntry struct {
|
||||
index uint32
|
||||
lastValueIndex uint32
|
||||
lastValue string
|
||||
}
|
||||
|
||||
type PostingsEncoder func(*encoding.Encbuf, []uint32) error
|
||||
|
||||
type PostingsDecoder func(encoding.Decbuf) (int, Postings, error)
|
||||
|
@ -146,7 +140,7 @@ type Writer struct {
|
|||
symbols *Symbols
|
||||
symbolFile *fileutil.MmapFile
|
||||
lastSymbol string
|
||||
symbolCache map[string]symbolCacheEntry
|
||||
symbolCache map[string]uint32 // From symbol to index in table.
|
||||
|
||||
labelIndexes []labelIndexHashEntry // Label index offsets.
|
||||
labelNames map[string]uint64 // Label names, and their usage.
|
||||
|
@ -246,7 +240,7 @@ func NewWriterWithEncoder(ctx context.Context, fn string, encoder PostingsEncode
|
|||
buf1: encoding.Encbuf{B: make([]byte, 0, 1<<22)},
|
||||
buf2: encoding.Encbuf{B: make([]byte, 0, 1<<22)},
|
||||
|
||||
symbolCache: make(map[string]symbolCacheEntry, 1<<8),
|
||||
symbolCache: make(map[string]uint32, 1<<16),
|
||||
labelNames: make(map[string]uint64, 1<<8),
|
||||
crc32: newCRC32(),
|
||||
postingsEncoder: encoder,
|
||||
|
@ -478,29 +472,16 @@ func (w *Writer) AddSeries(ref storage.SeriesRef, lset labels.Labels, chunks ...
|
|||
w.buf2.PutUvarint(lset.Len())
|
||||
|
||||
if err := lset.Validate(func(l labels.Label) error {
|
||||
var err error
|
||||
cacheEntry, ok := w.symbolCache[l.Name]
|
||||
nameIndex := cacheEntry.index
|
||||
nameIndex, ok := w.symbolCache[l.Name]
|
||||
if !ok {
|
||||
nameIndex, err = w.symbols.ReverseLookup(l.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("symbol entry for %q does not exist, %w", l.Name, err)
|
||||
}
|
||||
return fmt.Errorf("symbol entry for %q does not exist", l.Name)
|
||||
}
|
||||
w.labelNames[l.Name]++
|
||||
w.buf2.PutUvarint32(nameIndex)
|
||||
|
||||
valueIndex := cacheEntry.lastValueIndex
|
||||
if !ok || cacheEntry.lastValue != l.Value {
|
||||
valueIndex, err = w.symbols.ReverseLookup(l.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("symbol entry for %q does not exist, %w", l.Value, err)
|
||||
}
|
||||
w.symbolCache[l.Name] = symbolCacheEntry{
|
||||
index: nameIndex,
|
||||
lastValueIndex: valueIndex,
|
||||
lastValue: l.Value,
|
||||
}
|
||||
valueIndex, ok := w.symbolCache[l.Value]
|
||||
if !ok {
|
||||
return fmt.Errorf("symbol entry for %q does not exist", l.Value)
|
||||
}
|
||||
w.buf2.PutUvarint32(valueIndex)
|
||||
return nil
|
||||
|
@ -559,6 +540,7 @@ func (w *Writer) AddSymbol(sym string) error {
|
|||
return fmt.Errorf("symbol %q out-of-order", sym)
|
||||
}
|
||||
w.lastSymbol = sym
|
||||
w.symbolCache[sym] = uint32(w.numSymbols)
|
||||
w.numSymbols++
|
||||
w.buf1.Reset()
|
||||
w.buf1.PutUvarintStr(sym)
|
||||
|
@ -628,10 +610,10 @@ func (w *Writer) writeLabelIndices() error {
|
|||
values := []uint32{}
|
||||
for d.Err() == nil && cnt > 0 {
|
||||
cnt--
|
||||
d.Uvarint() // Keycount.
|
||||
name := d.UvarintBytes() // Label name.
|
||||
value := yoloString(d.UvarintBytes()) // Label value.
|
||||
d.Uvarint64() // Offset.
|
||||
d.Uvarint() // Keycount.
|
||||
name := d.UvarintBytes() // Label name.
|
||||
value := d.UvarintBytes() // Label value.
|
||||
d.Uvarint64() // Offset.
|
||||
if len(name) == 0 {
|
||||
continue // All index is ignored.
|
||||
}
|
||||
|
@ -644,9 +626,9 @@ func (w *Writer) writeLabelIndices() error {
|
|||
values = values[:0]
|
||||
}
|
||||
current = name
|
||||
sid, err := w.symbols.ReverseLookup(value)
|
||||
if err != nil {
|
||||
return err
|
||||
sid, ok := w.symbolCache[string(value)]
|
||||
if !ok {
|
||||
return fmt.Errorf("symbol entry for %q does not exist", string(value))
|
||||
}
|
||||
values = append(values, sid)
|
||||
}
|
||||
|
@ -918,9 +900,9 @@ func (w *Writer) writePostingsToTmpFiles() error {
|
|||
|
||||
nameSymbols := map[uint32]string{}
|
||||
for _, name := range batchNames {
|
||||
sid, err := w.symbols.ReverseLookup(name)
|
||||
if err != nil {
|
||||
return err
|
||||
sid, ok := w.symbolCache[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("symbol entry for %q does not exist", name)
|
||||
}
|
||||
nameSymbols[sid] = name
|
||||
}
|
||||
|
@ -957,9 +939,9 @@ func (w *Writer) writePostingsToTmpFiles() error {
|
|||
|
||||
for _, name := range batchNames {
|
||||
// Write out postings for this label name.
|
||||
sid, err := w.symbols.ReverseLookup(name)
|
||||
if err != nil {
|
||||
return err
|
||||
sid, ok := w.symbolCache[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("symbol entry for %q does not exist", name)
|
||||
}
|
||||
values := make([]uint32, 0, len(postings[sid]))
|
||||
for v := range postings[sid] {
|
||||
|
|
|
@ -331,7 +331,7 @@ func TestPostingsMany(t *testing.T) {
|
|||
exp = append(exp, e)
|
||||
}
|
||||
}
|
||||
require.Equal(t, exp, got, fmt.Sprintf("input: %v", c.in))
|
||||
require.Equalf(t, exp, got, "input: %v", c.in)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue