mirror of
https://github.com/prometheus/prometheus.git
synced 2025-08-05 13:47:10 +02:00
Merge branch 'main' into merge-3.5-into-main
Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
This commit is contained in:
commit
36ec1e4fb0
2
.github/workflows/automerge-dependabot.yml
vendored
2
.github/workflows/automerge-dependabot.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
steps:
|
||||
- name: Dependabot metadata
|
||||
id: metadata
|
||||
uses: dependabot/fetch-metadata@d7267f607e9d3fb96fc2fbe83e0af444713e90b7 # v2.3.0
|
||||
uses: dependabot/fetch-metadata@08eff52bf64351f401fb50d4972fa95b9f2c2d1b # v2.4.0
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: Enable auto-merge for Dependabot PRs
|
||||
|
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@ -95,7 +95,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
|
||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
- run: |
|
||||
@ -205,7 +205,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
cache: false
|
||||
go-version: 1.24.x
|
||||
@ -220,18 +220,18 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
- name: Install snmp_exporter/generator dependencies
|
||||
run: sudo apt-get update && sudo apt-get -y install libsnmp-dev
|
||||
if: github.repository == 'prometheus/snmp_exporter'
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v7.0.0
|
||||
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
|
||||
with:
|
||||
args: --verbose
|
||||
# Make sure to sync this with Makefile.common and scripts/golangci-lint.yml.
|
||||
version: v2.1.5
|
||||
version: v2.2.1
|
||||
fuzzing:
|
||||
uses: ./.github/workflows/fuzzing.yml
|
||||
if: github.event_name == 'pull_request'
|
||||
|
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@ -29,12 +29,12 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
|
||||
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
|
||||
uses: github/codeql-action/autobuild@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
|
||||
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
|
4
.github/workflows/scorecards.yml
vendored
4
.github/workflows/scorecards.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # tag=v2.4.1
|
||||
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # tag=v2.4.2
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
@ -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@28deaeda66b76a05916b6923827895f2b14ab387 # tag=v3.28.16
|
||||
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # tag=v3.29.2
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
## main / unreleased
|
||||
|
||||
* [BUGFIX] OTLP receiver: Generate `target_info` samples between the earliest and latest samples per resource. #16737
|
||||
|
||||
## 3.5.0 / 2025-07-14
|
||||
|
||||
* [FEATURE] PromQL: Add experimental type and unit metadata labels, behind feature flag `type-and-unit-labels`. #16228 #16632 #16718 #16743
|
||||
@ -246,6 +248,11 @@ This release includes new features such as a brand new UI and UTF-8 support enab
|
||||
* [BUGFIX] Autoreload: Reload invalid yaml files. #14947
|
||||
* [BUGFIX] Scrape: Do not override target parameter labels with config params. #11029
|
||||
|
||||
## 2.53.5 / 2025-06-30
|
||||
|
||||
* [ENHANCEMENT] TSDB: Add backward compatibility with the upcoming TSDB block index v3 #16762
|
||||
* [BUGFIX] Top-level: Update GOGC before loading TSDB #16521
|
||||
|
||||
## 2.53.4 / 2025-03-18
|
||||
|
||||
* [BUGFIX] Runtime: fix GOGC is being set to 0 when installed with empty prometheus.yml file resulting high cpu usage. #16090
|
||||
@ -260,7 +267,7 @@ This release includes new features such as a brand new UI and UTF-8 support enab
|
||||
Fix a bug where Prometheus would crash with a segmentation fault if a remote-read
|
||||
request accessed a block on disk at about the same time as TSDB created a new block.
|
||||
|
||||
[BUGFIX] Remote-Read: Resolve occasional segmentation fault on query. #14515,#14523
|
||||
* [BUGFIX] Remote-Read: Resolve occasional segmentation fault on query. #14515,#14523
|
||||
|
||||
## 2.55.1 / 2024-11-04
|
||||
|
||||
|
2
Makefile
2
Makefile
@ -189,6 +189,6 @@ update-all-go-deps:
|
||||
@$(MAKE) update-go-deps
|
||||
@echo ">> updating Go dependencies in ./documentation/examples/remote_storage/"
|
||||
@cd ./documentation/examples/remote_storage/ && for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \
|
||||
$(GO) get -d $$m; \
|
||||
$(GO) get $$m; \
|
||||
done
|
||||
@cd ./documentation/examples/remote_storage/ && $(GO) mod tidy
|
||||
|
@ -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 ?= v2.1.5
|
||||
GOLANGCI_LINT_VERSION ?= v2.2.1
|
||||
GOLANGCI_FMT_OPTS ?=
|
||||
# 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.
|
||||
@ -139,7 +139,7 @@ common-deps:
|
||||
update-go-deps:
|
||||
@echo ">> updating Go dependencies"
|
||||
@for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \
|
||||
$(GO) get -d $$m; \
|
||||
$(GO) get $$m; \
|
||||
done
|
||||
$(GO) mod tidy
|
||||
|
||||
|
@ -67,9 +67,9 @@ Prometheus will now be reachable at <http://localhost:9090/>.
|
||||
|
||||
To build Prometheus from source code, You need:
|
||||
|
||||
* Go [version 1.22 or greater](https://golang.org/doc/install).
|
||||
* NodeJS [version 22 or greater](https://nodejs.org/).
|
||||
* npm [version 8 or greater](https://www.npmjs.com/).
|
||||
* Go: Version specified in [go.mod](./go.mod) or greater.
|
||||
* NodeJS: Version specified in [.nvmrc](./web/ui/.nvmrc) or greater.
|
||||
* npm: Version 8 or greater (check with `npm --version` and [here](https://www.npmjs.com/)).
|
||||
|
||||
Start by cloning the repository:
|
||||
|
||||
|
@ -89,6 +89,8 @@ var (
|
||||
lintConfigOptions = append(append([]string{}, lintRulesOptions...), lintOptionTooLongScrapeInterval)
|
||||
)
|
||||
|
||||
const httpConfigFileDescription = "HTTP client configuration file, see details at https://prometheus.io/docs/prometheus/latest/configuration/promtool"
|
||||
|
||||
func main() {
|
||||
var (
|
||||
httpRoundTripper = api.DefaultRoundTripper
|
||||
@ -138,11 +140,11 @@ func main() {
|
||||
).Required().ExistingFiles()
|
||||
|
||||
checkServerHealthCmd := checkCmd.Command("healthy", "Check if the Prometheus server is healthy.")
|
||||
checkServerHealthCmd.Flag("http.config.file", "HTTP client configuration file for promtool to connect to Prometheus.").PlaceHolder("<filename>").ExistingFileVar(&httpConfigFilePath)
|
||||
checkServerHealthCmd.Flag("http.config.file", httpConfigFileDescription).PlaceHolder("<filename>").ExistingFileVar(&httpConfigFilePath)
|
||||
checkServerHealthCmd.Flag("url", "The URL for the Prometheus server.").Default("http://localhost:9090").URLVar(&serverURL)
|
||||
|
||||
checkServerReadyCmd := checkCmd.Command("ready", "Check if the Prometheus server is ready.")
|
||||
checkServerReadyCmd.Flag("http.config.file", "HTTP client configuration file for promtool to connect to Prometheus.").PlaceHolder("<filename>").ExistingFileVar(&httpConfigFilePath)
|
||||
checkServerReadyCmd.Flag("http.config.file", httpConfigFileDescription).PlaceHolder("<filename>").ExistingFileVar(&httpConfigFilePath)
|
||||
checkServerReadyCmd.Flag("url", "The URL for the Prometheus server.").Default("http://localhost:9090").URLVar(&serverURL)
|
||||
|
||||
checkRulesCmd := checkCmd.Command("rules", "Check if the rule files are valid or not.")
|
||||
@ -165,7 +167,7 @@ func main() {
|
||||
|
||||
queryCmd := app.Command("query", "Run query against a Prometheus server.")
|
||||
queryCmdFmt := queryCmd.Flag("format", "Output format of the query.").Short('o').Default("promql").Enum("promql", "json")
|
||||
queryCmd.Flag("http.config.file", "HTTP client configuration file for promtool to connect to Prometheus.").PlaceHolder("<filename>").ExistingFileVar(&httpConfigFilePath)
|
||||
queryCmd.Flag("http.config.file", httpConfigFileDescription).PlaceHolder("<filename>").ExistingFileVar(&httpConfigFilePath)
|
||||
|
||||
queryInstantCmd := queryCmd.Command("instant", "Run instant query.")
|
||||
queryInstantCmd.Arg("server", "Prometheus server to query.").Required().URLVar(&serverURL)
|
||||
@ -210,7 +212,7 @@ func main() {
|
||||
queryAnalyzeCmd.Flag("match", "Series selector. Can be specified multiple times.").Required().StringsVar(&queryAnalyzeCfg.matchers)
|
||||
|
||||
pushCmd := app.Command("push", "Push to a Prometheus server.")
|
||||
pushCmd.Flag("http.config.file", "HTTP client configuration file for promtool to connect to Prometheus.").PlaceHolder("<filename>").ExistingFileVar(&httpConfigFilePath)
|
||||
pushCmd.Flag("http.config.file", httpConfigFileDescription).PlaceHolder("<filename>").ExistingFileVar(&httpConfigFilePath)
|
||||
pushMetricsCmd := pushCmd.Command("metrics", "Push metrics to a prometheus remote write (for testing purpose only).")
|
||||
pushMetricsCmd.Arg("remote-write-url", "Prometheus remote write url to push metrics.").Required().URLVar(&remoteWriteURL)
|
||||
metricFiles := pushMetricsCmd.Arg(
|
||||
@ -277,7 +279,7 @@ func main() {
|
||||
importFilePath := openMetricsImportCmd.Arg("input file", "OpenMetrics file to read samples from.").Required().String()
|
||||
importDBPath := openMetricsImportCmd.Arg("output directory", "Output directory for generated blocks.").Default(defaultDBPath).String()
|
||||
importRulesCmd := importCmd.Command("rules", "Create blocks of data for new recording rules.")
|
||||
importRulesCmd.Flag("http.config.file", "HTTP client configuration file for promtool to connect to Prometheus.").PlaceHolder("<filename>").ExistingFileVar(&httpConfigFilePath)
|
||||
importRulesCmd.Flag("http.config.file", httpConfigFileDescription).PlaceHolder("<filename>").ExistingFileVar(&httpConfigFilePath)
|
||||
importRulesCmd.Flag("url", "The URL for the Prometheus API with the data where the rule will be backfilled from.").Default("http://localhost:9090").URLVar(&serverURL)
|
||||
importRulesStart := importRulesCmd.Flag("start", "The time to start backfilling the new rule from. Must be a RFC3339 formatted date or Unix timestamp. Required.").
|
||||
Required().String()
|
||||
|
130
config/config.go
130
config/config.go
@ -68,11 +68,6 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
LegacyValidationConfig = "legacy"
|
||||
UTF8ValidationConfig = "utf8"
|
||||
)
|
||||
|
||||
// Load parses the YAML input s into a Config.
|
||||
func Load(s string, logger *slog.Logger) (*Config, error) {
|
||||
cfg := &Config{}
|
||||
@ -109,10 +104,10 @@ func Load(s string, logger *slog.Logger) (*Config, error) {
|
||||
}
|
||||
|
||||
switch cfg.OTLPConfig.TranslationStrategy {
|
||||
case UnderscoreEscapingWithSuffixes:
|
||||
case UnderscoreEscapingWithSuffixes, UnderscoreEscapingWithoutSuffixes:
|
||||
case "":
|
||||
case NoTranslation, NoUTF8EscapingWithSuffixes:
|
||||
if cfg.GlobalConfig.MetricNameValidationScheme == LegacyValidationConfig {
|
||||
if cfg.GlobalConfig.MetricNameValidationScheme == model.LegacyValidation {
|
||||
return nil, fmt.Errorf("OTLP translation strategy %q is not allowed when UTF8 is disabled", cfg.OTLPConfig.TranslationStrategy)
|
||||
}
|
||||
default:
|
||||
@ -172,7 +167,7 @@ var (
|
||||
ScrapeProtocols: DefaultScrapeProtocols,
|
||||
ConvertClassicHistogramsToNHCB: false,
|
||||
AlwaysScrapeClassicHistograms: false,
|
||||
MetricNameValidationScheme: UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
|
||||
@ -486,8 +481,8 @@ type GlobalConfig struct {
|
||||
// 0 means no limit.
|
||||
KeepDroppedTargets uint `yaml:"keep_dropped_targets,omitempty"`
|
||||
// Allow UTF8 Metric and Label Names. Can be blank in config files but must
|
||||
// have a value if a ScrapeConfig is created programmatically.
|
||||
MetricNameValidationScheme string `yaml:"metric_name_validation_scheme,omitempty"`
|
||||
// have a value if a GlobalConfig is created programmatically.
|
||||
MetricNameValidationScheme model.ValidationScheme `yaml:"metric_name_validation_scheme,omitempty"`
|
||||
// Metric name escaping mode to request through content negotiation. Can be
|
||||
// blank in config files but must have a value if a ScrapeConfig is created
|
||||
// programmatically.
|
||||
@ -755,7 +750,7 @@ type ScrapeConfig struct {
|
||||
KeepDroppedTargets uint `yaml:"keep_dropped_targets,omitempty"`
|
||||
// Allow UTF8 Metric and Label Names. Can be blank in config files but must
|
||||
// have a value if a ScrapeConfig is created programmatically.
|
||||
MetricNameValidationScheme string `yaml:"metric_name_validation_scheme,omitempty"`
|
||||
MetricNameValidationScheme model.ValidationScheme `yaml:"metric_name_validation_scheme,omitempty"`
|
||||
// Metric name escaping mode to request through content negotiation. Can be
|
||||
// blank in config files but must have a value if a ScrapeConfig is created
|
||||
// programmatically.
|
||||
@ -882,32 +877,32 @@ func (c *ScrapeConfig) Validate(globalConfig GlobalConfig) error {
|
||||
}
|
||||
|
||||
switch globalConfig.MetricNameValidationScheme {
|
||||
case "":
|
||||
globalConfig.MetricNameValidationScheme = UTF8ValidationConfig
|
||||
case LegacyValidationConfig, UTF8ValidationConfig:
|
||||
case model.UnsetValidation:
|
||||
globalConfig.MetricNameValidationScheme = model.UTF8Validation
|
||||
case model.LegacyValidation, model.UTF8Validation:
|
||||
default:
|
||||
return fmt.Errorf("unknown global name validation method specified, must be either 'legacy' or 'utf8', got %s", globalConfig.MetricNameValidationScheme)
|
||||
return fmt.Errorf("unknown global name validation method specified, must be either '', 'legacy' or 'utf8', got %s", globalConfig.MetricNameValidationScheme)
|
||||
}
|
||||
// Scrapeconfig validation scheme matches global if left blank.
|
||||
switch c.MetricNameValidationScheme {
|
||||
case "":
|
||||
case model.UnsetValidation:
|
||||
c.MetricNameValidationScheme = globalConfig.MetricNameValidationScheme
|
||||
case LegacyValidationConfig, UTF8ValidationConfig:
|
||||
case model.LegacyValidation, model.UTF8Validation:
|
||||
default:
|
||||
return fmt.Errorf("unknown scrape config name validation method specified, must be either 'legacy' or 'utf8', got %s", c.MetricNameValidationScheme)
|
||||
return fmt.Errorf("unknown scrape config name validation method specified, must be either '', 'legacy' or 'utf8', got %s", c.MetricNameValidationScheme)
|
||||
}
|
||||
|
||||
// Escaping scheme is based on the validation scheme if left blank.
|
||||
switch globalConfig.MetricNameEscapingScheme {
|
||||
case "":
|
||||
if globalConfig.MetricNameValidationScheme == LegacyValidationConfig {
|
||||
if globalConfig.MetricNameValidationScheme == model.LegacyValidation {
|
||||
globalConfig.MetricNameEscapingScheme = model.EscapeUnderscores
|
||||
} else {
|
||||
globalConfig.MetricNameEscapingScheme = model.AllowUTF8
|
||||
}
|
||||
case model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues:
|
||||
default:
|
||||
return fmt.Errorf("unknown global name escaping method specified, must be one of '%s', '%s', '%s', or '%s', got %s", model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues, globalConfig.MetricNameValidationScheme)
|
||||
return fmt.Errorf("unknown global name escaping method specified, must be one of '%s', '%s', '%s', or '%s', got %q", model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues, globalConfig.MetricNameEscapingScheme)
|
||||
}
|
||||
|
||||
if c.MetricNameEscapingScheme == "" {
|
||||
@ -916,12 +911,12 @@ func (c *ScrapeConfig) Validate(globalConfig GlobalConfig) error {
|
||||
|
||||
switch c.MetricNameEscapingScheme {
|
||||
case model.AllowUTF8:
|
||||
if c.MetricNameValidationScheme != UTF8ValidationConfig {
|
||||
if c.MetricNameValidationScheme != model.UTF8Validation {
|
||||
return errors.New("utf8 metric names requested but validation scheme is not set to UTF8")
|
||||
}
|
||||
case model.EscapeUnderscores, model.EscapeDots, model.EscapeValues:
|
||||
default:
|
||||
return fmt.Errorf("unknown scrape config name escaping method specified, must be one of '%s', '%s', '%s', or '%s', got %s", model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues, c.MetricNameValidationScheme)
|
||||
return fmt.Errorf("unknown scrape config name escaping method specified, must be one of '%s', '%s', '%s', or '%s', got %q", model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues, c.MetricNameEscapingScheme)
|
||||
}
|
||||
|
||||
if c.ConvertClassicHistogramsToNHCB == nil {
|
||||
@ -942,26 +937,6 @@ func (c *ScrapeConfig) MarshalYAML() (interface{}, error) {
|
||||
return discovery.MarshalYAMLWithInlineConfigs(c)
|
||||
}
|
||||
|
||||
// ToValidationScheme returns the validation scheme for the given string config value.
|
||||
func ToValidationScheme(s string) (validationScheme model.ValidationScheme, err error) {
|
||||
switch s {
|
||||
case "":
|
||||
// This is a workaround for third party exporters that don't set the validation scheme.
|
||||
if DefaultGlobalConfig.MetricNameValidationScheme == "" {
|
||||
return model.UTF8Validation, errors.New("global metric name validation scheme is not set")
|
||||
}
|
||||
return ToValidationScheme(DefaultGlobalConfig.MetricNameValidationScheme)
|
||||
case UTF8ValidationConfig:
|
||||
validationScheme = model.UTF8Validation
|
||||
case LegacyValidationConfig:
|
||||
validationScheme = model.LegacyValidation
|
||||
default:
|
||||
return model.UTF8Validation, fmt.Errorf("invalid metric name validation scheme, %s", s)
|
||||
}
|
||||
|
||||
return validationScheme, nil
|
||||
}
|
||||
|
||||
// ToEscapingScheme wraps the equivalent common library function with the
|
||||
// desired default behavior based on the given validation scheme. This is a
|
||||
// workaround for third party exporters that don't set the escaping scheme.
|
||||
@ -972,6 +947,10 @@ func ToEscapingScheme(s string, v model.ValidationScheme) (model.EscapingScheme,
|
||||
return model.NoEscaping, nil
|
||||
case model.LegacyValidation:
|
||||
return model.UnderscoreEscaping, nil
|
||||
case model.UnsetValidation:
|
||||
return model.NoEscaping, fmt.Errorf("v is unset: %s", v)
|
||||
default:
|
||||
panic(fmt.Errorf("unhandled validation scheme: %s", v))
|
||||
}
|
||||
}
|
||||
return model.ToEscapingScheme(s)
|
||||
@ -1555,31 +1534,68 @@ func getGoGC() int {
|
||||
type translationStrategyOption string
|
||||
|
||||
var (
|
||||
// NoUTF8EscapingWithSuffixes will accept metric/label names as they are.
|
||||
// Unit and type suffixes may be added to metric names, according to certain rules.
|
||||
// NoUTF8EscapingWithSuffixes will accept metric/label names as they are. Unit
|
||||
// and type suffixes may be added to metric names, according to certain rules.
|
||||
NoUTF8EscapingWithSuffixes translationStrategyOption = "NoUTF8EscapingWithSuffixes"
|
||||
// UnderscoreEscapingWithSuffixes is the default option for translating OTLP to Prometheus.
|
||||
// This option will translate metric name characters that are not alphanumerics/underscores/colons to underscores,
|
||||
// and label name characters that are not alphanumerics/underscores to underscores.
|
||||
// Unit and type suffixes may be appended to metric names, according to certain rules.
|
||||
// UnderscoreEscapingWithSuffixes is the default option for translating OTLP
|
||||
// to Prometheus. This option will translate metric name characters that are
|
||||
// not alphanumerics/underscores/colons to underscores, and label name
|
||||
// characters that are not alphanumerics/underscores to underscores. Unit and
|
||||
// type suffixes may be appended to metric names, according to certain rules.
|
||||
UnderscoreEscapingWithSuffixes translationStrategyOption = "UnderscoreEscapingWithSuffixes"
|
||||
// UnderscoreEscapingWithoutSuffixes translates metric name characters that
|
||||
// are not alphanumerics/underscores/colons to underscores, and label name
|
||||
// characters that are not alphanumerics/underscores to underscores, but
|
||||
// unlike UnderscoreEscapingWithSuffixes it does not append any suffixes to
|
||||
// the names.
|
||||
UnderscoreEscapingWithoutSuffixes translationStrategyOption = "UnderscoreEscapingWithoutSuffixes"
|
||||
// NoTranslation (EXPERIMENTAL): disables all translation of incoming metric
|
||||
// and label names. This offers a way for the OTLP users to use native metric names, reducing confusion.
|
||||
// and label names. This offers a way for the OTLP users to use native metric
|
||||
// names, reducing confusion.
|
||||
//
|
||||
// WARNING: This setting has significant known risks and limitations (see
|
||||
// https://prometheus.io/docs/practices/naming/ for details):
|
||||
// * Impaired UX when using PromQL in plain YAML (e.g. alerts, rules, dashboard, autoscaling configuration).
|
||||
// * Series collisions which in the best case may result in OOO errors, in the worst case a silently malformed
|
||||
// time series. For instance, you may end up in situation of ingesting `foo.bar` series with unit
|
||||
// `seconds` and a separate series `foo.bar` with unit `milliseconds`.
|
||||
// https://prometheus.io/docs/practices/naming/ for details): * Impaired UX
|
||||
// when using PromQL in plain YAML (e.g. alerts, rules, dashboard, autoscaling
|
||||
// configuration). * Series collisions which in the best case may result in
|
||||
// OOO errors, in the worst case a silently malformed time series. For
|
||||
// instance, you may end up in situation of ingesting `foo.bar` series with
|
||||
// unit `seconds` and a separate series `foo.bar` with unit `milliseconds`.
|
||||
//
|
||||
// As a result, this setting is experimental and currently, should not be used in
|
||||
// production systems.
|
||||
// As a result, this setting is experimental and currently, should not be used
|
||||
// in production systems.
|
||||
//
|
||||
// TODO(ArthurSens): Mention `type-and-unit-labels` feature (https://github.com/prometheus/proposals/pull/39) once released, as potential mitigation of the above risks.
|
||||
// TODO(ArthurSens): Mention `type-and-unit-labels` feature
|
||||
// (https://github.com/prometheus/proposals/pull/39) once released, as
|
||||
// potential mitigation of the above risks.
|
||||
NoTranslation translationStrategyOption = "NoTranslation"
|
||||
)
|
||||
|
||||
// ShouldEscape returns true if the translation strategy requires that metric
|
||||
// names be escaped.
|
||||
func (o translationStrategyOption) ShouldEscape() bool {
|
||||
switch o {
|
||||
case UnderscoreEscapingWithSuffixes, UnderscoreEscapingWithoutSuffixes:
|
||||
return true
|
||||
case NoTranslation, NoUTF8EscapingWithSuffixes:
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ShouldAddSuffixes returns a bool deciding whether the given translation
|
||||
// strategy should have suffixes added.
|
||||
func (o translationStrategyOption) ShouldAddSuffixes() bool {
|
||||
switch o {
|
||||
case UnderscoreEscapingWithSuffixes, NoUTF8EscapingWithSuffixes:
|
||||
return true
|
||||
case UnderscoreEscapingWithoutSuffixes, NoTranslation:
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// OTLPConfig is the configuration for writing to the OTLP endpoint.
|
||||
type OTLPConfig struct {
|
||||
PromoteAllResourceAttributes bool `yaml:"promote_all_resource_attributes,omitempty"`
|
||||
|
@ -2567,7 +2567,7 @@ func TestGetScrapeConfigs(t *testing.T) {
|
||||
ScrapeInterval: opts.ScrapeInterval,
|
||||
ScrapeTimeout: opts.ScrapeTimeout,
|
||||
ScrapeProtocols: DefaultGlobalConfig.ScrapeProtocols,
|
||||
MetricNameValidationScheme: UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
|
||||
MetricsPath: "/metrics",
|
||||
@ -2627,7 +2627,7 @@ func TestGetScrapeConfigs(t *testing.T) {
|
||||
ScrapeInterval: model.Duration(60 * time.Second),
|
||||
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
|
||||
ScrapeProtocols: DefaultGlobalConfig.ScrapeProtocols,
|
||||
MetricNameValidationScheme: UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
AlwaysScrapeClassicHistograms: boolPtr(false),
|
||||
ConvertClassicHistogramsToNHCB: boolPtr(false),
|
||||
@ -2664,7 +2664,7 @@ func TestGetScrapeConfigs(t *testing.T) {
|
||||
ScrapeInterval: model.Duration(15 * time.Second),
|
||||
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
|
||||
ScrapeProtocols: DefaultGlobalConfig.ScrapeProtocols,
|
||||
MetricNameValidationScheme: UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
AlwaysScrapeClassicHistograms: boolPtr(false),
|
||||
ConvertClassicHistogramsToNHCB: boolPtr(false),
|
||||
@ -2788,27 +2788,27 @@ func TestScrapeConfigNameValidationSettings(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string
|
||||
expectScheme string
|
||||
expectScheme model.ValidationScheme
|
||||
}{
|
||||
{
|
||||
name: "blank config implies default",
|
||||
inputFile: "scrape_config_default_validation_mode",
|
||||
expectScheme: "utf8",
|
||||
expectScheme: model.UTF8Validation,
|
||||
},
|
||||
{
|
||||
name: "global setting implies local settings",
|
||||
inputFile: "scrape_config_global_validation_mode",
|
||||
expectScheme: "legacy",
|
||||
expectScheme: model.LegacyValidation,
|
||||
},
|
||||
{
|
||||
name: "local setting",
|
||||
inputFile: "scrape_config_local_validation_mode",
|
||||
expectScheme: "legacy",
|
||||
expectScheme: model.LegacyValidation,
|
||||
},
|
||||
{
|
||||
name: "local setting overrides global setting",
|
||||
inputFile: "scrape_config_local_global_validation_mode",
|
||||
expectScheme: "utf8",
|
||||
expectScheme: model.UTF8Validation,
|
||||
},
|
||||
}
|
||||
|
||||
@ -2832,31 +2832,31 @@ func TestScrapeConfigNameEscapingSettings(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string
|
||||
expectValidationScheme string
|
||||
expectValidationScheme model.ValidationScheme
|
||||
expectEscapingScheme string
|
||||
}{
|
||||
{
|
||||
name: "blank config implies default",
|
||||
inputFile: "scrape_config_default_validation_mode",
|
||||
expectValidationScheme: "utf8",
|
||||
expectValidationScheme: model.UTF8Validation,
|
||||
expectEscapingScheme: "allow-utf-8",
|
||||
},
|
||||
{
|
||||
name: "global setting implies local settings",
|
||||
inputFile: "scrape_config_global_validation_mode",
|
||||
expectValidationScheme: "legacy",
|
||||
expectValidationScheme: model.LegacyValidation,
|
||||
expectEscapingScheme: "dots",
|
||||
},
|
||||
{
|
||||
name: "local setting",
|
||||
inputFile: "scrape_config_local_validation_mode",
|
||||
expectValidationScheme: "legacy",
|
||||
expectValidationScheme: model.LegacyValidation,
|
||||
expectEscapingScheme: "values",
|
||||
},
|
||||
{
|
||||
name: "local setting overrides global setting",
|
||||
inputFile: "scrape_config_local_global_validation_mode",
|
||||
expectValidationScheme: "utf8",
|
||||
expectValidationScheme: model.UTF8Validation,
|
||||
expectEscapingScheme: "dots",
|
||||
},
|
||||
}
|
||||
|
@ -57,6 +57,8 @@ func (p *Provider) Discoverer() Discoverer {
|
||||
|
||||
// IsStarted return true if Discoverer is started.
|
||||
func (p *Provider) IsStarted() bool {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
return p.cancel != nil
|
||||
}
|
||||
|
||||
@ -216,15 +218,22 @@ func (m *Manager) ApplyConfig(cfg map[string]Configs) error {
|
||||
newProviders []*Provider
|
||||
)
|
||||
for _, prov := range m.providers {
|
||||
// Cancel obsolete providers.
|
||||
if len(prov.newSubs) == 0 {
|
||||
// Cancel obsolete providers if it has no new subs and it has a cancel function.
|
||||
// prov.cancel != nil is the same check as we use in IsStarted() method but we don't call IsStarted
|
||||
// here because it would take a lock and we need the same lock ourselves for other reads.
|
||||
prov.mu.RLock()
|
||||
if len(prov.newSubs) == 0 && prov.cancel != nil {
|
||||
wg.Add(1)
|
||||
prov.done = func() {
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
prov.cancel()
|
||||
prov.mu.RUnlock()
|
||||
continue
|
||||
}
|
||||
prov.mu.RUnlock()
|
||||
|
||||
newProviders = append(newProviders, prov)
|
||||
// refTargets keeps reference targets used to populate new subs' targets as they should be the same.
|
||||
var refTargets map[string]*targetgroup.Group
|
||||
@ -298,7 +307,9 @@ func (m *Manager) startProvider(ctx context.Context, p *Provider) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
updates := make(chan []*targetgroup.Group)
|
||||
|
||||
p.mu.Lock()
|
||||
p.cancel = cancel
|
||||
p.mu.Unlock()
|
||||
|
||||
go p.d.Run(ctx, updates)
|
||||
go m.updater(ctx, p, updates)
|
||||
@ -306,16 +317,20 @@ func (m *Manager) startProvider(ctx context.Context, p *Provider) {
|
||||
|
||||
// cleaner cleans resources associated with provider.
|
||||
func (m *Manager) cleaner(p *Provider) {
|
||||
p.mu.RLock()
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
m.targetsMtx.Lock()
|
||||
for s := range p.subs {
|
||||
delete(m.targets, poolKey{s, p.name})
|
||||
}
|
||||
m.targetsMtx.Unlock()
|
||||
p.mu.RUnlock()
|
||||
if p.done != nil {
|
||||
p.done()
|
||||
}
|
||||
|
||||
// Provider was cleaned so mark is as down.
|
||||
p.cancel = nil
|
||||
}
|
||||
|
||||
func (m *Manager) updater(ctx context.Context, p *Provider, updates chan []*targetgroup.Group) {
|
||||
@ -350,8 +365,10 @@ func (m *Manager) updater(ctx context.Context, p *Provider, updates chan []*targ
|
||||
|
||||
func (m *Manager) sender() {
|
||||
ticker := time.NewTicker(m.updatert)
|
||||
defer ticker.Stop()
|
||||
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
close(m.syncCh)
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-m.ctx.Done():
|
||||
@ -380,9 +397,11 @@ func (m *Manager) cancelDiscoverers() {
|
||||
m.mtx.RLock()
|
||||
defer m.mtx.RUnlock()
|
||||
for _, p := range m.providers {
|
||||
p.mu.RLock()
|
||||
if p.cancel != nil {
|
||||
p.cancel()
|
||||
}
|
||||
p.mu.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
@ -491,19 +510,3 @@ func (m *Manager) registerProviders(cfgs Configs, setName string) int {
|
||||
}
|
||||
return failed
|
||||
}
|
||||
|
||||
// StaticProvider holds a list of target groups that never change.
|
||||
type StaticProvider struct {
|
||||
TargetGroups []*targetgroup.Group
|
||||
}
|
||||
|
||||
// Run implements the Worker interface.
|
||||
func (sd *StaticProvider) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
|
||||
// We still have to consider that the consumer exits right away in which case
|
||||
// the context will be canceled.
|
||||
select {
|
||||
case ch <- sd.TargetGroups:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
close(ch)
|
||||
}
|
||||
|
@ -1562,3 +1562,53 @@ func TestUnregisterMetrics(t *testing.T) {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
// Calling ApplyConfig() that removes providers at the same time as shutting down
|
||||
// the manager should not hang.
|
||||
func TestConfigReloadAndShutdownRace(t *testing.T) {
|
||||
reg := prometheus.NewRegistry()
|
||||
_, sdMetrics := NewTestMetrics(t, reg)
|
||||
|
||||
mgrCtx, mgrCancel := context.WithCancel(context.Background())
|
||||
discoveryManager := NewManager(mgrCtx, promslog.NewNopLogger(), reg, sdMetrics)
|
||||
require.NotNil(t, discoveryManager)
|
||||
discoveryManager.updatert = 100 * time.Millisecond
|
||||
|
||||
var wgDiscovery sync.WaitGroup
|
||||
wgDiscovery.Add(1)
|
||||
go func() {
|
||||
discoveryManager.Run()
|
||||
wgDiscovery.Done()
|
||||
}()
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
|
||||
var wgBg sync.WaitGroup
|
||||
updateChan := discoveryManager.SyncCh()
|
||||
wgBg.Add(1)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
defer wgBg.Done()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-updateChan:
|
||||
}
|
||||
}()
|
||||
|
||||
c := map[string]Configs{
|
||||
"prometheus": {staticConfig("bar:9090")},
|
||||
}
|
||||
discoveryManager.ApplyConfig(c)
|
||||
|
||||
delete(c, "prometheus")
|
||||
wgBg.Add(1)
|
||||
go func() {
|
||||
discoveryManager.ApplyConfig(c)
|
||||
wgBg.Done()
|
||||
}()
|
||||
mgrCancel()
|
||||
wgDiscovery.Wait()
|
||||
|
||||
cancel()
|
||||
wgBg.Wait()
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ func replaceYAMLTypeError(err error, oldTyp, newTyp reflect.Type) error {
|
||||
func RegisterSDMetrics(registerer prometheus.Registerer, rmm RefreshMetricsManager) (map[string]DiscovererMetrics, error) {
|
||||
err := rmm.Register()
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to create service discovery refresh metrics")
|
||||
return nil, fmt.Errorf("failed to create service discovery refresh metrics: %w", err)
|
||||
}
|
||||
|
||||
metrics := make(map[string]DiscovererMetrics)
|
||||
@ -274,7 +274,7 @@ func RegisterSDMetrics(registerer prometheus.Registerer, rmm RefreshMetricsManag
|
||||
currentSdMetrics := conf.NewDiscovererMetrics(registerer, rmm)
|
||||
err = currentSdMetrics.Register()
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to create service discovery metrics")
|
||||
return nil, fmt.Errorf("failed to create service discovery metrics: %w", err)
|
||||
}
|
||||
metrics[conf.Name()] = currentSdMetrics
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ Check if the Prometheus server is healthy.
|
||||
|
||||
| Flag | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| <code class="text-nowrap">--http.config.file</code> | HTTP client configuration file for promtool to connect to Prometheus. | |
|
||||
| <code class="text-nowrap">--http.config.file</code> | HTTP client configuration file, see details at https://prometheus.io/docs/prometheus/latest/configuration/promtool | |
|
||||
| <code class="text-nowrap">--url</code> | The URL for the Prometheus server. | `http://localhost:9090` |
|
||||
|
||||
|
||||
@ -158,7 +158,7 @@ Check if the Prometheus server is ready.
|
||||
|
||||
| Flag | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| <code class="text-nowrap">--http.config.file</code> | HTTP client configuration file for promtool to connect to Prometheus. | |
|
||||
| <code class="text-nowrap">--http.config.file</code> | HTTP client configuration file, see details at https://prometheus.io/docs/prometheus/latest/configuration/promtool | |
|
||||
| <code class="text-nowrap">--url</code> | The URL for the Prometheus server. | `http://localhost:9090` |
|
||||
|
||||
|
||||
@ -213,7 +213,7 @@ Run query against a Prometheus server.
|
||||
| Flag | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| <code class="text-nowrap">-o</code>, <code class="text-nowrap">--format</code> | Output format of the query. | `promql` |
|
||||
| <code class="text-nowrap">--http.config.file</code> | HTTP client configuration file for promtool to connect to Prometheus. | |
|
||||
| <code class="text-nowrap">--http.config.file</code> | HTTP client configuration file, see details at https://prometheus.io/docs/prometheus/latest/configuration/promtool | |
|
||||
|
||||
|
||||
|
||||
@ -404,7 +404,7 @@ Push to a Prometheus server.
|
||||
|
||||
| Flag | Description |
|
||||
| --- | --- |
|
||||
| <code class="text-nowrap">--http.config.file</code> | HTTP client configuration file for promtool to connect to Prometheus. |
|
||||
| <code class="text-nowrap">--http.config.file</code> | HTTP client configuration file, see details at https://prometheus.io/docs/prometheus/latest/configuration/promtool |
|
||||
|
||||
|
||||
|
||||
@ -672,7 +672,7 @@ Create blocks of data for new recording rules.
|
||||
|
||||
| Flag | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| <code class="text-nowrap">--http.config.file</code> | HTTP client configuration file for promtool to connect to Prometheus. | |
|
||||
| <code class="text-nowrap">--http.config.file</code> | HTTP client configuration file, see details at https://prometheus.io/docs/prometheus/latest/configuration/promtool | |
|
||||
| <code class="text-nowrap">--url</code> | The URL for the Prometheus API with the data where the rule will be backfilled from. | `http://localhost:9090` |
|
||||
| <code class="text-nowrap">--start</code> | The time to start backfilling the new rule from. Must be a RFC3339 formatted date or Unix timestamp. Required. | |
|
||||
| <code class="text-nowrap">--end</code> | If an end time is provided, all recording rules in the rule files provided will be backfilled to the end time. Default will backfill up to 3 hours ago. Must be a RFC3339 formatted date or Unix timestamp. | |
|
||||
|
@ -197,6 +197,11 @@ otlp:
|
||||
# - "NoUTF8EscapingWithSuffixes" is a mode that relies on UTF-8 support in Prometheus.
|
||||
# It preserves all special characters like dots, but still adds required metric name suffixes
|
||||
# for units and _total, as UnderscoreEscapingWithSuffixes does.
|
||||
# - "UnderscoreEscapingWithoutSuffixes" translates metric name characters that
|
||||
# are not alphanumerics/underscores/colons to underscores, and label name
|
||||
# characters that are not alphanumerics/underscores to underscores, but
|
||||
# unlike UnderscoreEscapingWithSuffixes it does not append any suffixes to
|
||||
# the names.
|
||||
# - (EXPERIMENTAL) "NoTranslation" is a mode that relies on UTF-8 support in Prometheus.
|
||||
# It preserves all special character like dots and won't append special suffixes for metric
|
||||
# unit and type.
|
||||
@ -257,7 +262,7 @@ job_name: <job_name>
|
||||
# OpenMetricsText1.0.0, PrometheusText0.0.4, PrometheusText1.0.0.
|
||||
[ scrape_protocols: [<string>, ...] | default = <global_config.scrape_protocols> ]
|
||||
|
||||
# Fallback protocol to use if a scrape returns blank, unparseable, or otherwise
|
||||
# Fallback protocol to use if a scrape returns blank, unparsable, or otherwise
|
||||
# invalid Content-Type.
|
||||
# Supported values (case sensitive): PrometheusProto, OpenMetricsText0.0.1,
|
||||
# OpenMetricsText1.0.0, PrometheusText0.0.4, PrometheusText1.0.0.
|
||||
@ -2283,7 +2288,7 @@ The following meta labels are available on targets during [relabeling](#relabel_
|
||||
See below for the configuration options for STACKIT discovery:
|
||||
|
||||
```yaml
|
||||
# The STACKIT project
|
||||
# The STACKIT project
|
||||
project: <string>
|
||||
|
||||
# STACKIT region to use. No automatic discovery of the region is done.
|
||||
|
175
docs/configuration/promtool.md
Normal file
175
docs/configuration/promtool.md
Normal file
@ -0,0 +1,175 @@
|
||||
---
|
||||
title: HTTP configuration for promtool
|
||||
sort_rank: 6
|
||||
---
|
||||
|
||||
Promtool is a versatile CLI tool for Prometheus that supports validation, debugging, querying, unit testing, tsdb management, pushing data, and experimental PromQL editing.
|
||||
|
||||
Prometheus supports basic authentication and TLS. Since promtool needs to connect to Prometheus, we need to provide the authentication details. To specify those authentication details, use the `--http.config.file` for all requests that need to communicate with Prometheus.
|
||||
For instance, if you would like to check whether your local Prometheus server is healthy, you would use:
|
||||
```bash
|
||||
promtool check healthy --url=http://localhost:9090 --http.config.file=http-config-file.yml
|
||||
```
|
||||
|
||||
The file is written in [YAML format](https://en.wikipedia.org/wiki/YAML), defined by the schema described below.
|
||||
Brackets indicate that a parameter is optional. For non-list parameters the value is set to the specified default.
|
||||
|
||||
The file is read upon every http request, such as any change in the
|
||||
configuration and the certificates is picked up immediately.
|
||||
|
||||
Generic placeholders are defined as follows:
|
||||
|
||||
* `<bool>`: a boolean that can take the values `true` or `false`
|
||||
* `<filename>`: a valid path to a file
|
||||
* `<secret>`: a regular string that is a secret, such as a password
|
||||
* `<string>`: a regular string
|
||||
|
||||
A valid example file can be found [here](/documentation/examples/promtool-http-config-file.yml).
|
||||
|
||||
```yaml
|
||||
# Note that `basic_auth` and `authorization` options are mutually exclusive.
|
||||
|
||||
# Sets the `Authorization` header with the configured username and password.
|
||||
# `username_ref` and `password_ref`refer to the name of the secret within the secret manager.
|
||||
# `password`, `password_file` and `password_ref` are mutually exclusive.
|
||||
basic_auth:
|
||||
[ username: <string> ]
|
||||
[ username_file: <filename> ]
|
||||
[ username_ref: <string> ]
|
||||
[ password: <secret> ]
|
||||
[ password_file: <string> ]
|
||||
[ password_ref: <string> ]
|
||||
|
||||
# Optional the `Authorization` header configuration.
|
||||
authorization:
|
||||
# Sets the authentication type.
|
||||
[ type: <string> | default: Bearer ]
|
||||
# Sets the credentials. It is mutually exclusive with
|
||||
# `credentials_file`.
|
||||
[ credentials: <secret> ]
|
||||
# Sets the credentials with the credentials read from the configured file.
|
||||
# It is mutually exclusive with `credentials`.
|
||||
[ credentials_file: <filename> ]
|
||||
[ credentials_ref: <string> ]
|
||||
|
||||
# Optional OAuth 2.0 configuration.
|
||||
# Cannot be used at the same time as basic_auth or authorization.
|
||||
oauth2:
|
||||
[ <oauth2> ]
|
||||
|
||||
tls_config:
|
||||
[ <tls_config> ]
|
||||
|
||||
[ follow_redirects: <bool> | default: true ]
|
||||
|
||||
# Whether to enable HTTP2.
|
||||
[ enable_http2: <bool> | default: true ]
|
||||
|
||||
# Optional proxy URL.
|
||||
[ proxy_url: <string> ]
|
||||
# Comma-separated string that can contain IPs, CIDR notation, domain names
|
||||
# that should be excluded from proxying. IP and domain names can
|
||||
# contain port numbers.
|
||||
[ no_proxy: <string> ]
|
||||
[ proxy_from_environment: <bool> ]
|
||||
[ proxy_connect_header:
|
||||
[ <string>: [ <secret>, ... ] ] ]
|
||||
|
||||
# `http_headers` specifies a set of headers that will be injected into each request.
|
||||
http_headers:
|
||||
[ <string>: <header> ]
|
||||
```
|
||||
|
||||
## \<oauth2\>
|
||||
OAuth 2.0 authentication using the client credentials grant type.
|
||||
```yaml
|
||||
# `client_id` and `client_secret` are used to authenticate your
|
||||
# application with the authorization server in order to get
|
||||
# an access token.
|
||||
# `client_secret`, `client_secret_file` and `client_secret_ref` are mutually exclusive.
|
||||
client_id: <string>
|
||||
[ client_secret: <secret> ]
|
||||
[ client_secret_file: <filename> ]
|
||||
[ client_secret_ref: <string> ]
|
||||
|
||||
# `scopes` specify the reason for the resource access.
|
||||
scopes:
|
||||
[ - <string> ...]
|
||||
|
||||
# The URL to fetch the token from.
|
||||
token_url: <string>
|
||||
|
||||
# Optional parameters to append to the token URL.
|
||||
[ endpoint_params:
|
||||
<string>: <string> ... ]
|
||||
|
||||
# Configures the token request's TLS settings.
|
||||
tls_config:
|
||||
[ <tls_config> ]
|
||||
|
||||
# Optional proxy URL.
|
||||
[ proxy_url: <string> ]
|
||||
# Comma-separated string that can contain IPs, CIDR notation, domain names
|
||||
# that should be excluded from proxying. IP and domain names can
|
||||
# contain port numbers.
|
||||
[ no_proxy: <string> ]
|
||||
[ proxy_from_environment: <bool> ]
|
||||
[ proxy_connect_header:
|
||||
[ <string>: [ <secret>, ... ] ] ]
|
||||
```
|
||||
|
||||
## <tls_config>
|
||||
```yaml
|
||||
# For the following configurations, use either `ca`, `cert` and `key` or `ca_file`, `cert_file` and `key_file` or use `ca_ref`, `cert_ref` or `key_ref`.
|
||||
# Text of the CA certificate to use for the server.
|
||||
[ ca: <string> ]
|
||||
# CA certificate to validate the server certificate with.
|
||||
[ ca_file: <filename> ]
|
||||
# `ca_ref` is the name of the secret within the secret manager to use as the CA cert.
|
||||
[ ca_ref: <string> ]
|
||||
|
||||
# Text of the client cert file for the server.
|
||||
[ cert: <string> ]
|
||||
# Certificate file for client certificate authentication.
|
||||
[ cert_file: <filename> ]
|
||||
# `cert_ref` is the name of the secret within the secret manager to use as the client certificate.
|
||||
[ cert_ref: <string> ]
|
||||
|
||||
# Text of the client key file for the server.
|
||||
[ key: <secret> ]
|
||||
# Key file for client certificate authentication.
|
||||
[ key_file: <filename> ]
|
||||
# `key_ref` is the name of the secret within the secret manager to use as the client key.
|
||||
[ key_ref: <string> ]
|
||||
|
||||
# ServerName extension to indicate the name of the server.
|
||||
# http://tools.ietf.org/html/rfc4366#section-3.1
|
||||
[ server_name: <string> ]
|
||||
|
||||
# Disable validation of the server certificate.
|
||||
[ insecure_skip_verify: <bool> ]
|
||||
|
||||
# Minimum acceptable TLS version. Accepted values: TLS10 (TLS 1.0), TLS11 (TLS
|
||||
# 1.1), TLS12 (TLS 1.2), TLS13 (TLS 1.3).
|
||||
# If unset, promtool will use Go default minimum version, which is TLS 1.2.
|
||||
# See MinVersion in https://pkg.go.dev/crypto/tls#Config.
|
||||
[ min_version: <string> ]
|
||||
# Maximum acceptable TLS version. Accepted values: TLS10 (TLS 1.0), TLS11 (TLS
|
||||
# 1.1), TLS12 (TLS 1.2), TLS13 (TLS 1.3).
|
||||
# If unset, promtool will use Go default maximum version, which is TLS 1.3.
|
||||
# See MaxVersion in https://pkg.go.dev/crypto/tls#Config.
|
||||
[ max_version: <string> ]
|
||||
```
|
||||
|
||||
## \<header\>
|
||||
`header` represents the configuration for a single HTTP header.
|
||||
```yaml
|
||||
[ values:
|
||||
[ - <string> ... ] ]
|
||||
|
||||
[ secrets:
|
||||
[ - <secret> ... ] ]
|
||||
|
||||
[ files:
|
||||
[ - <filename> ... ] ]
|
||||
```
|
@ -55,8 +55,10 @@ If functions are used in a pipeline, the pipeline value is passed as the last ar
|
||||
| humanize1024 | number or string | string | Like `humanize`, but uses 1024 as the base rather than 1000. |
|
||||
| humanizeDuration | number or string | string | Converts a duration in seconds to a more readable format. |
|
||||
| humanizePercentage | number or string | string | Converts a ratio value to a fraction of 100. |
|
||||
| humanizeTimestamp | number or string | string | Converts a Unix timestamp in seconds to a more readable format. |
|
||||
| toTime | number or string | *time.Time | Converts a Unix timestamp in seconds to a time.Time. |
|
||||
| humanizeTimestamp | number or string | string | Converts a Unix timestamp in seconds to a more readable format. |
|
||||
| toTime | number or string | *time.Time | Converts a Unix timestamp in seconds to a time.Time. |
|
||||
| toDuration | number or string | *time.Duration | Converts a duration in seconds to a time.Duration. |
|
||||
| now | none | float64 | Returns the Unix timestamp in seconds at the time of the template evaluation. |
|
||||
|
||||
Humanizing functions are intended to produce reasonable output for consumption
|
||||
by humans, and are not guaranteed to return the same results between Prometheus
|
||||
|
@ -181,6 +181,8 @@ This state is periodically ([`max_stale`][d2c]) cleared of inactive series.
|
||||
Enabling this _can_ have negative impact on performance, because the in-memory
|
||||
state is mutex guarded. Cumulative-only OTLP requests are not affected.
|
||||
|
||||
[d2c]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/deltatocumulativeprocessor
|
||||
|
||||
## PromQL arithmetic expressions in time durations
|
||||
|
||||
`--enable-feature=promql-duration-expr`
|
||||
@ -203,6 +205,12 @@ When using offset with duration expressions, you must wrap the expression in
|
||||
parentheses. Without parentheses, only the first duration value will be used in
|
||||
the offset calculation.
|
||||
|
||||
`step()` can be used in duration expressions.
|
||||
For a **range query**, it resolves to the step width of the range query.
|
||||
For an **instant query**, it resolves to `0s`.
|
||||
|
||||
`min(<duration>, <duration>)` and `max(<duration>, <duration>)` can be used to find the minimum or maximum of two duration expressions.
|
||||
|
||||
**Note**: Duration expressions are not supported in the @ timestamp operator.
|
||||
|
||||
The following operators are supported:
|
||||
@ -216,14 +224,16 @@ The following operators are supported:
|
||||
|
||||
Examples of equivalent durations:
|
||||
|
||||
* `5m * 2` is the equivalent to `10m` or `600s`
|
||||
* `10m - 1m` is the equivalent to `9m` or `540s`
|
||||
* `(5+2) * 1m` is the equivalent to `7m` or `420s`
|
||||
* `1h / 2` is the equivalent to `30m` or `1800s`
|
||||
* `4h % 3h` is the equivalent to `1h` or `3600s`
|
||||
* `(2 ^ 3) * 1m` is the equivalent to `8m` or `480s`
|
||||
* `5m * 2` is equivalent to `10m` or `600s`
|
||||
* `10m - 1m` is equivalent to `9m` or `540s`
|
||||
* `(5+2) * 1m` is equivalent to `7m` or `420s`
|
||||
* `1h / 2` is equivalent to `30m` or `1800s`
|
||||
* `4h % 3h` is equivalent to `1h` or `3600s`
|
||||
* `(2 ^ 3) * 1m` is equivalent to `8m` or `480s`
|
||||
* `step() + 1` is equivalent to the query step width increased by 1s.
|
||||
* `max(step(), 5s)` is equivalent to the larger of the query step width and `5s`.
|
||||
* `min(2 * step() + 5s, 5m)` is equivalent to the smaller of twice the query step increased by `5s` and `5m`.
|
||||
|
||||
[d2c]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/deltatocumulativeprocessor
|
||||
|
||||
## OTLP Native Delta Support
|
||||
|
||||
|
@ -25,47 +25,58 @@ and vector/vector value pairs. They follow the usual [IEEE 754 floating point
|
||||
arithmetic](https://en.wikipedia.org/wiki/IEEE_754), including the handling of
|
||||
special values like `NaN`, `+Inf`, and `-Inf`.
|
||||
|
||||
**Between two scalars**, the behavior is obvious: they evaluate to another
|
||||
**Between two scalars**, the behavior is straightforward: they evaluate to another
|
||||
scalar that is the result of the operator applied to both scalar operands.
|
||||
|
||||
**Between an instant vector and a scalar**, the operator is applied to the
|
||||
value of every data sample in the vector. If the data sample is a float, the
|
||||
operation performed on the data sample is again obvious, e.g. if an instant
|
||||
vector of float samples is multiplied by 2, the result is another vector of
|
||||
float samples in which every sample value of the original vector is multiplied
|
||||
by 2. For vector elements that are histogram samples, the behavior is the
|
||||
following: For `*`, all bucket populations and the count and the sum of
|
||||
observations are multiplied by the scalar. For `/`, the histogram sample has to
|
||||
be on the left hand side (LHS), followed by the scalar on the right hand side
|
||||
(RHS). All bucket populations and the count and the sum of observations are
|
||||
then divided by the scalar. A division by zero results in a histogram with no
|
||||
regular buckets and the zero bucket population and the count and sum of
|
||||
observations all set to +Inf, -Inf, or NaN, depending on their values in the
|
||||
input histogram (positive, negative, or zero/NaN, respectively). For `/` with a
|
||||
scalar on the LHS and a histogram sample on the RHS, and similarly for all
|
||||
other arithmetic binary operators in any combination of a scalar and a
|
||||
histogram sample, there is no result and the corresponding element is removed
|
||||
from the resulting vector. Such a removal is flagged by an info-level
|
||||
annotation.
|
||||
value of every data sample in the vector.
|
||||
|
||||
If the data sample is a float, the operation is performed between that float and the scalar.
|
||||
For example, if an instant vector of float samples is multiplied by 2,
|
||||
the result is another vector of float samples in which every sample value of
|
||||
the original vector is multiplied by 2.
|
||||
|
||||
For vector elements that are histogram samples, the behavior is the
|
||||
following:
|
||||
|
||||
* For `*`, all bucket populations and the count and the sum of observations
|
||||
are multiplied by the scalar.
|
||||
|
||||
* For `/`, the histogram sample has to be on the left hand side (LHS), followed
|
||||
by the scalar on the right hand side (RHS). All bucket populations and the count
|
||||
and the sum of observations are then divided by the scalar. A division by zero
|
||||
results in a histogram with no regular buckets and the zero bucket population
|
||||
and the count and sum of observations all set to `+Inf`, `-Inf`, or `NaN`, depending
|
||||
on their values in the input histogram (positive, negative, or zero/`NaN`, respectively).
|
||||
|
||||
* For `/` with a scalar on the LHS and a histogram sample on the RHS, and similarly for all
|
||||
other arithmetic binary operators in any combination of a scalar and a
|
||||
histogram sample, there is no result and the corresponding element is removed
|
||||
from the resulting vector. Such a removal is flagged by an info-level
|
||||
annotation.
|
||||
|
||||
**Between two instant vectors**, a binary arithmetic operator is applied to
|
||||
each entry in the LHS vector and its [matching element](#vector-matching) in
|
||||
the RHS vector. The result is propagated into the result vector with the
|
||||
grouping labels becoming the output label set. Entries for which no matching
|
||||
entry in the right-hand vector can be found are not part of the result. If two
|
||||
float samples are matched, the behavior is obvious. If a float sample is
|
||||
matched with a histogram sample, the behavior follows the same logic as between
|
||||
a scalar and a histogram sample (see above), i.e. `*` and `/` (the latter with
|
||||
the histogram sample on the LHS) are valid operations, while all others lead to
|
||||
the removal of the corresponding element from the resulting vector. If two
|
||||
histogram samples are matched, only `+` and `-` are valid operations, each
|
||||
adding or substracting all matching bucket populations and the count and the
|
||||
entry in the right-hand vector can be found are not part of the result.
|
||||
|
||||
If two float samples are matched, the arithmetic operator is applied to the two input values.
|
||||
|
||||
If a float sample is matched with a histogram sample, the behavior follows the same
|
||||
logic as between a scalar and a histogram sample (see above), i.e. `*` and `/`
|
||||
(the latter with the histogram sample on the LHS) are valid operations, while all
|
||||
others lead to the removal of the corresponding element from the resulting vector.
|
||||
|
||||
If two histogram samples are matched, only `+` and `-` are valid operations, each
|
||||
adding or subtracting all matching bucket populations and the count and the
|
||||
sum of observations. All other operations result in the removal of the
|
||||
corresponding element from the output vector, flagged by an info-level
|
||||
annotation.
|
||||
|
||||
**In any arithmetic binary operation involving vectors**, the metric name is
|
||||
dropped.
|
||||
dropped. This occurs even if `__name__` is explicitly mentioned in `on`
|
||||
(see https://github.com/prometheus/prometheus/issues/16631 for further discussion).
|
||||
|
||||
### Trigonometric binary operators
|
||||
|
||||
@ -102,8 +113,8 @@ operators result in another scalar that is either `0` (`false`) or `1`
|
||||
|
||||
**Between an instant vector and a scalar**, these operators are applied to the
|
||||
value of every data sample in the vector, and vector elements between which the
|
||||
comparison result is `false` get dropped from the result vector. These
|
||||
operation only work with float samples in the vector. For histogram samples,
|
||||
comparison result is false get dropped from the result vector. These
|
||||
operations only work with float samples in the vector. For histogram samples,
|
||||
the corresponding element is removed from the result vector, flagged by an
|
||||
info-level annotation.
|
||||
|
||||
@ -111,19 +122,33 @@ info-level annotation.
|
||||
applied to matching entries. Vector elements for which the expression is not
|
||||
true or which do not find a match on the other side of the expression get
|
||||
dropped from the result, while the others are propagated into a result vector
|
||||
with the grouping labels becoming the output label set. Matches between two
|
||||
float samples work as usual, while matches between a float sample and a
|
||||
histogram sample are invalid. The corresponding element is removed from the
|
||||
result vector, flagged by an info-level annotation. Between two histogram
|
||||
samples, `==` and `!=` work as expected, but all other comparison binary
|
||||
operations are again invalid.
|
||||
with the grouping labels becoming the output label set.
|
||||
|
||||
Matches between two float samples work as usual.
|
||||
|
||||
Matches between a float sample and a histogram sample are invalid, and the
|
||||
corresponding element is removed from the result vector, flagged by an info-level
|
||||
annotation.
|
||||
|
||||
Between two histogram samples, `==` and `!=` work as expected, but all other
|
||||
comparison binary operations are again invalid.
|
||||
|
||||
**In any comparison binary operation involving vectors**, providing the `bool`
|
||||
modifier changes the behavior in the following way: Vector elements that would
|
||||
be dropped instead have the value `0` and vector elements that would be kept
|
||||
have the value `1`. Additionally, the metric name is dropped. (Note that
|
||||
invalid operations involving histogram samples still return no result rather
|
||||
than the value `0`.)
|
||||
modifier changes the behavior in the following ways:
|
||||
|
||||
* Vector elements which find a match on the other side of the expression but for
|
||||
which the expression is false instead have the value `0` and vector elements
|
||||
that do find a match and for which the expression is true have the value `1`.
|
||||
(Note that elements with no match or invalid operations involving histogram
|
||||
samples still return no result rather than the value `0`.)
|
||||
* The metric name is dropped.
|
||||
|
||||
If the `bool` modifier is not provided, then the metric name from the left side
|
||||
is retained, with some exceptions:
|
||||
|
||||
* If `on` is used, then the metric name is dropped.
|
||||
* If `group_right` is used, then the metric name from the right side is retained,
|
||||
to avoid collisions.
|
||||
|
||||
### Logical/set binary operators
|
||||
|
||||
@ -259,21 +284,21 @@ Prometheus supports the following built-in aggregation operators that can be
|
||||
used to aggregate the elements of a single instant vector, resulting in a new
|
||||
vector of fewer elements with aggregated values:
|
||||
|
||||
* `sum` (calculate sum over dimensions)
|
||||
* `avg` (calculate the arithmetic average over dimensions)
|
||||
* `min` (select minimum over dimensions)
|
||||
* `max` (select maximum over dimensions)
|
||||
* `bottomk` (smallest _k_ elements by sample value)
|
||||
* `topk` (largest _k_ elements by sample value)
|
||||
* `limitk` (sample _k_ elements, **experimental**, must be enabled with `--enable-feature=promql-experimental-functions`)
|
||||
* `limit_ratio` (sample a pseudo-random ratio _r_ of elements, **experimental**, must be enabled with `--enable-feature=promql-experimental-functions`)
|
||||
* `group` (all values in the resulting vector are 1)
|
||||
* `count` (count number of elements in the vector)
|
||||
* `count_values` (count number of elements with the same value)
|
||||
* `sum(v)` (calculate sum over dimensions)
|
||||
* `avg(v)` (calculate the arithmetic average over dimensions)
|
||||
* `min(v)` (select minimum over dimensions)
|
||||
* `max(v)` (select maximum over dimensions)
|
||||
* `bottomk(k, v)` (smallest `k` elements by sample value)
|
||||
* `topk(k, v)` (largest `k` elements by sample value)
|
||||
* `limitk(k, v)` (sample `k` elements, **experimental**, must be enabled with `--enable-feature=promql-experimental-functions`)
|
||||
* `limit_ratio(r, v)` (sample a pseudo-random ratio `r` of elements, **experimental**, must be enabled with `--enable-feature=promql-experimental-functions`)
|
||||
* `group(v)` (all values in the resulting vector are 1)
|
||||
* `count(v)` (count number of elements in the vector)
|
||||
* `count_values(l, v)` (count number of elements with the same value)
|
||||
|
||||
* `stddev` (calculate population standard deviation over dimensions)
|
||||
* `stdvar` (calculate population standard variance over dimensions)
|
||||
* `quantile` (calculate φ-quantile (0 ≤ φ ≤ 1) over dimensions)
|
||||
* `stddev(v)` (calculate population standard deviation over dimensions)
|
||||
* `stdvar(v)` (calculate population standard variance over dimensions)
|
||||
* `quantile(φ, v)` (calculate φ-quantile (0 ≤ φ ≤ 1) over dimensions)
|
||||
|
||||
These operators can either be used to aggregate over **all** label dimensions
|
||||
or preserve distinct dimensions by including a `without` or `by` clause. These
|
||||
@ -293,29 +318,62 @@ all other labels are preserved in the output. `by` does the opposite and drops
|
||||
labels that are not listed in the `by` clause, even if their label values are
|
||||
identical between all elements of the vector.
|
||||
|
||||
`parameter` is only required for `topk`, `bottomk`, `limitk`, `limit_ratio`,
|
||||
`quantile`, and `count_values`. It is used as the value for _k_, _r_, φ, or the
|
||||
name of the additional label, respectively.
|
||||
|
||||
### Detailed explanations
|
||||
|
||||
`sum` sums up sample values in the same way as the `+` binary operator does
|
||||
between two values. Similarly, `avg` divides the sum by the number of
|
||||
aggregated samples in the same way as the `/` binary operator. Therefore, all
|
||||
sample values aggregation into a single resulting vector element must either be
|
||||
#### `sum`
|
||||
|
||||
`sum(v)` sums up sample values in `v` in the same way as the `+` binary operator does
|
||||
between two values.
|
||||
|
||||
All sample values being aggregated into a single resulting vector element must either be
|
||||
float samples or histogram samples. An aggregation of a mix of both is invalid,
|
||||
resulting in the removeal of the corresponding vector element from the output
|
||||
resulting in the removal of the corresponding vector element from the output
|
||||
vector, flagged by a warn-level annotation.
|
||||
|
||||
`min` and `max` only operate on float samples, following IEEE 754 floating
|
||||
##### Examples
|
||||
|
||||
If the metric `memory_consumption_bytes` had time series that fan out by
|
||||
`application`, `instance`, and `group` labels, we could calculate the total
|
||||
memory consumption per application and group over all instances via:
|
||||
|
||||
sum without (instance) (memory_consumption_bytes)
|
||||
|
||||
Which is equivalent to:
|
||||
|
||||
sum by (application, group) (memory_consumption_bytes)
|
||||
|
||||
If we are just interested in the total memory consumption in **all**
|
||||
applications, we could simply write:
|
||||
|
||||
sum(memory_consumption_bytes)
|
||||
|
||||
#### `avg`
|
||||
|
||||
`avg(v)` divides the sum of `v` by the number of aggregated samples in the same way
|
||||
as the `/` binary operator.
|
||||
|
||||
All sample values being aggregated into a single resulting vector element must either be
|
||||
float samples or histogram samples. An aggregation of a mix of both is invalid,
|
||||
resulting in the removal of the corresponding vector element from the output
|
||||
vector, flagged by a warn-level annotation.
|
||||
|
||||
#### `min` and `max`
|
||||
|
||||
`min(v)` and `max(v)` return the minimum or maximum value, respectively, in `v`.
|
||||
|
||||
They only operate on float samples, following IEEE 754 floating
|
||||
point arithmetic, which in particular implies that `NaN` is only ever
|
||||
considered a minimum or maximum if all aggregated values are `NaN`. Histogram
|
||||
samples in the input vector are ignored, flagged by an info-level annotation.
|
||||
|
||||
`topk` and `bottomk` are different from other aggregators in that a subset of
|
||||
the input samples, including the original labels, are returned in the result
|
||||
vector. `by` and `without` are only used to bucket the input vector. Similar to
|
||||
`min` and `max`, they only operate on float samples, considering `NaN` values
|
||||
#### `topk` and `bottomk`
|
||||
|
||||
`topk(k, v)` and `bottomk(k, v)` are different from other aggregators in that a subset of
|
||||
`k` values from the input samples, including the original labels, are returned in the result vector.
|
||||
|
||||
`by` and `without` are only used to bucket the input vector.
|
||||
|
||||
Similar to `min` and `max`, they only operate on float samples, considering `NaN` values
|
||||
to be farthest from the top or bottom, respectively. Histogram samples in the
|
||||
input vector are ignored, flagged by an info-level annotation.
|
||||
|
||||
@ -323,72 +381,108 @@ If used in an instant query, `topk` and `bottomk` return series ordered by
|
||||
value in descending or ascending order, respectively. If used with `by` or
|
||||
`without`, then series within each bucket are sorted by value, and series in
|
||||
the same bucket are returned consecutively, but there is no guarantee that
|
||||
buckets of series will be returned in any particular order. No sorting applies
|
||||
to range queries.
|
||||
buckets of series will be returned in any particular order.
|
||||
|
||||
`limitk` and `limit_ratio` also return a subset of the input samples, including
|
||||
the original labels in the result vector. The subset is selected in a
|
||||
deterministic pseudo-random way. `limitk` picks _k_ samples, while
|
||||
`limit_ratio` picks a ratio _r_ of samples (each determined by `parameter`).
|
||||
This happens independent of the sample type. Therefore, it works for both float
|
||||
samples and histogram samples. _r_ can be between +1 and -1. The absolute value
|
||||
of _r_ is used as the selection ratio, but the selection order is inverted for
|
||||
a negative _r_, which can be used to select complements. For example,
|
||||
`limit_ratio(0.1, ...)` returns a deterministic set of approximatiely 10% of
|
||||
No sorting applies to range queries.
|
||||
|
||||
##### Example
|
||||
|
||||
To get the 5 instances with the highest memory consumption across all instances we could write:
|
||||
|
||||
topk(5, memory_consumption_bytes)
|
||||
|
||||
#### `limitk` and `limit_ratio`
|
||||
|
||||
`limitk(k, v)` returns a subset of `k` input samples, including
|
||||
the original labels in the result vector.
|
||||
|
||||
The subset is selected in a deterministic pseudo-random way.
|
||||
This happens independent of the sample type.
|
||||
Therefore, it works for both float samples and histogram samples.
|
||||
|
||||
##### Example
|
||||
|
||||
To sample 10 timeseries we could write:
|
||||
|
||||
limitk(10, memory_consumption_bytes)
|
||||
|
||||
#### `limit_ratio`
|
||||
|
||||
`limit_ratio(r, v)` returns a subset of the input samples, including
|
||||
the original labels in the result vector.
|
||||
|
||||
The subset is selected in a deterministic pseudo-random way.
|
||||
This happens independent of the sample type.
|
||||
Therefore, it works for both float samples and histogram samples.
|
||||
|
||||
`r` can be between +1 and -1. The absolute value of `r` is used as the selection ratio,
|
||||
but the selection order is inverted for a negative `r`, which can be used to select complements.
|
||||
For example, `limit_ratio(0.1, ...)` returns a deterministic set of approximatiely 10% of
|
||||
the input samples, while `limit_ratio(-0.9, ...)` returns precisely the
|
||||
remaining approximately 90% of the input samples not returned by
|
||||
`limit_ratio(0.1, ...)`.
|
||||
remaining approximately 90% of the input samples not returned by `limit_ratio(0.1, ...)`.
|
||||
|
||||
`group` and `count` do not interact with the sample values,
|
||||
they work in the same way for float samples and histogram samples.
|
||||
#### `group`
|
||||
|
||||
`group(v)` returns 1 for each group that contains any value at that timestamp.
|
||||
|
||||
The value may be a float or histogram sample.
|
||||
|
||||
#### `count`
|
||||
|
||||
`count(v)` returns the number of values at that timestamp, or no value at all
|
||||
if no values are present at that timestamp.
|
||||
|
||||
The value may be a float or histogram sample.
|
||||
|
||||
#### `count_values`
|
||||
|
||||
`count_values(l, v)` outputs one time series per unique sample value in `v`.
|
||||
Each series has an additional label, given by `l`, and the label value is the
|
||||
unique sample value. The value of each time series is the number of times that sample value was present.
|
||||
|
||||
`count_values` outputs one time series per unique sample value. Each series has
|
||||
an additional label. The name of that label is given by the aggregation
|
||||
parameter, and the label value is the unique sample value. The value of each
|
||||
time series is the number of times that sample value was present.
|
||||
`count_values` works with both float samples and histogram samples. For the
|
||||
latter, a compact string representation of the histogram sample value is used
|
||||
as the label value.
|
||||
|
||||
`stddev` and `stdvar` only work with float samples, following IEEE 754 floating
|
||||
point arithmetic. Histogram samples in the input vector are ignored, flagged by
|
||||
an info-level annotation.
|
||||
|
||||
`quantile` calculates the φ-quantile, the value that ranks at number φ*N among
|
||||
the N metric values of the dimensions aggregated over. φ is provided as the
|
||||
aggregation parameter. For example, `quantile(0.5, ...)` calculates the median,
|
||||
`quantile(0.95, ...)` the 95th percentile. For φ = `NaN`, `NaN` is returned.
|
||||
For φ < 0, `-Inf` is returned. For φ > 1, `+Inf` is returned.
|
||||
|
||||
### Examples
|
||||
|
||||
If the metric `http_requests_total` had time series that fan out by
|
||||
`application`, `instance`, and `group` labels, we could calculate the total
|
||||
number of seen HTTP requests per application and group over all instances via:
|
||||
|
||||
sum without (instance) (http_requests_total)
|
||||
|
||||
Which is equivalent to:
|
||||
|
||||
sum by (application, group) (http_requests_total)
|
||||
|
||||
If we are just interested in the total of HTTP requests we have seen in **all**
|
||||
applications, we could simply write:
|
||||
|
||||
sum(http_requests_total)
|
||||
##### Example
|
||||
|
||||
To count the number of binaries running each build version we could write:
|
||||
|
||||
count_values("version", build_version)
|
||||
|
||||
To get the 5 largest HTTP requests counts across all instances we could write:
|
||||
#### `stddev`
|
||||
|
||||
topk(5, http_requests_total)
|
||||
`stddev(v)` returns the standard deviation of `v`.
|
||||
|
||||
To sample 10 timeseries, for example to inspect labels and their values, we
|
||||
could write:
|
||||
`stddev` only works with float samples, following IEEE 754 floating
|
||||
point arithmetic. Histogram samples in the input vector are ignored, flagged by
|
||||
an info-level annotation.
|
||||
|
||||
limitk(10, http_requests_total)
|
||||
#### `stdvar`
|
||||
|
||||
`stdvar(v)` returns the standard deviation of `v`.
|
||||
|
||||
`stdvar` only works with float samples, following IEEE 754 floating
|
||||
point arithmetic. Histogram samples in the input vector are ignored, flagged by
|
||||
an info-level annotation.
|
||||
|
||||
#### `quantile`
|
||||
|
||||
`quantile(φ, v)` calculates the φ-quantile, the value that ranks at number φ*N among
|
||||
the N metric values of the dimensions aggregated over.
|
||||
|
||||
`quantile` only works with float samples. Histogram samples in the input vector
|
||||
are ignored, flagged by an info-level annotation.
|
||||
|
||||
`NaN` is considered the smallest possible value.
|
||||
|
||||
For example, `quantile(0.5, ...)` calculates the median, `quantile(0.95, ...)` the 95th percentile.
|
||||
|
||||
Special cases:
|
||||
|
||||
* For φ = `NaN`, `NaN` is returned.
|
||||
* For φ < 0, `-Inf` is returned.
|
||||
* For φ > 1, `+Inf` is returned.
|
||||
|
||||
## Binary operator precedence
|
||||
|
||||
|
12
documentation/examples/promtool-http-config-file.yml
Normal file
12
documentation/examples/promtool-http-config-file.yml
Normal file
@ -0,0 +1,12 @@
|
||||
# TLS and basic authentication configuration example.
|
||||
|
||||
# For `basic_auth`, use the Prometheus credentials configured earlier in the `web-config.yml` file.
|
||||
# The password must be provided in plaintext.
|
||||
# To avoid including plaintext passwords directly in this file, consider using `password_file` or `password_ref` instead.
|
||||
basic_auth:
|
||||
username: alice
|
||||
password: verylongpassword
|
||||
|
||||
tls_config:
|
||||
cert_file: server.crt
|
||||
key_file: server.key
|
@ -445,6 +445,7 @@ local row = panel.row;
|
||||
dashboard.new('%(prefix)sOverview' % $._config.grafanaPrometheus)
|
||||
+ dashboard.time.withFrom('now-1h')
|
||||
+ dashboard.withTags($._config.grafanaPrometheus.tags)
|
||||
+ dashboard.withUid('9fa0d141-d019-4ad7-8bc5-42196ee308bd')
|
||||
+ dashboard.timepicker.withRefreshIntervals($._config.grafanaPrometheus.refresh)
|
||||
+ dashboard.withVariables(std.prune([
|
||||
datasourceVariable,
|
||||
|
46
go.mod
46
go.mod
@ -15,8 +15,8 @@ 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.152.0
|
||||
github.com/docker/docker v28.2.2+incompatible
|
||||
github.com/digitalocean/godo v1.157.0
|
||||
github.com/docker/docker v28.3.0+incompatible
|
||||
github.com/edsrzf/mmap-go v1.2.0
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1
|
||||
@ -38,19 +38,19 @@ require (
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/klauspost/compress v1.18.0
|
||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b
|
||||
github.com/linode/linodego v1.52.1
|
||||
github.com/linode/linodego v1.52.2
|
||||
github.com/miekg/dns v1.1.66
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f
|
||||
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1
|
||||
github.com/oklog/run v1.1.0
|
||||
github.com/oklog/run v1.2.0
|
||||
github.com/oklog/ulid/v2 v2.1.1
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.128.0
|
||||
github.com/ovh/go-ovh v1.8.0
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.129.0
|
||||
github.com/ovh/go-ovh v1.9.0
|
||||
github.com/prometheus/alertmanager v0.28.1
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/prometheus/client_model v0.6.2
|
||||
github.com/prometheus/common v0.65.0
|
||||
github.com/prometheus/common v0.65.1-0.20250703115700-7f8b2a0d32d3
|
||||
github.com/prometheus/common/assets v0.2.0
|
||||
github.com/prometheus/exporter-toolkit v0.14.0
|
||||
github.com/prometheus/sigv4 v0.2.0
|
||||
@ -59,10 +59,10 @@ require (
|
||||
github.com/stackitcloud/stackit-sdk-go/core v0.17.2
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/vultr/govultr/v2 v2.17.2
|
||||
go.opentelemetry.io/collector/component v1.34.0
|
||||
go.opentelemetry.io/collector/consumer v1.34.0
|
||||
go.opentelemetry.io/collector/pdata v1.34.0
|
||||
go.opentelemetry.io/collector/processor v1.34.0
|
||||
go.opentelemetry.io/collector/component v1.35.0
|
||||
go.opentelemetry.io/collector/consumer v1.35.0
|
||||
go.opentelemetry.io/collector/pdata v1.35.0
|
||||
go.opentelemetry.io/collector/processor v1.35.0
|
||||
go.opentelemetry.io/collector/semconv v0.128.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0
|
||||
@ -81,7 +81,7 @@ require (
|
||||
golang.org/x/sync v0.15.0
|
||||
golang.org/x/sys v0.33.0
|
||||
golang.org/x/text v0.26.0
|
||||
google.golang.org/api v0.238.0
|
||||
google.golang.org/api v0.239.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822
|
||||
google.golang.org/grpc v1.73.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
@ -115,8 +115,8 @@ require (
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
||||
go.opentelemetry.io/collector/featuregate v1.34.0 // indirect
|
||||
go.opentelemetry.io/collector/internal/telemetry v0.128.0 // indirect
|
||||
go.opentelemetry.io/collector/featuregate v1.35.0 // indirect
|
||||
go.opentelemetry.io/collector/internal/telemetry v0.129.0 // indirect
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.11.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.12.2 // indirect
|
||||
)
|
||||
@ -153,7 +153,7 @@ require (
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/validate v0.24.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.16.5 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
@ -180,7 +180,7 @@ require (
|
||||
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
||||
github.com/knadh/koanf/maps v0.1.2 // indirect
|
||||
github.com/knadh/koanf/providers/confmap v1.0.0 // indirect
|
||||
github.com/knadh/koanf/v2 v2.2.0 // indirect
|
||||
github.com/knadh/koanf/v2 v2.2.1 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
@ -197,8 +197,8 @@ require (
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.128.0 // indirect
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.128.0 // indirect
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.129.0 // indirect
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.129.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
@ -206,7 +206,7 @@ require (
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/otlptranslator v0.0.0-20250527173959-2573485683d5
|
||||
github.com/prometheus/otlptranslator v0.0.0-20250620074007-94f535e0c588
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
@ -214,9 +214,9 @@ require (
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/collector/confmap v1.34.0 // indirect
|
||||
go.opentelemetry.io/collector/confmap/xconfmap v0.128.0 // indirect
|
||||
go.opentelemetry.io/collector/pipeline v0.128.0 // indirect
|
||||
go.opentelemetry.io/collector/confmap v1.35.0 // indirect
|
||||
go.opentelemetry.io/collector/confmap/xconfmap v0.129.0 // indirect
|
||||
go.opentelemetry.io/collector/pipeline v0.129.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
@ -225,7 +225,7 @@ require (
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
|
128
go.sum
128
go.sum
@ -110,14 +110,14 @@ 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.152.0 h1:WRgkPMogZSXEJK70IkZKTB/PsMn16hMQ+NI3wCIQdzA=
|
||||
github.com/digitalocean/godo v1.152.0/go.mod h1:tYeiWY5ZXVpU48YaFv0M5irUFHXGorZpDNm7zzdWMzM=
|
||||
github.com/digitalocean/godo v1.157.0 h1:ReELaS6FxXNf8gryUiVH0wmyUmZN8/NCmBX4gXd3F0o=
|
||||
github.com/digitalocean/godo v1.157.0/go.mod h1:tYeiWY5ZXVpU48YaFv0M5irUFHXGorZpDNm7zzdWMzM=
|
||||
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=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw=
|
||||
github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.3.0+incompatible h1:ffS62aKWupCWdvcee7nBU9fhnmknOqDPaJAMtfK0ImQ=
|
||||
github.com/docker/docker v28.3.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
@ -176,8 +176,8 @@ github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ
|
||||
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=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
|
||||
github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-zookeeper/zk v1.0.4 h1:DPzxraQx7OrPyXq2phlGlNSIyWEsAox0RJmjTseMV6I=
|
||||
github.com/go-zookeeper/zk v1.0.4/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
@ -314,8 +314,8 @@ github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpb
|
||||
github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
|
||||
github.com/knadh/koanf/providers/confmap v1.0.0 h1:mHKLJTE7iXEys6deO5p6olAiZdG5zwp8Aebir+/EaRE=
|
||||
github.com/knadh/koanf/providers/confmap v1.0.0/go.mod h1:txHYHiI2hAtF0/0sCmcuol4IDcuQbKTybiB1nOcUo1A=
|
||||
github.com/knadh/koanf/v2 v2.2.0 h1:FZFwd9bUjpb8DyCWARUBy5ovuhDs1lI87dOEn2K8UVU=
|
||||
github.com/knadh/koanf/v2 v2.2.0/go.mod h1:PSFru3ufQgTsI7IF+95rf9s8XA1+aHxKuO/W+dPoHEY=
|
||||
github.com/knadh/koanf/v2 v2.2.1 h1:jaleChtw85y3UdBnI0wCqcg1sj1gPoz6D3caGNHtrNE=
|
||||
github.com/knadh/koanf/v2 v2.2.1/go.mod h1:PSFru3ufQgTsI7IF+95rf9s8XA1+aHxKuO/W+dPoHEY=
|
||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00=
|
||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@ -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.52.1 h1:HJ1cz1n9n3chRP9UrtqmP91+xTi0Q5l+H/4z4tpkwgQ=
|
||||
github.com/linode/linodego v1.52.1/go.mod h1:zEN2sX+cSdp67EuRY1HJiyuLujoa7HqvVwNEcJv3iXw=
|
||||
github.com/linode/linodego v1.52.2 h1:N9ozU27To1LMSrDd8WvJZ5STSz1eGYdyLnxhAR/dIZg=
|
||||
github.com/linode/linodego v1.52.2/go.mod h1:bI949fZaVchjWyKIA08hNyvAcV6BAS+PM2op3p7PAWA=
|
||||
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=
|
||||
@ -395,8 +395,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM=
|
||||
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E=
|
||||
github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk=
|
||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
|
||||
@ -405,20 +405,20 @@ github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.128.0 h1:hZa4FkI2JhYC0tkiwOepnHyyfWzezz3FfCmt88nWJa0=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.128.0/go.mod h1:sLbOuJEFckPdw4li0RtWpoSsMeppcck3s/cmzPyKAgc=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.128.0 h1:+rUULr4xqOJjZK3SokFmRYzsiPq5onoWoSv3He4aaus=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.128.0/go.mod h1:Fh2SXPeFkr4J97w9CV/apFAib8TC9Hi0P08xtiT7Lng=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.128.0 h1:8OWwRSdIhm3DY3PEYJ0PtSEz1a1OjL0fghLXSr14JMk=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.128.0/go.mod h1:32OeaysZe4vkSmD1LJ18Q1DfooryYqpSzFNmz+5A5RU=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.128.0 h1:9wVFaWEhgV8WQD+nP662nHNaQIkmyF57KRhtsqlaWEI=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.128.0/go.mod h1:Yak3vQIvwYQiAO83u+zD9ujdCmpcDL7JSfg2YK+Mwn4=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.129.0 h1:2pzb6bC/AAfciC9DN+8d7Y8Rsk8ZPCfp/ACTfZu87FQ=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.129.0/go.mod h1:tIE4dzdxuM7HnFeYA6sj5zfLuUA/JxzQ+UDl1YrHvQw=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.129.0 h1:ydkfqpZ5BWZfEJEs7OUhTHW59og5aZspbUYxoGcAEok=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.129.0/go.mod h1:oA+49dkzmhUx0YFC9JXGuPPSBL0TOTp6jkv7qSr2n0Q=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.129.0 h1:AOVxBvCZfTPj0GLGqBVHpAnlC9t9pl1JXUQXymHliiY=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.129.0/go.mod h1:0CAJ32V/bCUBhNTEvnN9wlOG5IsyZ+Bmhe9e3Eri7CU=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.129.0 h1:yDLSAoIi3jNt4R/5xN4IJ9YAg1rhOShgchlO/ESv8EY=
|
||||
github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.129.0/go.mod h1:IXQHbTPxqNcuu44FvkyvpYJ6Qy4wh4YsCVkKsp0Flzo=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/ovh/go-ovh v1.8.0 h1:eQ5TAAFZvZAVarQir62oaTL+8a503pIBuOWVn72iGtY=
|
||||
github.com/ovh/go-ovh v1.8.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
|
||||
github.com/ovh/go-ovh v1.9.0 h1:6K8VoL3BYjVV3In9tPJUdT7qMx9h0GExN9EXx1r2kKE=
|
||||
github.com/ovh/go-ovh v1.9.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
@ -454,14 +454,14 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
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.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/common v0.65.1-0.20250703115700-7f8b2a0d32d3 h1:R/zO7ombSHCI8bjQusgCMSL+cE669w5/R2upq5WlPD0=
|
||||
github.com/prometheus/common v0.65.1-0.20250703115700-7f8b2a0d32d3/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
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.14.0 h1:NMlswfibpcZZ+H0sZBiTjrA3/aBFHkNZqE+iCj5EmRg=
|
||||
github.com/prometheus/exporter-toolkit v0.14.0/go.mod h1:Gu5LnVvt7Nr/oqTBUC23WILZepW0nffNo10XdhQcwWA=
|
||||
github.com/prometheus/otlptranslator v0.0.0-20250527173959-2573485683d5 h1:LCbPeVKZSu9RS4CsaDCOmDCcribskJ8c6H5u1VvyxY0=
|
||||
github.com/prometheus/otlptranslator v0.0.0-20250527173959-2573485683d5/go.mod h1:v1PzmPjSnNkmZSDvKJ9OmsWcmWMEF5+JdllEcXrRfzM=
|
||||
github.com/prometheus/otlptranslator v0.0.0-20250620074007-94f535e0c588 h1:QlySqDdSESgWDePeAYskbbcKKdowI26m9aU9zloHyYE=
|
||||
github.com/prometheus/otlptranslator v0.0.0-20250620074007-94f535e0c588/go.mod h1:P8AwMgdD7XEr6QRUJ2QWLpiAZTgTE2UYgjlu3svompI=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
@ -522,40 +522,40 @@ go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd
|
||||
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/collector/component v1.34.0 h1:YONg7FaZ5zZbj5cLdARvwtMNuZHunuyxw2fWe5fcWqc=
|
||||
go.opentelemetry.io/collector/component v1.34.0/go.mod h1:GvolsSVZskXuyfQdwYacqeBSZe/1tg4RJ0YK55KSvDA=
|
||||
go.opentelemetry.io/collector/component/componentstatus v0.128.0 h1:0lEYHgUQEMMkl5FLtMgDH8lue4B3auElQINzGIWUya4=
|
||||
go.opentelemetry.io/collector/component/componentstatus v0.128.0/go.mod h1:8vVO6JSV+edmiezJsQzW7aKQ7sFLIN6S3JawKBI646o=
|
||||
go.opentelemetry.io/collector/component/componenttest v0.128.0 h1:MGNh5lQQ0Qmz2SmNwOqLJYaWMDkMLYj/51wjMzTBR34=
|
||||
go.opentelemetry.io/collector/component/componenttest v0.128.0/go.mod h1:hALNxcacqOaX/Gm/dE7sNOxAEFj41SbRqtvF57Yd6gs=
|
||||
go.opentelemetry.io/collector/confmap v1.34.0 h1:PG4sYlLxgCMnA5F7daKXZV+NKjU1IzXBzVQeyvcwyh0=
|
||||
go.opentelemetry.io/collector/confmap v1.34.0/go.mod h1:BbAit8+hAJg5vyFBQoDh9vOXOH8UzCdNu91jCh+b72E=
|
||||
go.opentelemetry.io/collector/confmap/xconfmap v0.128.0 h1:hcVKU45pjC+PLz7xUc8kwSlR5wsN2w8hs9midZ3ez10=
|
||||
go.opentelemetry.io/collector/confmap/xconfmap v0.128.0/go.mod h1:2928x4NAAu1CysfzLbEJE6MSSDB/gOYVq6YRGWY9LmM=
|
||||
go.opentelemetry.io/collector/consumer v1.34.0 h1:oBhHH6mgViOGhVDPozE+sUdt7jFBo2Hh32lsSr2L3Tc=
|
||||
go.opentelemetry.io/collector/consumer v1.34.0/go.mod h1:DVMCb56ZBlPNcmo0lSJKn3rp18oyZQCedRE4GKIMI+Q=
|
||||
go.opentelemetry.io/collector/consumer/consumertest v0.128.0 h1:x50GB0I/QvU3sQuNCap5z/P2cnq2yHoRJ/8awkiT87w=
|
||||
go.opentelemetry.io/collector/consumer/consumertest v0.128.0/go.mod h1:Wb3IAbMY/DOIwJPy81PuBiW2GnKoNIz4THE7wfJwovE=
|
||||
go.opentelemetry.io/collector/consumer/xconsumer v0.128.0 h1:4E+KTdCjkRS3SIw0bsv5kpv9XFXHf8x9YiPEuxBVEHY=
|
||||
go.opentelemetry.io/collector/consumer/xconsumer v0.128.0/go.mod h1:OmzilL/qbjCzPMHay+WEA7/cPe5xuX7Jbj5WPIpqaMo=
|
||||
go.opentelemetry.io/collector/featuregate v1.34.0 h1:zqDHpEYy1UeudrfUCvlcJL2t13dXywrC6lwpNZ5DrCU=
|
||||
go.opentelemetry.io/collector/featuregate v1.34.0/go.mod h1:Y/KsHbvREENKvvN9RlpiWk/IGBK+CATBYzIIpU7nccc=
|
||||
go.opentelemetry.io/collector/internal/telemetry v0.128.0 h1:ySEYWoY7J8DAYdlw2xlF0w+ODQi3AhYj7TRNflsCbx8=
|
||||
go.opentelemetry.io/collector/internal/telemetry v0.128.0/go.mod h1:572B/iJqjauv3aT+zcwnlNWBPqM7+KqrYGSUuOAStrM=
|
||||
go.opentelemetry.io/collector/pdata v1.34.0 h1:2vwYftckXe7pWxI9mfSo+tw3wqdGNrYpMbDx/5q6rw8=
|
||||
go.opentelemetry.io/collector/pdata v1.34.0/go.mod h1:StPHMFkhLBellRWrULq0DNjv4znCDJZP6La4UuC+JHI=
|
||||
go.opentelemetry.io/collector/pdata/pprofile v0.128.0 h1:6DEtzs/liqv/ukz2EHbC5OMaj2V6K2pzuj/LaRg2YmY=
|
||||
go.opentelemetry.io/collector/pdata/pprofile v0.128.0/go.mod h1:bVVRpz+zKFf1UCCRUFqy8LvnO3tHlXKkdqW2d+Wi/iA=
|
||||
go.opentelemetry.io/collector/pdata/testdata v0.128.0 h1:5xcsMtyzvb18AnS2skVtWreQP1nl6G3PiXaylKCZ6pA=
|
||||
go.opentelemetry.io/collector/pdata/testdata v0.128.0/go.mod h1:9/VYVgzv3JMuIyo19KsT3FwkVyxbh3Eg5QlabQEUczA=
|
||||
go.opentelemetry.io/collector/pipeline v0.128.0 h1:WgNXdFbyf/QRLy5XbO/jtPQosWrSWX/TEnSYpJq8bgI=
|
||||
go.opentelemetry.io/collector/pipeline v0.128.0/go.mod h1:TO02zju/K6E+oFIOdi372Wk0MXd+Szy72zcTsFQwXl4=
|
||||
go.opentelemetry.io/collector/processor v1.34.0 h1:5pwXIG12XXxdkJ8F68e2cBEjEnFlCIAZhqEYM7vjkqE=
|
||||
go.opentelemetry.io/collector/processor v1.34.0/go.mod h1:VCl4vYj2tdO4APUcr0q6Eh796mqCCsH9Z/gqaPuzlUs=
|
||||
go.opentelemetry.io/collector/processor/processortest v0.128.0 h1:xPhOSmGFDGqhC3/nu1BqPSE6EpDPAf1/F+BfaYjDn/8=
|
||||
go.opentelemetry.io/collector/processor/processortest v0.128.0/go.mod h1:XXXom+mbAQtrkcvq4Ecd6n8RQoVgcfLe1vrUlr6U2gI=
|
||||
go.opentelemetry.io/collector/processor/xprocessor v0.128.0 h1:ObbtdXab0is6bdt4XabsRJZ+SUTuwQjPVlHTbmScfNg=
|
||||
go.opentelemetry.io/collector/processor/xprocessor v0.128.0/go.mod h1:/nHXW15nzwSRQ+25Cb+r17he/uMtCEvSOBGqpDbn3Uk=
|
||||
go.opentelemetry.io/collector/component v1.35.0 h1:JpvBukEcEUvJ/TInF1KYpXtWEP+C7iYkxCHKjI0o7BQ=
|
||||
go.opentelemetry.io/collector/component v1.35.0/go.mod h1:hU/ieWPxWbMAacODCSqem5ZaN6QH9W5GWiZ3MtXVuwc=
|
||||
go.opentelemetry.io/collector/component/componentstatus v0.129.0 h1:ejpBAt7hXAAZiQKcSxLvcy8sj8SjY4HOLdoXIlW6ybw=
|
||||
go.opentelemetry.io/collector/component/componentstatus v0.129.0/go.mod h1:/dLPIxn/tRMWmGi+DPtuFoBsffOLqPpSZ2IpEQzYtwI=
|
||||
go.opentelemetry.io/collector/component/componenttest v0.129.0 h1:gpKkZGCRPu3Yn0U2co09bMvhs17yLFb59oV8Gl9mmRI=
|
||||
go.opentelemetry.io/collector/component/componenttest v0.129.0/go.mod h1:JR9k34Qvd/pap6sYkPr5QqdHpTn66A5lYeYwhenKBAM=
|
||||
go.opentelemetry.io/collector/confmap v1.35.0 h1:U4JDATAl4PrKWe9bGHbZkoQXmJXefWgR2DIkFvw8ULQ=
|
||||
go.opentelemetry.io/collector/confmap v1.35.0/go.mod h1:qX37ExVBa+WU4jWWJCZc7IJ+uBjb58/9oL+/ctF1Bt0=
|
||||
go.opentelemetry.io/collector/confmap/xconfmap v0.129.0 h1:Q/+pJKrkCaMPSoSAH2BpC3UZCh+5hTiFkh/bdy5yChk=
|
||||
go.opentelemetry.io/collector/confmap/xconfmap v0.129.0/go.mod h1:RNMnlay2meJDXcKjxiLbST9/YAhKLJlj0kZCrJrLGgw=
|
||||
go.opentelemetry.io/collector/consumer v1.35.0 h1:mgS42yh1maXBIE65IT4//iOA89BE+7xSUzV8czyevHg=
|
||||
go.opentelemetry.io/collector/consumer v1.35.0/go.mod h1:9sSPX0hDHaHqzR2uSmfLOuFK9v3e9K3HRQ+fydAjOWs=
|
||||
go.opentelemetry.io/collector/consumer/consumertest v0.129.0 h1:kRmrAgVvPxH5c/rTaOYAzyy0YrrYhQpBNkuqtDRrgeU=
|
||||
go.opentelemetry.io/collector/consumer/consumertest v0.129.0/go.mod h1:JgJKms1+v/CuAjkPH+ceTnKeDgUUGTQV4snGu5wTEHY=
|
||||
go.opentelemetry.io/collector/consumer/xconsumer v0.129.0 h1:bRyJ9TGWwnrUnB5oQGTjPhxpVRbkIVeugmvks22bJ4A=
|
||||
go.opentelemetry.io/collector/consumer/xconsumer v0.129.0/go.mod h1:pbe5ZyPJrtzdt/RRI0LqfT1GVBiJLbtkDKx3SBRTiTY=
|
||||
go.opentelemetry.io/collector/featuregate v1.35.0 h1:c/XRtA35odgxVc4VgOF/PTIk7ajw1wYdQ6QI562gzd4=
|
||||
go.opentelemetry.io/collector/featuregate v1.35.0/go.mod h1:Y/KsHbvREENKvvN9RlpiWk/IGBK+CATBYzIIpU7nccc=
|
||||
go.opentelemetry.io/collector/internal/telemetry v0.129.0 h1:jkzRpIyMxMGdAzVOcBe8aRNrbP7eUrMq6cxEHe0sbzA=
|
||||
go.opentelemetry.io/collector/internal/telemetry v0.129.0/go.mod h1:riAPlR2LZBV7VEx4LicOKebg3N1Ja3izzkv5fl1Lhiw=
|
||||
go.opentelemetry.io/collector/pdata v1.35.0 h1:ck6WO6hCNjepADY/p9sT9/rLECTLO5ukYTumKzsqB/E=
|
||||
go.opentelemetry.io/collector/pdata v1.35.0/go.mod h1:pttpb089864qG1k0DMeXLgwwTFLk+o3fAW9I6MF9tzw=
|
||||
go.opentelemetry.io/collector/pdata/pprofile v0.129.0 h1:DgZTvjOGmyZRx7Or80hz8XbEaGwHPkIh2SX1A5eXttQ=
|
||||
go.opentelemetry.io/collector/pdata/pprofile v0.129.0/go.mod h1:uUBZxqJNOk6QIMvbx30qom//uD4hXJ1K/l3qysijMLE=
|
||||
go.opentelemetry.io/collector/pdata/testdata v0.129.0 h1:n1QLnLOtrcAR57oMSVzmtPsQEpCc/nE5Avk1xfuAkjY=
|
||||
go.opentelemetry.io/collector/pdata/testdata v0.129.0/go.mod h1:RfY5IKpmcvkS2IGVjl9jG9fcT7xpQEBWpg9sQOn/7mY=
|
||||
go.opentelemetry.io/collector/pipeline v0.129.0 h1:Mp7RuKLizLQJ0381eJqKQ0zpgkFlhTE9cHidpJQIvMU=
|
||||
go.opentelemetry.io/collector/pipeline v0.129.0/go.mod h1:TO02zju/K6E+oFIOdi372Wk0MXd+Szy72zcTsFQwXl4=
|
||||
go.opentelemetry.io/collector/processor v1.35.0 h1:YOfHemhhodYn4BnPjN7kWYYDhzPVqRkyHCaQ8mAlavs=
|
||||
go.opentelemetry.io/collector/processor v1.35.0/go.mod h1:cWHDOpmpAaVNCc9K9j2/okZoLIuP/EpGGRNhM4JGmFM=
|
||||
go.opentelemetry.io/collector/processor/processortest v0.129.0 h1:r5iJHdS7Ffdb2zmMVYx4ahe92PLrce5cas/AJEXivkY=
|
||||
go.opentelemetry.io/collector/processor/processortest v0.129.0/go.mod h1:gdf8GzyzjGoDTA11+CPwC4jfXphtC+B7MWbWn+LIWXc=
|
||||
go.opentelemetry.io/collector/processor/xprocessor v0.129.0 h1:V3Zgd+YIeu3Ij3DPlGtzdcTwpqOQIqQVcL5jdHHS7sc=
|
||||
go.opentelemetry.io/collector/processor/xprocessor v0.129.0/go.mod h1:78T+AP5NO137W/E+SibQhaqOyS67fR+IN697b4JFh00=
|
||||
go.opentelemetry.io/collector/semconv v0.128.0 h1:MzYOz7Vgb3Kf5D7b49pqqgeUhEmOCuT10bIXb/Cc+k4=
|
||||
go.opentelemetry.io/collector/semconv v0.128.0/go.mod h1:OPXer4l43X23cnjLXIZnRj/qQOjSuq4TgBLI76P9hns=
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.11.0 h1:u2E32P7j1a/gRgZDWhIXC+Shd4rLg70mnE7QLI/Ssnw=
|
||||
@ -675,14 +675,14 @@ 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.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
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.238.0 h1:+EldkglWIg/pWjkq97sd+XxH7PxakNYoe/rkSTbnvOs=
|
||||
google.golang.org/api v0.238.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
|
||||
google.golang.org/api v0.239.0 h1:2hZKUnFZEy81eugPs4e2XzIJ5SOwQg0G82bpXD65Puo=
|
||||
google.golang.org/api v0.239.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
|
||||
|
@ -6,7 +6,7 @@ require (
|
||||
github.com/bufbuild/buf v1.51.0
|
||||
github.com/daixiang0/gci v0.13.6
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1
|
||||
)
|
||||
|
||||
require (
|
||||
@ -18,7 +18,7 @@ require (
|
||||
buf.build/go/bufplugin v0.8.0 // indirect
|
||||
buf.build/go/protoyaml v0.3.1 // indirect
|
||||
buf.build/go/spdx v0.2.0 // indirect
|
||||
cel.dev/expr v0.21.2 // indirect
|
||||
cel.dev/expr v0.23.0 // indirect
|
||||
connectrpc.com/connect v1.18.1 // indirect
|
||||
connectrpc.com/otelconnect v0.7.2 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
@ -97,19 +97,19 @@ require (
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/grpc v1.70.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
pluginrpc.com/pluginrpc v0.5.0 // indirect
|
||||
)
|
||||
|
@ -14,8 +14,8 @@ buf.build/go/protoyaml v0.3.1 h1:ucyzE7DRnjX+mQ6AH4JzN0Kg50ByHHu+yrSKbgQn2D4=
|
||||
buf.build/go/protoyaml v0.3.1/go.mod h1:0TzNpFQDXhwbkXb/ajLvxIijqbve+vMQvWY/b3/Dzxg=
|
||||
buf.build/go/spdx v0.2.0 h1:IItqM0/cMxvFJJumcBuP8NrsIzMs/UYjp/6WSpq8LTw=
|
||||
buf.build/go/spdx v0.2.0/go.mod h1:bXdwQFem9Si3nsbNy8aJKGPoaPi5DKwdeEp5/ArZ6w8=
|
||||
cel.dev/expr v0.21.2 h1:o+Wj235dy4gFYlYin3JsMpp3EEfMrPm/6tdoyjT98S0=
|
||||
cel.dev/expr v0.21.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cel.dev/expr v0.23.0 h1:wUb94w6OYQS4uXraxo9U+wUAs9jT47Xvl4iPgAwM2ss=
|
||||
cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw=
|
||||
connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8=
|
||||
connectrpc.com/otelconnect v0.7.2 h1:WlnwFzaW64dN06JXU+hREPUGeEzpz3Acz2ACOmN8cMI=
|
||||
@ -112,8 +112,8 @@ github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7 h1:+J3r2e8+RsmN3vKfo7
|
||||
github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
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/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
@ -260,25 +260,25 @@ go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 h1:aWwlzYV971S4BXRS9AmqwDLAD85ouC6X+pocatKY58c=
|
||||
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||
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.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -288,34 +288,34 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.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-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.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
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/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
@ -248,6 +248,17 @@ func (ls Labels) WithoutEmpty() Labels {
|
||||
return ls
|
||||
}
|
||||
|
||||
// ByteSize returns the approximate size of the labels in bytes including
|
||||
// the two string headers size for name and value.
|
||||
// Slice header size is ignored because it should be amortized to zero.
|
||||
func (ls Labels) ByteSize() uint64 {
|
||||
var size uint64 = 0
|
||||
for _, l := range ls {
|
||||
size += uint64(len(l.Name)+len(l.Value)) + 2*uint64(unsafe.Sizeof(""))
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// Equal returns whether the two label sets are equal.
|
||||
func Equal(ls, o Labels) bool {
|
||||
return slices.Equal(ls, o)
|
||||
|
@ -417,6 +417,13 @@ func (ls Labels) WithoutEmpty() Labels {
|
||||
return ls
|
||||
}
|
||||
|
||||
// ByteSize returns the approximate size of the labels in bytes.
|
||||
// String header size is ignored because it should be amortized to zero.
|
||||
// SymbolTable size is also not taken into account.
|
||||
func (ls Labels) ByteSize() uint64 {
|
||||
return uint64(len(ls.data))
|
||||
}
|
||||
|
||||
// Equal returns whether the two label sets are equal.
|
||||
func Equal(a, b Labels) bool {
|
||||
if a.syms == b.syms {
|
||||
|
@ -21,6 +21,24 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var expectedSizeOfLabels = []uint64{ // Values must line up with testCaseLabels.
|
||||
16,
|
||||
0,
|
||||
41,
|
||||
270,
|
||||
271,
|
||||
325,
|
||||
}
|
||||
|
||||
var expectedByteSize = []uint64{ // Values must line up with testCaseLabels.
|
||||
8,
|
||||
0,
|
||||
8,
|
||||
8,
|
||||
8,
|
||||
32,
|
||||
}
|
||||
|
||||
func TestVarint(t *testing.T) {
|
||||
cases := []struct {
|
||||
v int
|
||||
|
27
model/labels/labels_slicelabels_test.go
Normal file
27
model/labels/labels_slicelabels_test.go
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2025 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build slicelabels
|
||||
|
||||
package labels
|
||||
|
||||
var expectedSizeOfLabels = []uint64{ // Values must line up with testCaseLabels.
|
||||
72,
|
||||
0,
|
||||
97,
|
||||
326,
|
||||
327,
|
||||
549,
|
||||
}
|
||||
|
||||
var expectedByteSize = expectedSizeOfLabels // They are identical
|
@ -283,6 +283,13 @@ func (ls Labels) WithoutEmpty() Labels {
|
||||
return ls
|
||||
}
|
||||
|
||||
// ByteSize returns the approximate size of the labels in bytes.
|
||||
// String header size is ignored because it should be amortized to zero
|
||||
// because it may be shared across multiple copies of the Labels.
|
||||
func (ls Labels) ByteSize() uint64 {
|
||||
return uint64(len(ls.data))
|
||||
}
|
||||
|
||||
// Equal returns whether the two label sets are equal.
|
||||
func Equal(ls, o Labels) bool {
|
||||
return ls.data == o.data
|
||||
|
34
model/labels/labels_stringlabels_test.go
Normal file
34
model/labels/labels_stringlabels_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2025 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !slicelabels && !dedupelabels
|
||||
|
||||
package labels
|
||||
|
||||
var expectedSizeOfLabels = []uint64{ // Values must line up with testCaseLabels.
|
||||
12,
|
||||
0,
|
||||
37,
|
||||
266,
|
||||
270,
|
||||
309,
|
||||
}
|
||||
|
||||
var expectedByteSize = []uint64{ // Values must line up with testCaseLabels.
|
||||
12,
|
||||
0,
|
||||
37,
|
||||
266,
|
||||
270,
|
||||
309,
|
||||
}
|
@ -26,37 +26,33 @@ import (
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
s254 = strings.Repeat("x", 254) // Edge cases for stringlabels encoding.
|
||||
s255 = strings.Repeat("x", 255)
|
||||
)
|
||||
|
||||
var testCaseLabels = []Labels{
|
||||
FromStrings("t1", "t1", "t2", "t2"),
|
||||
{},
|
||||
FromStrings("service.name", "t1", "whatever\\whatever", "t2"),
|
||||
FromStrings("aaa", "111", "xx", s254),
|
||||
FromStrings("aaa", "111", "xx", s255),
|
||||
FromStrings("__name__", "kube_pod_container_status_last_terminated_exitcode", "cluster", "prod-af-north-0", " container", "prometheus", "instance", "kube-state-metrics-0:kube-state-metrics:ksm", "job", "kube-state-metrics/kube-state-metrics", " namespace", "observability-prometheus", "pod", "observability-prometheus-0", "uid", "d3ec90b2-4975-4607-b45d-b9ad64bb417e"),
|
||||
}
|
||||
|
||||
func TestLabels_String(t *testing.T) {
|
||||
s254 := strings.Repeat("x", 254) // Edge cases for stringlabels encoding.
|
||||
s255 := strings.Repeat("x", 255)
|
||||
cases := []struct {
|
||||
labels Labels
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
labels: FromStrings("t1", "t1", "t2", "t2"),
|
||||
expected: "{t1=\"t1\", t2=\"t2\"}",
|
||||
},
|
||||
{
|
||||
labels: Labels{},
|
||||
expected: "{}",
|
||||
},
|
||||
{
|
||||
labels: FromStrings("service.name", "t1", "whatever\\whatever", "t2"),
|
||||
expected: `{"service.name"="t1", "whatever\\whatever"="t2"}`,
|
||||
},
|
||||
{
|
||||
labels: FromStrings("aaa", "111", "xx", s254),
|
||||
expected: `{aaa="111", xx="` + s254 + `"}`,
|
||||
},
|
||||
{
|
||||
labels: FromStrings("aaa", "111", "xx", s255),
|
||||
expected: `{aaa="111", xx="` + s255 + `"}`,
|
||||
},
|
||||
expected := []string{ // Values must line up with testCaseLabels.
|
||||
"{t1=\"t1\", t2=\"t2\"}",
|
||||
"{}",
|
||||
`{"service.name"="t1", "whatever\\whatever"="t2"}`,
|
||||
`{aaa="111", xx="` + s254 + `"}`,
|
||||
`{aaa="111", xx="` + s255 + `"}`,
|
||||
`{" container"="prometheus", " namespace"="observability-prometheus", __name__="kube_pod_container_status_last_terminated_exitcode", cluster="prod-af-north-0", instance="kube-state-metrics-0:kube-state-metrics:ksm", job="kube-state-metrics/kube-state-metrics", pod="observability-prometheus-0", uid="d3ec90b2-4975-4607-b45d-b9ad64bb417e"}`,
|
||||
}
|
||||
for _, c := range cases {
|
||||
str := c.labels.String()
|
||||
require.Equal(t, c.expected, str)
|
||||
require.Len(t, expected, len(testCaseLabels))
|
||||
for i, c := range expected {
|
||||
str := testCaseLabels[i].String()
|
||||
require.Equal(t, c, str)
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +63,44 @@ func BenchmarkString(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSizeOfLabels(t *testing.T) {
|
||||
require.Len(t, expectedSizeOfLabels, len(testCaseLabels))
|
||||
for i, c := range expectedSizeOfLabels { // Declared in build-tag-specific files, e.g. labels_slicelabels_test.go.
|
||||
var total uint64
|
||||
testCaseLabels[i].Range(func(l Label) {
|
||||
total += SizeOfLabels(l.Name, l.Value, 1)
|
||||
})
|
||||
require.Equal(t, c, total)
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteSize(t *testing.T) {
|
||||
require.Len(t, expectedByteSize, len(testCaseLabels))
|
||||
for i, c := range expectedByteSize { // Declared in build-tag-specific files, e.g. labels_slicelabels_test.go.
|
||||
require.Equal(t, c, testCaseLabels[i].ByteSize())
|
||||
}
|
||||
}
|
||||
|
||||
var GlobalTotal uint64 // Encourage the compiler not to elide the benchmark computation.
|
||||
|
||||
func BenchmarkSize(b *testing.B) {
|
||||
lb := New(benchmarkLabels...)
|
||||
b.Run("SizeOfLabels", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var total uint64
|
||||
lb.Range(func(l Label) {
|
||||
total += SizeOfLabels(l.Name, l.Value, 1)
|
||||
})
|
||||
GlobalTotal = total
|
||||
}
|
||||
})
|
||||
b.Run("ByteSize", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
GlobalTotal = lb.ByteSize()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLabels_MatchLabels(t *testing.T) {
|
||||
labels := FromStrings(
|
||||
"__name__", "ALERTS",
|
||||
|
@ -49,7 +49,7 @@ sum without(instance) (rate(requests_total[5m]))
|
||||
|
||||
require.Equal(t, "HighAlert", rg.Rules[2].Alert)
|
||||
require.Equal(t, "critical", rg.Rules[2].Labels["severity"])
|
||||
require.Equal(t, "stuff's happening with {{ $.labels.service }}", rg.Rules[0].Annotations["description"])
|
||||
require.Equal(t, "stuff's happening with {{ $.labels.service }}", rg.Rules[2].Annotations["description"])
|
||||
|
||||
require.Equal(t, "HighAlert2", rg.Rules[3].Alert)
|
||||
require.Equal(t, "critical", rg.Rules[3].Labels["severity"])
|
||||
|
@ -254,7 +254,10 @@ func (n *Manager) targetUpdateLoop(tsets <-chan map[string][]*targetgroup.Group)
|
||||
select {
|
||||
case <-n.stopRequested:
|
||||
return
|
||||
case ts := <-tsets:
|
||||
case ts, ok := <-tsets:
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
n.reload(ts)
|
||||
}
|
||||
}
|
||||
|
@ -21,14 +21,25 @@ import (
|
||||
"github.com/prometheus/prometheus/promql/parser"
|
||||
)
|
||||
|
||||
// durationVisitor is a visitor that visits a duration expression and calculates the duration.
|
||||
type durationVisitor struct{}
|
||||
// durationVisitor is a visitor that calculates the actual value of
|
||||
// duration expressions in AST nodes. For example the query
|
||||
// "http_requests_total offset (1h / 2)" is represented in the AST
|
||||
// as a VectorSelector with OriginalOffset 0 and the duration expression
|
||||
// in OriginalOffsetExpr representing (1h / 2). This visitor evaluates
|
||||
// such duration expression, setting OriginalOffset to 30m.
|
||||
type durationVisitor struct {
|
||||
step time.Duration
|
||||
}
|
||||
|
||||
// Visit finds any duration expressions in AST Nodes and modifies the Node to
|
||||
// store the concrete value. Note that parser.Walk does NOT traverse the
|
||||
// duration expressions such as OriginalOffsetExpr so we make our own recursive
|
||||
// call on those to evaluate the result.
|
||||
func (v *durationVisitor) Visit(node parser.Node, _ []parser.Node) (parser.Visitor, error) {
|
||||
switch n := node.(type) {
|
||||
case *parser.VectorSelector:
|
||||
if n.OriginalOffsetExpr != nil {
|
||||
duration, err := calculateDuration(n.OriginalOffsetExpr, true)
|
||||
duration, err := v.calculateDuration(n.OriginalOffsetExpr, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -36,7 +47,7 @@ func (v *durationVisitor) Visit(node parser.Node, _ []parser.Node) (parser.Visit
|
||||
}
|
||||
case *parser.MatrixSelector:
|
||||
if n.RangeExpr != nil {
|
||||
duration, err := calculateDuration(n.RangeExpr, false)
|
||||
duration, err := v.calculateDuration(n.RangeExpr, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -44,21 +55,21 @@ func (v *durationVisitor) Visit(node parser.Node, _ []parser.Node) (parser.Visit
|
||||
}
|
||||
case *parser.SubqueryExpr:
|
||||
if n.OriginalOffsetExpr != nil {
|
||||
duration, err := calculateDuration(n.OriginalOffsetExpr, true)
|
||||
duration, err := v.calculateDuration(n.OriginalOffsetExpr, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n.OriginalOffset = duration
|
||||
}
|
||||
if n.StepExpr != nil {
|
||||
duration, err := calculateDuration(n.StepExpr, false)
|
||||
duration, err := v.calculateDuration(n.StepExpr, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n.Step = duration
|
||||
}
|
||||
if n.RangeExpr != nil {
|
||||
duration, err := calculateDuration(n.RangeExpr, false)
|
||||
duration, err := v.calculateDuration(n.RangeExpr, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -68,9 +79,10 @@ func (v *durationVisitor) Visit(node parser.Node, _ []parser.Node) (parser.Visit
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// calculateDuration computes the duration from a duration expression.
|
||||
func calculateDuration(expr parser.Expr, allowedNegative bool) (time.Duration, error) {
|
||||
duration, err := evaluateDurationExpr(expr)
|
||||
// calculateDuration returns the float value of a duration expression as
|
||||
// time.Duration or an error if the duration is invalid.
|
||||
func (v *durationVisitor) calculateDuration(expr parser.Expr, allowedNegative bool) (time.Duration, error) {
|
||||
duration, err := v.evaluateDurationExpr(expr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -84,7 +96,7 @@ func calculateDuration(expr parser.Expr, allowedNegative bool) (time.Duration, e
|
||||
}
|
||||
|
||||
// evaluateDurationExpr recursively evaluates a duration expression to a float64 value.
|
||||
func evaluateDurationExpr(expr parser.Expr) (float64, error) {
|
||||
func (v *durationVisitor) evaluateDurationExpr(expr parser.Expr) (float64, error) {
|
||||
switch n := expr.(type) {
|
||||
case *parser.NumberLiteral:
|
||||
return n.Val, nil
|
||||
@ -93,19 +105,31 @@ func evaluateDurationExpr(expr parser.Expr) (float64, error) {
|
||||
var err error
|
||||
|
||||
if n.LHS != nil {
|
||||
lhs, err = evaluateDurationExpr(n.LHS)
|
||||
lhs, err = v.evaluateDurationExpr(n.LHS)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
rhs, err = evaluateDurationExpr(n.RHS)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
if n.RHS != nil {
|
||||
rhs, err = v.evaluateDurationExpr(n.RHS)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
switch n.Op {
|
||||
case parser.STEP:
|
||||
return float64(v.step.Seconds()), nil
|
||||
case parser.MIN:
|
||||
return math.Min(lhs, rhs), nil
|
||||
case parser.MAX:
|
||||
return math.Max(lhs, rhs), nil
|
||||
case parser.ADD:
|
||||
if n.LHS == nil {
|
||||
// Unary positive duration expression.
|
||||
return rhs, nil
|
||||
}
|
||||
return lhs + rhs, nil
|
||||
case parser.SUB:
|
||||
if n.LHS == nil {
|
||||
|
@ -195,6 +195,24 @@ func TestCalculateDuration(t *testing.T) {
|
||||
expected: -5 * time.Second,
|
||||
allowedNegative: true,
|
||||
},
|
||||
{
|
||||
name: "step",
|
||||
expr: &parser.DurationExpr{
|
||||
Op: parser.STEP,
|
||||
},
|
||||
expected: 1 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "step multiplication",
|
||||
expr: &parser.DurationExpr{
|
||||
LHS: &parser.DurationExpr{
|
||||
Op: parser.STEP,
|
||||
},
|
||||
RHS: &parser.NumberLiteral{Val: 3},
|
||||
Op: parser.MUL,
|
||||
},
|
||||
expected: 3 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "division by zero",
|
||||
expr: &parser.DurationExpr{
|
||||
@ -225,7 +243,8 @@ func TestCalculateDuration(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := calculateDuration(tt.expr, tt.allowedNegative)
|
||||
v := &durationVisitor{step: 1 * time.Second}
|
||||
result, err := v.calculateDuration(tt.expr, tt.allowedNegative)
|
||||
if tt.errorMessage != "" {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tt.errorMessage)
|
||||
|
@ -86,11 +86,6 @@ type engineMetrics struct {
|
||||
querySamples prometheus.Counter
|
||||
}
|
||||
|
||||
// convertibleToInt64 returns true if v does not over-/underflow an int64.
|
||||
func convertibleToInt64(v float64) bool {
|
||||
return v <= maxInt64 && v >= minInt64
|
||||
}
|
||||
|
||||
type (
|
||||
// ErrQueryTimeout is returned if a query timed out during processing.
|
||||
ErrQueryTimeout string
|
||||
@ -134,7 +129,7 @@ type QueryLogger interface {
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// A Query is derived from an a raw query string and can be run against an engine
|
||||
// A Query is derived from a raw query string and can be run against an engine
|
||||
// it is associated with.
|
||||
type Query interface {
|
||||
// Exec processes the query. Can only be called once.
|
||||
@ -481,7 +476,7 @@ func (ng *Engine) SetQueryLogger(l QueryLogger) {
|
||||
|
||||
// NewInstantQuery returns an evaluation query for the given expression at the given time.
|
||||
func (ng *Engine) NewInstantQuery(ctx context.Context, q storage.Queryable, opts QueryOpts, qs string, ts time.Time) (Query, error) {
|
||||
pExpr, qry := ng.newQuery(q, qs, opts, ts, ts, 0)
|
||||
pExpr, qry := ng.newQuery(q, qs, opts, ts, ts, 0*time.Second)
|
||||
finishQueue, err := ng.queueActive(ctx, qry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -494,7 +489,7 @@ func (ng *Engine) NewInstantQuery(ctx context.Context, q storage.Queryable, opts
|
||||
if err := ng.validateOpts(expr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*pExpr, err = PreprocessExpr(expr, ts, ts)
|
||||
*pExpr, err = PreprocessExpr(expr, ts, ts, 0)
|
||||
|
||||
return qry, err
|
||||
}
|
||||
@ -518,7 +513,7 @@ func (ng *Engine) NewRangeQuery(ctx context.Context, q storage.Queryable, opts Q
|
||||
if expr.Type() != parser.ValueTypeVector && expr.Type() != parser.ValueTypeScalar {
|
||||
return nil, fmt.Errorf("invalid expression type %q for range query, must be Scalar or instant Vector", parser.DocumentedType(expr.Type()))
|
||||
}
|
||||
*pExpr, err = PreprocessExpr(expr, start, end)
|
||||
*pExpr, err = PreprocessExpr(expr, start, end, interval)
|
||||
|
||||
return qry, err
|
||||
}
|
||||
@ -1433,6 +1428,15 @@ func (ev *evaluator) rangeEvalAgg(ctx context.Context, aggExpr *parser.Aggregate
|
||||
if params.Max() < 1 {
|
||||
return nil, annos
|
||||
}
|
||||
if params.HasAnyNaN() {
|
||||
ev.errorf("Parameter value is NaN")
|
||||
}
|
||||
if fParam := params.Min(); fParam <= minInt64 {
|
||||
ev.errorf("Scalar value %v underflows int64", fParam)
|
||||
}
|
||||
if fParam := params.Max(); fParam >= maxInt64 {
|
||||
ev.errorf("Scalar value %v overflows int64", fParam)
|
||||
}
|
||||
seriess = make(map[uint64]Series, len(inputMatrix))
|
||||
|
||||
case parser.LIMIT_RATIO:
|
||||
@ -1440,6 +1444,9 @@ func (ev *evaluator) rangeEvalAgg(ctx context.Context, aggExpr *parser.Aggregate
|
||||
if params.Max() == 0 && params.Min() == 0 {
|
||||
return nil, annos
|
||||
}
|
||||
if params.HasAnyNaN() {
|
||||
ev.errorf("Ratio value is NaN")
|
||||
}
|
||||
if params.Max() > 1.0 {
|
||||
annos.Add(annotations.NewInvalidRatioWarning(params.Max(), 1.0, aggExpr.Param.PositionRange()))
|
||||
}
|
||||
@ -3325,9 +3332,6 @@ seriesLoop:
|
||||
var r float64
|
||||
switch op {
|
||||
case parser.TOPK, parser.BOTTOMK, parser.LIMITK:
|
||||
if !convertibleToInt64(fParam) {
|
||||
ev.errorf("Scalar value %v overflows int64", fParam)
|
||||
}
|
||||
k = int64(fParam)
|
||||
if k > int64(len(inputMatrix)) {
|
||||
k = int64(len(inputMatrix))
|
||||
@ -3339,9 +3343,6 @@ seriesLoop:
|
||||
return nil, annos
|
||||
}
|
||||
case parser.LIMIT_RATIO:
|
||||
if math.IsNaN(fParam) {
|
||||
ev.errorf("Ratio value %v is NaN", fParam)
|
||||
}
|
||||
switch {
|
||||
case fParam == 0:
|
||||
if enh.Ts != ev.endTimestamp {
|
||||
@ -3730,10 +3731,10 @@ func unwrapStepInvariantExpr(e parser.Expr) parser.Expr {
|
||||
// PreprocessExpr wraps all possible step invariant parts of the given expression with
|
||||
// StepInvariantExpr. It also resolves the preprocessors and evaluates duration expressions
|
||||
// into their numeric values.
|
||||
func PreprocessExpr(expr parser.Expr, start, end time.Time) (parser.Expr, error) {
|
||||
func PreprocessExpr(expr parser.Expr, start, end time.Time, step time.Duration) (parser.Expr, error) {
|
||||
detectHistogramStatsDecoding(expr)
|
||||
|
||||
if err := parser.Walk(&durationVisitor{}, expr, nil); err != nil {
|
||||
if err := parser.Walk(&durationVisitor{step: step}, expr, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -3088,7 +3088,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) {
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
expr, err := parser.ParseExpr(test.input)
|
||||
require.NoError(t, err)
|
||||
expr, err = promql.PreprocessExpr(expr, startTime, endTime)
|
||||
expr, err = promql.PreprocessExpr(expr, startTime, endTime, 0)
|
||||
require.NoError(t, err)
|
||||
if test.outputTest {
|
||||
require.Equal(t, test.input, expr.String(), "error on input '%s'", test.input)
|
||||
|
@ -144,32 +144,37 @@ func extrapolatedRate(vals []parser.Value, args parser.Expressions, enh *EvalNod
|
||||
// (which is our guess for where the series actually starts or ends).
|
||||
|
||||
extrapolationThreshold := averageDurationBetweenSamples * 1.1
|
||||
extrapolateToInterval := sampledInterval
|
||||
|
||||
if durationToStart >= extrapolationThreshold {
|
||||
durationToStart = averageDurationBetweenSamples / 2
|
||||
}
|
||||
if isCounter && resultFloat > 0 && len(samples.Floats) > 0 && samples.Floats[0].F >= 0 {
|
||||
if isCounter {
|
||||
// Counters cannot be negative. If we have any slope at all
|
||||
// (i.e. resultFloat went up), we can extrapolate the zero point
|
||||
// of the counter. If the duration to the zero point is shorter
|
||||
// than the durationToStart, we take the zero point as the start
|
||||
// of the series, thereby avoiding extrapolation to negative
|
||||
// counter values.
|
||||
// TODO(beorn7): Do this for histograms, too.
|
||||
durationToZero := sampledInterval * (samples.Floats[0].F / resultFloat)
|
||||
durationToZero := durationToStart
|
||||
if resultFloat > 0 &&
|
||||
len(samples.Floats) > 0 &&
|
||||
samples.Floats[0].F >= 0 {
|
||||
durationToZero = sampledInterval * (samples.Floats[0].F / resultFloat)
|
||||
} else if resultHistogram != nil &&
|
||||
resultHistogram.Count > 0 &&
|
||||
len(samples.Histograms) > 0 &&
|
||||
samples.Histograms[0].H.Count >= 0 {
|
||||
durationToZero = sampledInterval * (samples.Histograms[0].H.Count / resultHistogram.Count)
|
||||
}
|
||||
if durationToZero < durationToStart {
|
||||
durationToStart = durationToZero
|
||||
}
|
||||
}
|
||||
extrapolateToInterval += durationToStart
|
||||
|
||||
if durationToEnd >= extrapolationThreshold {
|
||||
durationToEnd = averageDurationBetweenSamples / 2
|
||||
}
|
||||
extrapolateToInterval += durationToEnd
|
||||
|
||||
factor := extrapolateToInterval / sampledInterval
|
||||
factor := (sampledInterval + durationToStart + durationToEnd) / sampledInterval
|
||||
if isRate {
|
||||
factor /= ms.Range.Seconds()
|
||||
}
|
||||
|
@ -116,7 +116,8 @@ type DurationExpr struct {
|
||||
LHS, RHS Expr // The operands on the respective sides of the operator.
|
||||
Wrapped bool // Set when the duration is wrapped in parentheses.
|
||||
|
||||
StartPos posrange.Pos // For unary operations, the position of the operator.
|
||||
StartPos posrange.Pos // For unary operations and step(), the start position of the operator.
|
||||
EndPos posrange.Pos // For step(), the end position of the operator.
|
||||
}
|
||||
|
||||
// Call represents a function call.
|
||||
@ -455,6 +456,18 @@ func (e *BinaryExpr) PositionRange() posrange.PositionRange {
|
||||
}
|
||||
|
||||
func (e *DurationExpr) PositionRange() posrange.PositionRange {
|
||||
if e.Op == STEP {
|
||||
return posrange.PositionRange{
|
||||
Start: e.StartPos,
|
||||
End: e.EndPos,
|
||||
}
|
||||
}
|
||||
if e.RHS == nil {
|
||||
return posrange.PositionRange{
|
||||
Start: e.StartPos,
|
||||
End: e.RHS.PositionRange().End,
|
||||
}
|
||||
}
|
||||
if e.LHS == nil {
|
||||
return posrange.PositionRange{
|
||||
Start: e.StartPos,
|
||||
|
@ -150,6 +150,7 @@ WITHOUT
|
||||
%token <item>
|
||||
START
|
||||
END
|
||||
STEP
|
||||
%token preprocessorEnd
|
||||
|
||||
// Counter reset hints.
|
||||
@ -174,7 +175,7 @@ START_METRIC_SELECTOR
|
||||
// Type definitions for grammar rules.
|
||||
%type <matchers> label_match_list
|
||||
%type <matcher> label_matcher
|
||||
%type <item> aggregate_op grouping_label match_op maybe_label metric_identifier unary_op at_modifier_preprocessors string_identifier counter_reset_hint
|
||||
%type <item> aggregate_op grouping_label match_op maybe_label metric_identifier unary_op at_modifier_preprocessors string_identifier counter_reset_hint min_max
|
||||
%type <labels> label_set metric
|
||||
%type <lblList> label_set_list
|
||||
%type <label> label_set_item
|
||||
@ -478,7 +479,7 @@ offset_expr: expr OFFSET offset_duration_expr
|
||||
$$ = $1
|
||||
}
|
||||
| expr OFFSET error
|
||||
{ yylex.(*parser).unexpected("offset", "number or duration"); $$ = $1 }
|
||||
{ yylex.(*parser).unexpected("offset", "number, duration, or step()"); $$ = $1 }
|
||||
;
|
||||
|
||||
/*
|
||||
@ -574,11 +575,11 @@ subquery_expr : expr LEFT_BRACKET positive_duration_expr COLON positive_durati
|
||||
| expr LEFT_BRACKET positive_duration_expr COLON positive_duration_expr error
|
||||
{ yylex.(*parser).unexpected("subquery selector", "\"]\""); $$ = $1 }
|
||||
| expr LEFT_BRACKET positive_duration_expr COLON error
|
||||
{ yylex.(*parser).unexpected("subquery selector", "number or duration or \"]\""); $$ = $1 }
|
||||
{ yylex.(*parser).unexpected("subquery selector", "number, duration, or step() or \"]\""); $$ = $1 }
|
||||
| expr LEFT_BRACKET positive_duration_expr error
|
||||
{ yylex.(*parser).unexpected("subquery or range", "\":\" or \"]\""); $$ = $1 }
|
||||
| expr LEFT_BRACKET error
|
||||
{ yylex.(*parser).unexpected("subquery selector", "number or duration"); $$ = $1 }
|
||||
{ yylex.(*parser).unexpected("subquery or range selector", "number, duration, or step()"); $$ = $1 }
|
||||
;
|
||||
|
||||
/*
|
||||
@ -695,7 +696,7 @@ metric : metric_identifier label_set
|
||||
;
|
||||
|
||||
|
||||
metric_identifier: AVG | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | IDENTIFIER | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | QUANTILE | STDDEV | STDVAR | SUM | TOPK | WITHOUT | START | END | LIMITK | LIMIT_RATIO;
|
||||
metric_identifier: AVG | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | IDENTIFIER | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | QUANTILE | STDDEV | STDVAR | SUM | TOPK | WITHOUT | START | END | LIMITK | LIMIT_RATIO | STEP;
|
||||
|
||||
label_set : LEFT_BRACE label_set_list RIGHT_BRACE
|
||||
{ $$ = labels.New($2...) }
|
||||
@ -952,7 +953,7 @@ counter_reset_hint : UNKNOWN_COUNTER_RESET | COUNTER_RESET | NOT_COUNTER_RESET |
|
||||
aggregate_op : AVG | BOTTOMK | COUNT | COUNT_VALUES | GROUP | MAX | MIN | QUANTILE | STDDEV | STDVAR | SUM | TOPK | LIMITK | LIMIT_RATIO;
|
||||
|
||||
// Inside of grouping options label names can be recognized as keywords by the lexer. This is a list of keywords that could also be a label name.
|
||||
maybe_label : AVG | BOOL | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | GROUP_LEFT | GROUP_RIGHT | IDENTIFIER | IGNORING | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | ON | QUANTILE | STDDEV | STDVAR | SUM | TOPK | START | END | ATAN2 | LIMITK | LIMIT_RATIO;
|
||||
maybe_label : AVG | BOOL | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | GROUP_LEFT | GROUP_RIGHT | IDENTIFIER | IGNORING | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | ON | QUANTILE | STDDEV | STDVAR | SUM | TOPK | START | END | ATAN2 | LIMITK | LIMIT_RATIO | STEP;
|
||||
|
||||
unary_op : ADD | SUB;
|
||||
|
||||
@ -1079,9 +1080,70 @@ offset_duration_expr : number_duration_literal
|
||||
nl.PosRange.Start = $1.Pos
|
||||
$$ = nl
|
||||
}
|
||||
| STEP LEFT_PAREN RIGHT_PAREN
|
||||
{
|
||||
$$ = &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: $1.PositionRange().Start,
|
||||
EndPos: $3.PositionRange().End,
|
||||
}
|
||||
}
|
||||
| unary_op STEP LEFT_PAREN RIGHT_PAREN
|
||||
{
|
||||
$$ = &DurationExpr{
|
||||
Op: $1.Typ,
|
||||
RHS: &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: $2.PositionRange().Start,
|
||||
EndPos: $4.PositionRange().End,
|
||||
},
|
||||
StartPos: $1.Pos,
|
||||
}
|
||||
}
|
||||
| min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
|
||||
{
|
||||
$$ = &DurationExpr{
|
||||
Op: $1.Typ,
|
||||
StartPos: $1.PositionRange().Start,
|
||||
EndPos: $6.PositionRange().End,
|
||||
LHS: $3.(Expr),
|
||||
RHS: $5.(Expr),
|
||||
}
|
||||
}
|
||||
| unary_op min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
|
||||
{
|
||||
$$ = &DurationExpr{
|
||||
Op: $1.Typ,
|
||||
StartPos: $1.Pos,
|
||||
EndPos: $6.PositionRange().End,
|
||||
RHS: &DurationExpr{
|
||||
Op: $2.Typ,
|
||||
StartPos: $2.PositionRange().Start,
|
||||
EndPos: $6.PositionRange().End,
|
||||
LHS: $4.(Expr),
|
||||
RHS: $6.(Expr),
|
||||
},
|
||||
}
|
||||
}
|
||||
| unary_op LEFT_PAREN duration_expr RIGHT_PAREN %prec MUL
|
||||
{
|
||||
de := $3.(*DurationExpr)
|
||||
de.Wrapped = true
|
||||
if $1.Typ == SUB {
|
||||
$$ = &DurationExpr{
|
||||
Op: SUB,
|
||||
RHS: de,
|
||||
StartPos: $1.Pos,
|
||||
}
|
||||
break
|
||||
}
|
||||
$$ = $3
|
||||
}
|
||||
| duration_expr
|
||||
;
|
||||
|
||||
min_max: MIN | MAX ;
|
||||
|
||||
duration_expr : number_duration_literal
|
||||
{
|
||||
nl := $1.(*NumberLiteral)
|
||||
@ -1164,6 +1226,24 @@ duration_expr : number_duration_literal
|
||||
yylex.(*parser).experimentalDurationExpr($1.(Expr))
|
||||
$$ = &DurationExpr{Op: POW, LHS: $1.(Expr), RHS: $3.(Expr)}
|
||||
}
|
||||
| STEP LEFT_PAREN RIGHT_PAREN
|
||||
{
|
||||
$$ = &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: $1.PositionRange().Start,
|
||||
EndPos: $3.PositionRange().End,
|
||||
}
|
||||
}
|
||||
| min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
|
||||
{
|
||||
$$ = &DurationExpr{
|
||||
Op: $1.Typ,
|
||||
StartPos: $1.PositionRange().Start,
|
||||
EndPos: $6.PositionRange().End,
|
||||
LHS: $3.(Expr),
|
||||
RHS: $5.(Expr),
|
||||
}
|
||||
}
|
||||
| paren_duration_expr
|
||||
;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -140,6 +140,7 @@ var key = map[string]ItemType{
|
||||
// Preprocessors.
|
||||
"start": START,
|
||||
"end": END,
|
||||
"step": STEP,
|
||||
}
|
||||
|
||||
var histogramDesc = map[string]ItemType{
|
||||
@ -462,11 +463,20 @@ func lexStatements(l *Lexer) stateFn {
|
||||
l.backup()
|
||||
return lexKeywordOrIdentifier
|
||||
}
|
||||
if l.gotColon {
|
||||
return l.errorf("unexpected colon %q", r)
|
||||
switch r {
|
||||
case ':':
|
||||
if l.gotColon {
|
||||
return l.errorf("unexpected colon %q", r)
|
||||
}
|
||||
l.emit(COLON)
|
||||
l.gotColon = true
|
||||
return lexStatements
|
||||
case 's', 'S', 'm', 'M':
|
||||
if l.scanDurationKeyword() {
|
||||
return lexStatements
|
||||
}
|
||||
}
|
||||
l.emit(COLON)
|
||||
l.gotColon = true
|
||||
return l.errorf("unexpected character: %q, expected %q", r, ':')
|
||||
case r == '(':
|
||||
l.emit(LEFT_PAREN)
|
||||
l.parenDepth++
|
||||
@ -889,6 +899,32 @@ func lexNumber(l *Lexer) stateFn {
|
||||
return lexStatements
|
||||
}
|
||||
|
||||
func (l *Lexer) scanDurationKeyword() bool {
|
||||
for {
|
||||
switch r := l.next(); {
|
||||
case isAlpha(r):
|
||||
// absorb.
|
||||
default:
|
||||
l.backup()
|
||||
word := l.input[l.start:l.pos]
|
||||
kw := strings.ToLower(word)
|
||||
switch kw {
|
||||
case "step":
|
||||
l.emit(STEP)
|
||||
return true
|
||||
case "min":
|
||||
l.emit(MIN)
|
||||
return true
|
||||
case "max":
|
||||
l.emit(MAX)
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lexNumberOrDuration scans a number or a duration Item.
|
||||
func lexNumberOrDuration(l *Lexer) stateFn {
|
||||
if l.scanNumber() {
|
||||
@ -1133,6 +1169,14 @@ func lexDurationExpr(l *Lexer) stateFn {
|
||||
case r == '^':
|
||||
l.emit(POW)
|
||||
return lexDurationExpr
|
||||
case r == ',':
|
||||
l.emit(COMMA)
|
||||
return lexDurationExpr
|
||||
case r == 's' || r == 'S' || r == 'm' || r == 'M':
|
||||
if l.scanDurationKeyword() {
|
||||
return lexDurationExpr
|
||||
}
|
||||
return l.errorf("unexpected character in duration expression: %q", r)
|
||||
case isDigit(r) || (r == '.' && isDigit(l.peek())):
|
||||
l.backup()
|
||||
l.gotDuration = true
|
||||
|
@ -614,6 +614,43 @@ var testExpr = []struct {
|
||||
fail: true,
|
||||
errMsg: "1:11: parse error: unexpected <ignoring>",
|
||||
},
|
||||
// Vector selectors.
|
||||
{
|
||||
input: `offset{step="1s"}[5m]`,
|
||||
expected: &MatrixSelector{
|
||||
VectorSelector: &VectorSelector{
|
||||
Name: "offset",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, "step", "1s"),
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "offset"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 0,
|
||||
End: 17,
|
||||
},
|
||||
},
|
||||
Range: 5 * time.Minute,
|
||||
EndPos: 21,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `step{offset="1s"}[5m]`,
|
||||
expected: &MatrixSelector{
|
||||
VectorSelector: &VectorSelector{
|
||||
Name: "step",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, "offset", "1s"),
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "step"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 0,
|
||||
End: 17,
|
||||
},
|
||||
},
|
||||
Range: 5 * time.Minute,
|
||||
EndPos: 21,
|
||||
},
|
||||
},
|
||||
// Vector binary operations.
|
||||
{
|
||||
input: "foo * bar",
|
||||
@ -2388,7 +2425,7 @@ var testExpr = []struct {
|
||||
{
|
||||
input: `foo[]`,
|
||||
fail: true,
|
||||
errMsg: "unexpected \"]\" in subquery selector, expected number or duration",
|
||||
errMsg: "unexpected \"]\" in subquery or range selector, expected number, duration, or step()",
|
||||
},
|
||||
{
|
||||
input: `foo[-1]`,
|
||||
@ -2403,7 +2440,7 @@ var testExpr = []struct {
|
||||
{
|
||||
input: `some_metric[5m] OFFSET`,
|
||||
fail: true,
|
||||
errMsg: "unexpected end of input in offset, expected number or duration",
|
||||
errMsg: "1:23: parse error: unexpected end of input in offset, expected number, duration, or step()",
|
||||
},
|
||||
{
|
||||
input: `some_metric OFFSET 1m[5m]`,
|
||||
@ -4131,6 +4168,242 @@ var testExpr = []struct {
|
||||
EndPos: 13,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo[step()]`,
|
||||
expected: &MatrixSelector{
|
||||
VectorSelector: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 0,
|
||||
End: 3,
|
||||
},
|
||||
},
|
||||
RangeExpr: &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: 4,
|
||||
EndPos: 10,
|
||||
},
|
||||
EndPos: 11,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo[ - step ( ) ]`,
|
||||
expected: &MatrixSelector{
|
||||
VectorSelector: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 0,
|
||||
End: 3,
|
||||
},
|
||||
},
|
||||
RangeExpr: &DurationExpr{
|
||||
Op: SUB,
|
||||
StartPos: 6,
|
||||
RHS: &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: 9,
|
||||
EndPos: 19,
|
||||
},
|
||||
},
|
||||
EndPos: 22,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo[ step ( ) ]`,
|
||||
expected: &MatrixSelector{
|
||||
VectorSelector: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 0,
|
||||
End: 3,
|
||||
},
|
||||
},
|
||||
RangeExpr: &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: 7,
|
||||
EndPos: 17,
|
||||
},
|
||||
EndPos: 20,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo[-step()]`,
|
||||
expected: &MatrixSelector{
|
||||
VectorSelector: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 0,
|
||||
End: 3,
|
||||
},
|
||||
},
|
||||
RangeExpr: &DurationExpr{
|
||||
Op: SUB,
|
||||
StartPos: 4,
|
||||
RHS: &DurationExpr{Op: STEP, StartPos: 5, EndPos: 11},
|
||||
},
|
||||
EndPos: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo offset step()`,
|
||||
expected: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 0,
|
||||
End: 17,
|
||||
},
|
||||
OriginalOffsetExpr: &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: 11,
|
||||
EndPos: 17,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo offset -step()`,
|
||||
expected: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 0,
|
||||
End: 18,
|
||||
},
|
||||
OriginalOffsetExpr: &DurationExpr{
|
||||
Op: SUB,
|
||||
StartPos: 11,
|
||||
RHS: &DurationExpr{Op: STEP, StartPos: 12, EndPos: 18},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo[max(step(),5s)]`,
|
||||
expected: &MatrixSelector{
|
||||
VectorSelector: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 0,
|
||||
End: 3,
|
||||
},
|
||||
},
|
||||
RangeExpr: &DurationExpr{
|
||||
Op: MAX,
|
||||
LHS: &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: 8,
|
||||
EndPos: 14,
|
||||
},
|
||||
RHS: &NumberLiteral{
|
||||
Val: 5,
|
||||
Duration: true,
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 15,
|
||||
End: 17,
|
||||
},
|
||||
},
|
||||
StartPos: 4,
|
||||
EndPos: 18,
|
||||
},
|
||||
EndPos: 19,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo offset max(step(),5s)`,
|
||||
expected: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 0,
|
||||
End: 25,
|
||||
},
|
||||
OriginalOffsetExpr: &DurationExpr{
|
||||
Op: MAX,
|
||||
LHS: &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: 15,
|
||||
EndPos: 21,
|
||||
},
|
||||
RHS: &NumberLiteral{
|
||||
Val: 5,
|
||||
Duration: true,
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 22,
|
||||
End: 24,
|
||||
},
|
||||
},
|
||||
StartPos: 11,
|
||||
EndPos: 25,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo offset -min(5s,step()+8s)`,
|
||||
expected: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
||||
},
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 0,
|
||||
End: 29,
|
||||
},
|
||||
OriginalOffsetExpr: &DurationExpr{
|
||||
Op: SUB,
|
||||
RHS: &DurationExpr{
|
||||
Op: MIN,
|
||||
LHS: &NumberLiteral{
|
||||
Val: 5,
|
||||
Duration: true,
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 16,
|
||||
End: 18,
|
||||
},
|
||||
},
|
||||
RHS: &DurationExpr{
|
||||
Op: ADD,
|
||||
LHS: &DurationExpr{
|
||||
Op: STEP,
|
||||
StartPos: 19,
|
||||
EndPos: 25,
|
||||
},
|
||||
RHS: &NumberLiteral{
|
||||
Val: 8,
|
||||
Duration: true,
|
||||
PosRange: posrange.PositionRange{
|
||||
Start: 26,
|
||||
End: 28,
|
||||
},
|
||||
},
|
||||
},
|
||||
StartPos: 12,
|
||||
EndPos: 28,
|
||||
},
|
||||
StartPos: 11,
|
||||
EndPos: 28,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo[4s+4s:1s*2] offset (5s-8)`,
|
||||
expected: &SubqueryExpr{
|
||||
@ -4453,6 +4726,16 @@ var testExpr = []struct {
|
||||
EndPos: 11,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `foo[step]`,
|
||||
fail: true,
|
||||
errMsg: `1:9: parse error: unexpected "]" in subquery or range selector, expected number, duration, or step()`,
|
||||
},
|
||||
{
|
||||
input: `foo[step()/0d]`,
|
||||
fail: true,
|
||||
errMsg: `division by zero`,
|
||||
},
|
||||
{
|
||||
input: `foo[5s/0d]`,
|
||||
fail: true,
|
||||
@ -4545,6 +4828,16 @@ var testExpr = []struct {
|
||||
fail: true,
|
||||
errMsg: "unclosed left parenthesis",
|
||||
},
|
||||
{
|
||||
input: "foo[5s x 5s]",
|
||||
fail: true,
|
||||
errMsg: "unexpected character: 'x', expected ':'",
|
||||
},
|
||||
{
|
||||
input: "foo[5s s 5s]",
|
||||
fail: true,
|
||||
errMsg: "unexpected character: 's', expected ':'",
|
||||
},
|
||||
}
|
||||
|
||||
func makeInt64Pointer(val int64) *int64 {
|
||||
|
@ -84,8 +84,8 @@ func (e *DurationExpr) Pretty(int) string {
|
||||
fmt.Println("e.LHS", e.LHS)
|
||||
fmt.Println("e.RHS", e.RHS)
|
||||
if e.LHS == nil {
|
||||
// This is a unary negative duration expression.
|
||||
s = fmt.Sprintf("%s %s", e.Op, e.RHS.Pretty(0))
|
||||
// This is a unary duration expression.
|
||||
s = fmt.Sprintf("%s%s", e.Op, e.RHS.Pretty(0))
|
||||
} else {
|
||||
s = fmt.Sprintf("%s %s %s", e.LHS.Pretty(0), e.Op, e.RHS.Pretty(0))
|
||||
}
|
||||
|
@ -148,10 +148,25 @@ func (node *BinaryExpr) getMatchingStr() string {
|
||||
|
||||
func (node *DurationExpr) String() string {
|
||||
var expr string
|
||||
if node.LHS == nil {
|
||||
// This is a unary negative duration expression.
|
||||
expr = fmt.Sprintf("%s%s", node.Op, node.RHS)
|
||||
} else {
|
||||
switch {
|
||||
case node.Op == STEP:
|
||||
expr = "step()"
|
||||
case node.Op == MIN:
|
||||
expr = fmt.Sprintf("min(%s, %s)", node.LHS, node.RHS)
|
||||
case node.Op == MAX:
|
||||
expr = fmt.Sprintf("max(%s, %s)", node.LHS, node.RHS)
|
||||
case node.LHS == nil:
|
||||
// This is a unary duration expression.
|
||||
switch node.Op {
|
||||
case SUB:
|
||||
expr = fmt.Sprintf("%s%s", node.Op, node.RHS)
|
||||
case ADD:
|
||||
expr = node.RHS.String()
|
||||
default:
|
||||
// This should never happen.
|
||||
panic(fmt.Sprintf("unexpected unary duration expression: %s", node.Op))
|
||||
}
|
||||
default:
|
||||
expr = fmt.Sprintf("%s %s %s", node.LHS, node.Op, node.RHS)
|
||||
}
|
||||
if node.Wrapped {
|
||||
|
@ -22,6 +22,10 @@ import (
|
||||
)
|
||||
|
||||
func TestExprString(t *testing.T) {
|
||||
ExperimentalDurationExpr = true
|
||||
t.Cleanup(func() {
|
||||
ExperimentalDurationExpr = false
|
||||
})
|
||||
// A list of valid expressions that are expected to be
|
||||
// returned as out when calling String(). If out is empty the output
|
||||
// is expected to equal the input.
|
||||
@ -167,18 +171,66 @@ func TestExprString(t *testing.T) {
|
||||
in: "1048576",
|
||||
out: "1048576",
|
||||
},
|
||||
{
|
||||
in: "foo[step()]",
|
||||
},
|
||||
{
|
||||
in: "foo[-step()]",
|
||||
},
|
||||
{
|
||||
in: "foo[(step())]",
|
||||
},
|
||||
{
|
||||
in: "foo[-(step())]",
|
||||
},
|
||||
{
|
||||
in: "foo offset step()",
|
||||
},
|
||||
{
|
||||
in: "foo offset -step()",
|
||||
},
|
||||
{
|
||||
in: "foo offset (step())",
|
||||
},
|
||||
{
|
||||
in: "foo offset -(step())",
|
||||
},
|
||||
{
|
||||
in: "foo offset +(5*2)",
|
||||
out: "foo offset (5 * 2)",
|
||||
},
|
||||
{
|
||||
in: "foo offset +min(10s, 20s)",
|
||||
out: "foo offset min(10s, 20s)",
|
||||
},
|
||||
{
|
||||
in: "foo offset -min(10s, 20s)",
|
||||
},
|
||||
{
|
||||
in: "foo offset -min(10s, +max(step() ^ 2, 2))",
|
||||
out: "foo offset -min(10s, max(step() ^ 2, 2))",
|
||||
},
|
||||
{
|
||||
in: "foo[200-min(-step()^+step(),1)]",
|
||||
out: "foo[200 - min(-step() ^ step(), 1)]",
|
||||
},
|
||||
{
|
||||
in: "foo[200 - min(step() + 10s, -max(step() ^ 2, 3))]",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range inputs {
|
||||
expr, err := ParseExpr(test.in)
|
||||
require.NoError(t, err)
|
||||
t.Run(test.in, func(t *testing.T) {
|
||||
expr, err := ParseExpr(test.in)
|
||||
require.NoError(t, err)
|
||||
|
||||
exp := test.in
|
||||
if test.out != "" {
|
||||
exp = test.out
|
||||
}
|
||||
exp := test.in
|
||||
if test.out != "" {
|
||||
exp = test.out
|
||||
}
|
||||
|
||||
require.Equal(t, exp, expr.String())
|
||||
require.Equal(t, exp, expr.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
161
promql/promqltest/testdata/aggregators.test
vendored
161
promql/promqltest/testdata/aggregators.test
vendored
@ -232,30 +232,38 @@ load 5m
|
||||
http_requests_histogram{job="api-server", instance="3", group="canary"} {{schema:2 count:4 sum:10 buckets:[1 0 0 0 1 0 0 1 1]}}
|
||||
|
||||
eval instant at 0m max(http_requests)
|
||||
expect no_info
|
||||
{} 4
|
||||
|
||||
# The histogram is ignored here so the result doesn't change but it has an info annotation now.
|
||||
eval_info instant at 0m max({job="api-server"})
|
||||
eval instant at 0m max({job="api-server"})
|
||||
expect info
|
||||
{} 4
|
||||
|
||||
# The histogram is ignored here so there is no result but it has an info annotation now.
|
||||
eval_info instant at 0m max(http_requests_histogram)
|
||||
eval instant at 0m max(http_requests_histogram)
|
||||
expect info
|
||||
|
||||
eval instant at 0m min(http_requests)
|
||||
expect no_info
|
||||
{} 1
|
||||
|
||||
# The histogram is ignored here so the result doesn't change but it has an info annotation now.
|
||||
eval_info instant at 0m min({job="api-server"})
|
||||
eval instant at 0m min({job="api-server"})
|
||||
expect info
|
||||
{} 1
|
||||
|
||||
# The histogram is ignored here so there is no result but it has an info annotation now.
|
||||
eval_info instant at 0m min(http_requests_histogram)
|
||||
eval instant at 0m min(http_requests_histogram)
|
||||
expect info
|
||||
|
||||
eval instant at 0m max by (group) (http_requests)
|
||||
expect no_info
|
||||
{group="production"} 2
|
||||
{group="canary"} 4
|
||||
|
||||
eval instant at 0m min by (group) (http_requests)
|
||||
expect no_info
|
||||
{group="production"} 1
|
||||
{group="canary"} 3
|
||||
|
||||
@ -276,26 +284,31 @@ load 5m
|
||||
http_requests_histogram{job="api-server", instance="3", group="production"} {{schema:0 sum:20 count:20}}x11
|
||||
foo 1+1x9 3
|
||||
|
||||
eval_ordered instant at 50m topk(3, http_requests)
|
||||
eval instant at 50m topk(3, http_requests)
|
||||
expect ordered
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
|
||||
eval_ordered instant at 50m topk((3), (http_requests))
|
||||
eval instant at 50m topk((3), (http_requests))
|
||||
expect ordered
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
|
||||
eval_ordered instant at 50m topk(5, http_requests{group="canary",job="app-server"})
|
||||
eval instant at 50m topk(5, http_requests{group="canary",job="app-server"})
|
||||
expect ordered
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
|
||||
eval_ordered instant at 50m bottomk(3, http_requests)
|
||||
eval instant at 50m bottomk(3, http_requests)
|
||||
expect ordered
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
|
||||
eval_ordered instant at 50m bottomk(5, http_requests{group="canary",job="app-server"})
|
||||
eval instant at 50m bottomk(5, http_requests{group="canary",job="app-server"})
|
||||
expect ordered
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
|
||||
@ -309,33 +322,39 @@ eval instant at 50m bottomk by (group) (2, http_requests)
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
|
||||
eval_ordered instant at 50m bottomk by (group) (2, http_requests{group="production"})
|
||||
eval instant at 50m bottomk by (group) (2, http_requests{group="production"})
|
||||
expect ordered
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
|
||||
# Test NaN is sorted away from the top/bottom.
|
||||
eval_ordered instant at 50m topk(3, http_requests{job="api-server",group="production"})
|
||||
eval instant at 50m topk(3, http_requests{job="api-server",group="production"})
|
||||
expect ordered
|
||||
http_requests{job="api-server", instance="1", group="production"} 200
|
||||
http_requests{job="api-server", instance="0", group="production"} 100
|
||||
http_requests{job="api-server", instance="2", group="production"} NaN
|
||||
|
||||
eval_ordered instant at 50m bottomk(3, http_requests{job="api-server",group="production"})
|
||||
eval instant at 50m bottomk(3, http_requests{job="api-server",group="production"})
|
||||
expect ordered
|
||||
http_requests{job="api-server", instance="0", group="production"} 100
|
||||
http_requests{job="api-server", instance="1", group="production"} 200
|
||||
http_requests{job="api-server", instance="2", group="production"} NaN
|
||||
|
||||
# Test topk and bottomk allocate min(k, input_vector) for results vector
|
||||
eval_ordered instant at 50m bottomk(9999999999, http_requests{job="app-server",group="canary"})
|
||||
eval instant at 50m bottomk(9999999999, http_requests{job="app-server",group="canary"})
|
||||
expect ordered
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
|
||||
eval_ordered instant at 50m topk(9999999999, http_requests{job="api-server",group="production"})
|
||||
eval instant at 50m topk(9999999999, http_requests{job="api-server",group="production"})
|
||||
expect ordered
|
||||
http_requests{job="api-server", instance="1", group="production"} 200
|
||||
http_requests{job="api-server", instance="0", group="production"} 100
|
||||
http_requests{job="api-server", instance="2", group="production"} NaN
|
||||
|
||||
# Bug #5276.
|
||||
eval_ordered instant at 50m topk(scalar(foo), http_requests)
|
||||
eval instant at 50m topk(scalar(foo), http_requests)
|
||||
expect ordered
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
@ -348,46 +367,67 @@ eval range from 0m to 50m step 5m count(bottomk(scalar(foo), http_requests))
|
||||
{} 1 2 3 4 5 6 7 8 9 9 3
|
||||
|
||||
# Tests for histogram: should ignore histograms.
|
||||
eval_info instant at 50m topk(100, http_requests_histogram)
|
||||
eval instant at 50m topk(100, http_requests_histogram)
|
||||
expect info
|
||||
#empty
|
||||
|
||||
eval_info range from 0 to 50m step 5m topk(100, http_requests_histogram)
|
||||
eval range from 0 to 50m step 5m topk(100, http_requests_histogram)
|
||||
expect info
|
||||
#empty
|
||||
|
||||
eval_info instant at 50m topk(1, {__name__=~"http_requests(_histogram)?"})
|
||||
eval instant at 50m topk(1, {__name__=~"http_requests(_histogram)?"})
|
||||
expect info
|
||||
{__name__="http_requests", group="canary", instance="1", job="app-server"} 800
|
||||
|
||||
eval_info instant at 50m count(topk(1000, {__name__=~"http_requests(_histogram)?"}))
|
||||
eval instant at 50m count(topk(1000, {__name__=~"http_requests(_histogram)?"}))
|
||||
expect info
|
||||
{} 9
|
||||
|
||||
eval_info range from 0 to 50m step 5m count(topk(1000, {__name__=~"http_requests(_histogram)?"}))
|
||||
eval range from 0 to 50m step 5m count(topk(1000, {__name__=~"http_requests(_histogram)?"}))
|
||||
expect info
|
||||
{} 9x10
|
||||
|
||||
eval_info instant at 50m topk by (instance) (1, {__name__=~"http_requests(_histogram)?"})
|
||||
eval instant at 50m topk by (instance) (1, {__name__=~"http_requests(_histogram)?"})
|
||||
expect info
|
||||
{__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)
|
||||
eval instant at 50m bottomk(100, http_requests_histogram)
|
||||
expect info
|
||||
#empty
|
||||
|
||||
eval_info range from 0 to 50m step 5m bottomk(100, http_requests_histogram)
|
||||
eval range from 0 to 50m step 5m bottomk(100, http_requests_histogram)
|
||||
expect info
|
||||
#empty
|
||||
|
||||
eval_info instant at 50m bottomk(1, {__name__=~"http_requests(_histogram)?"})
|
||||
eval instant at 50m bottomk(1, {__name__=~"http_requests(_histogram)?"})
|
||||
expect info
|
||||
{__name__="http_requests", group="production", instance="0", job="api-server"} 100
|
||||
|
||||
eval_info instant at 50m count(bottomk(1000, {__name__=~"http_requests(_histogram)?"}))
|
||||
eval instant at 50m count(bottomk(1000, {__name__=~"http_requests(_histogram)?"}))
|
||||
expect info
|
||||
{} 9
|
||||
|
||||
eval_info range from 0 to 50m step 5m count(bottomk(1000, {__name__=~"http_requests(_histogram)?"}))
|
||||
eval range from 0 to 50m step 5m count(bottomk(1000, {__name__=~"http_requests(_histogram)?"}))
|
||||
expect info
|
||||
{} 9x10
|
||||
|
||||
eval_info instant at 50m bottomk by (instance) (1, {__name__=~"http_requests(_histogram)?"})
|
||||
eval instant at 50m bottomk by (instance) (1, {__name__=~"http_requests(_histogram)?"})
|
||||
expect info
|
||||
{__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
|
||||
|
||||
eval instant at 50m topk(NaN, non_existent)
|
||||
expect fail msg: Parameter value is NaN
|
||||
|
||||
eval instant at 50m limitk(NaN, non_existent)
|
||||
expect fail msg: Parameter value is NaN
|
||||
|
||||
eval instant at 50m limit_ratio(NaN, non_existent)
|
||||
expect fail msg: Ratio value is NaN
|
||||
|
||||
clear
|
||||
|
||||
# Tests for count_values.
|
||||
@ -438,8 +478,8 @@ eval instant at 1m count_values by (job, group)("job", version)
|
||||
{job="{count:20, sum:10, [-2,-1):2, [-1,-0.5):1, [-0.001,0.001]:2, (0.5,1]:1, (1,2]:2}", group="canary"} 2
|
||||
|
||||
# Test an invalid label value.
|
||||
eval_fail instant at 0 count_values("a\xc5z", version)
|
||||
expected_fail_message invalid label name "a\xc5z"
|
||||
eval instant at 0 count_values("a\xc5z", version)
|
||||
expect fail msg:invalid label name "a\xc5z"
|
||||
|
||||
# Tests for quantile.
|
||||
clear
|
||||
@ -453,46 +493,67 @@ load 10s
|
||||
data{test="uneven samples",point="a"} 0
|
||||
data{test="uneven samples",point="b"} 1
|
||||
data{test="uneven samples",point="c"} 4
|
||||
data{test="NaN sample",point="a"} 0
|
||||
data{test="NaN sample",point="b"} 1
|
||||
data{test="NaN sample",point="c"} NaN
|
||||
data_histogram{test="histogram sample", point="c"} {{schema:2 count:4 sum:10 buckets:[1 0 0 0 1 0 0 1 1]}}
|
||||
foo 0 1 0 1 0 1 0.8
|
||||
|
||||
# 80th percentile.
|
||||
# The NaN sample is treated as the smallest possible value.
|
||||
eval instant at 1m quantile without(point)(0.8, data)
|
||||
expect no_info
|
||||
{test="two samples"} 0.8
|
||||
{test="three samples"} 1.6
|
||||
{test="uneven samples"} 2.8
|
||||
{test="NaN sample"} 0.6
|
||||
|
||||
# 20th percentile.
|
||||
# A quantile between NaN and 0 is interpolated as NaN.
|
||||
eval instant at 1m quantile without(point)(0.2, data)
|
||||
{test="two samples"} 0.2
|
||||
{test="three samples"} 0.4
|
||||
{test="uneven samples"} 0.4
|
||||
{test="NaN sample"} NaN
|
||||
|
||||
# The histogram is ignored here so the result doesn't change but it has an info annotation now.
|
||||
eval_info instant at 1m quantile without(point)(0.8, {__name__=~"data(_histogram)?"})
|
||||
eval instant at 1m quantile without(point)(0.8, {__name__=~"data(_histogram)?"})
|
||||
expect info
|
||||
{test="two samples"} 0.8
|
||||
{test="three samples"} 1.6
|
||||
{test="uneven samples"} 2.8
|
||||
{test="NaN sample"} 0.6
|
||||
|
||||
# The histogram is ignored here so there is no result but it has an info annotation now.
|
||||
eval_info instant at 1m quantile(0.8, data_histogram)
|
||||
eval instant at 1m quantile(0.8, data_histogram)
|
||||
expect info
|
||||
|
||||
# Bug #5276.
|
||||
eval instant at 1m quantile without(point)(scalar(foo), data)
|
||||
{test="two samples"} 0.8
|
||||
{test="three samples"} 1.6
|
||||
{test="uneven samples"} 2.8
|
||||
|
||||
{test="NaN sample"} 0.6
|
||||
|
||||
eval instant at 1m quantile without(point)((scalar(foo)), data)
|
||||
{test="two samples"} 0.8
|
||||
{test="three samples"} 1.6
|
||||
{test="uneven samples"} 2.8
|
||||
{test="NaN sample"} 0.6
|
||||
|
||||
eval instant at 1m quantile without(point)(NaN, data)
|
||||
expect warn msg: PromQL warning: quantile value should be between 0 and 1, got NaN
|
||||
{test="two samples"} NaN
|
||||
{test="three samples"} NaN
|
||||
{test="uneven samples"} NaN
|
||||
{test="two samples"} NaN
|
||||
{test="three samples"} NaN
|
||||
{test="uneven samples"} NaN
|
||||
{test="NaN sample"} NaN
|
||||
|
||||
# Bug #15971.
|
||||
eval range from 0m to 1m step 10s quantile without(point) (scalar(foo), data)
|
||||
{test="two samples"} 0 1 0 1 0 1 0.8
|
||||
{test="three samples"} 0 2 0 2 0 2 1.6
|
||||
{test="uneven samples"} 0 4 0 4 0 4 2.8
|
||||
{test="NaN sample"} NaN 1 NaN 1 NaN 1 0.6
|
||||
|
||||
# Tests for group.
|
||||
clear
|
||||
@ -736,22 +797,28 @@ load 5m
|
||||
series{label="c"} {{schema:1 sum:15 count:10 buckets:[3 2 5 7 9]}}
|
||||
|
||||
# The histogram is ignored here so the result doesn't change but it has an info annotation now.
|
||||
eval_info instant at 0m stddev(series)
|
||||
eval instant at 0m stddev(series)
|
||||
expect info
|
||||
{} 0.5
|
||||
|
||||
eval_info instant at 0m stdvar(series)
|
||||
eval instant at 0m stdvar(series)
|
||||
expect info
|
||||
{} 0.25
|
||||
|
||||
# The histogram is ignored here so there is no result but it has an info annotation now.
|
||||
eval_info instant at 0m stddev({label="c"})
|
||||
eval instant at 0m stddev({label="c"})
|
||||
expect info
|
||||
|
||||
eval_info instant at 0m stdvar({label="c"})
|
||||
eval instant at 0m stdvar({label="c"})
|
||||
expect info
|
||||
|
||||
eval_info instant at 0m stddev by (label) (series)
|
||||
eval instant at 0m stddev by (label) (series)
|
||||
expect info
|
||||
{label="a"} 0
|
||||
{label="b"} 0
|
||||
|
||||
eval_info instant at 0m stdvar by (label) (series)
|
||||
eval instant at 0m stdvar by (label) (series)
|
||||
expect info
|
||||
{label="a"} 0
|
||||
{label="b"} 0
|
||||
|
||||
@ -762,17 +829,21 @@ load 5m
|
||||
series{label="b"} 1
|
||||
series{label="c"} 2
|
||||
|
||||
eval_info instant at 0m stddev(series)
|
||||
eval instant at 0m stddev(series)
|
||||
expect info
|
||||
{} 0.5
|
||||
|
||||
eval_info instant at 0m stdvar(series)
|
||||
eval instant at 0m stdvar(series)
|
||||
expect info
|
||||
{} 0.25
|
||||
|
||||
eval_info instant at 0m stddev by (label) (series)
|
||||
eval instant at 0m stddev by (label) (series)
|
||||
expect info
|
||||
{label="b"} 0
|
||||
{label="c"} 0
|
||||
|
||||
eval_info instant at 0m stdvar by (label) (series)
|
||||
eval instant at 0m stdvar by (label) (series)
|
||||
expect info
|
||||
{label="b"} 0
|
||||
{label="c"} 0
|
||||
|
||||
|
3
promql/promqltest/testdata/at_modifier.test
vendored
3
promql/promqltest/testdata/at_modifier.test
vendored
@ -90,8 +90,7 @@ 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
|
||||
|
||||
# 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", "", "")
|
||||
eval 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", "", "")
|
||||
|
3
promql/promqltest/testdata/collision.test
vendored
3
promql/promqltest/testdata/collision.test
vendored
@ -17,6 +17,7 @@ load 5m
|
||||
testmetric1{src="a",dst="b"} 0
|
||||
testmetric2{src="a",dst="b"} 1
|
||||
|
||||
eval_fail instant at 0m ceil({__name__=~'testmetric1|testmetric2'})
|
||||
eval instant at 0m ceil({__name__=~'testmetric1|testmetric2'})
|
||||
expect fail
|
||||
|
||||
clear
|
||||
|
@ -145,4 +145,84 @@ eval instant at 1000s metric1_total offset -4
|
||||
metric1_total{} 100
|
||||
|
||||
eval instant at 1000s metric1_total offset (-2 ^ 2)
|
||||
metric1_total{} 100
|
||||
metric1_total{} 100
|
||||
|
||||
clear
|
||||
|
||||
load 1s
|
||||
metric1_total 0+1x100
|
||||
|
||||
eval range from 50s to 60s step 10s count_over_time(metric1_total[step()])
|
||||
{} 10 10
|
||||
|
||||
eval range from 50s to 60s step 10s count_over_time(metric1_total[step()+1ms])
|
||||
{} 11 11
|
||||
|
||||
eval range from 50s to 60s step 10s count_over_time(metric1_total[(step())+1])
|
||||
{} 11 11
|
||||
|
||||
eval range from 50s to 60s step 10s count_over_time(metric1_total[1+(STep()-5)*2])
|
||||
{} 11 11
|
||||
|
||||
eval range from 50s to 60s step 5s count_over_time(metric1_total[step()+1])
|
||||
{} 6 6 6
|
||||
|
||||
eval range from 50s to 60s step 5s count_over_time(metric1_total[min(step()+1,1h)])
|
||||
{} 6 6 6
|
||||
|
||||
eval range from 50s to 60s step 5s count_over_time(metric1_total[max(min(step()+1,1h),1ms)])
|
||||
{} 6 6 6
|
||||
|
||||
eval range from 50s to 60s step 5s count_over_time(metric1_total[((max(min((step()+1),((1h))),1ms)))])
|
||||
{} 6 6 6
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset STEP()
|
||||
metric1_total{} 45 50 55
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset step()
|
||||
metric1_total{} 45 50 55
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset step()*0
|
||||
{} 0 0 0
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset (-step()*2)
|
||||
metric1_total{} 60 65 70
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset -step()*2
|
||||
{} 110 120 130
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset step()^0
|
||||
{} 1 1 1
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset (STEP()/10)
|
||||
metric1_total{} 49 54 59
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset (step())
|
||||
metric1_total{} 45 50 55
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset min(step(), 1s)
|
||||
metric1_total{} 49 54 59
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset min(step(), 1s)+8000
|
||||
{} 8049 8054 8059
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset -min(step(), 1s)+8000
|
||||
{} 8051 8056 8061
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset -(min(step(), 1s))+8000
|
||||
{} 8051 8056 8061
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset -min(step(), 1s)^0
|
||||
{} 1 1 1
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset +min(step(), 1s)^0
|
||||
{} 1 1 1
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset min(step(), 1s)^0
|
||||
{} 1 1 1
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset max(3s,min(step(), 1s))+8000
|
||||
{} 8047 8052 8057
|
||||
|
||||
eval range from 50s to 60s step 5s metric1_total offset -(min(step(), 2s)-5)+8000
|
||||
{} 8047 8052 8057
|
189
promql/promqltest/testdata/functions.test
vendored
189
promql/promqltest/testdata/functions.test
vendored
@ -228,37 +228,48 @@ load 5m
|
||||
http_requests_histogram{path="/g"} 0 0 {{schema:-53 sum:3 count:3 custom_values:[1] buckets:[3]}} {{schema:-53 sum:3 count:3 custom_values:[5 10] buckets:[3]}}
|
||||
|
||||
eval instant at 50m irate(http_requests_total[50m])
|
||||
expect no_warn
|
||||
{path="/foo"} .03333333333333333333
|
||||
{path="/bar"} .03333333333333333333
|
||||
|
||||
# Counter reset.
|
||||
eval instant at 30m irate(http_requests_total[50m])
|
||||
expect no_warn
|
||||
{path="/foo"} .03333333333333333333
|
||||
{path="/bar"} 0
|
||||
|
||||
eval range from 0 to 20m step 5m irate(http_requests_nan[15m1s])
|
||||
expect no_warn
|
||||
{} _ NaN NaN NaN 0.02
|
||||
|
||||
eval instant at 20m irate(http_requests_histogram{path="/a"}[20m])
|
||||
expect no_warn
|
||||
{path="/a"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}}
|
||||
|
||||
eval instant at 20m irate(http_requests_histogram{path="/b"}[20m])
|
||||
expect no_warn
|
||||
{path="/b"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}}
|
||||
|
||||
eval instant at 20m irate(http_requests_histogram{path="/b"}[6m])
|
||||
expect no_warn
|
||||
|
||||
eval_warn instant at 20m irate(http_requests_histogram{path="/c"}[20m])
|
||||
eval instant at 20m irate(http_requests_histogram{path="/c"}[20m])
|
||||
expect warn
|
||||
{path="/c"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}}
|
||||
|
||||
eval_warn instant at 20m irate(http_requests_histogram{path="/d"}[20m])
|
||||
eval instant at 20m irate(http_requests_histogram{path="/d"}[20m])
|
||||
expect warn
|
||||
{path="/d"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}}
|
||||
|
||||
eval_warn instant at 20m irate(http_requests_histogram{path="/e"}[20m])
|
||||
eval instant at 20m irate(http_requests_histogram{path="/e"}[20m])
|
||||
expect warn
|
||||
|
||||
eval instant at 20m irate(http_requests_histogram{path="/f"}[20m])
|
||||
expect no_warn
|
||||
{path="/f"} {{schema:-53 sum:0.01 count:0.01 custom_values:[5 10] buckets:[0.01]}}
|
||||
|
||||
eval instant at 20m irate(http_requests_histogram{path="/g"}[20m])
|
||||
expect no_warn
|
||||
{path="/g"} {{schema:-53 sum:0.01 count:0.01 custom_values:[5 10] buckets:[0.01]}}
|
||||
|
||||
clear
|
||||
@ -272,18 +283,22 @@ load 5m
|
||||
http_requests_mix{path="/foo"} 0 50 100 {{schema:0 sum:0 count:0 buckets:[0 0 0] counter_reset_hint:gauge}} {{schema:0 sum:1 count:2 buckets:[1 1 1] counter_reset_hint:gauge}}
|
||||
|
||||
eval instant at 20m delta(http_requests[20m])
|
||||
expect no_warn
|
||||
{path="/foo"} 200
|
||||
{path="/bar"} -200
|
||||
|
||||
eval instant at 20m delta(http_requests_gauge[20m])
|
||||
expect no_warn
|
||||
{path="/foo"} {{schema:0 sum:4 count:8 buckets:[4 4 4]}}
|
||||
|
||||
# delta emits warn annotation for non-gauge histogram types.
|
||||
eval_warn instant at 20m delta(http_requests_counter[20m])
|
||||
eval instant at 20m delta(http_requests_counter[20m])
|
||||
expect warn
|
||||
{path="/foo"} {{schema:0 sum:4 count:8 buckets:[4 4 4]}}
|
||||
|
||||
# delta emits warn annotation for mix of histogram and floats.
|
||||
eval_warn instant at 20m delta(http_requests_mix[20m])
|
||||
eval instant at 20m delta(http_requests_mix[20m])
|
||||
expect warn
|
||||
#empty
|
||||
|
||||
clear
|
||||
@ -302,31 +317,41 @@ load 5m
|
||||
http_requests_histogram{path="/g"} 0 0 {{schema:-53 sum:1 count:1 custom_values:[1] buckets:[2] counter_reset_hint:gauge}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1] counter_reset_hint:gauge}}
|
||||
|
||||
eval instant at 20m idelta(http_requests[20m])
|
||||
expect no_warn
|
||||
{path="/foo"} 50
|
||||
{path="/bar"} -50
|
||||
|
||||
eval range from 0 to 20m step 5m idelta(http_requests_nan[15m1s])
|
||||
expect no_warn
|
||||
{} _ NaN NaN NaN 6
|
||||
|
||||
eval instant at 20m idelta(http_requests_histogram{path="/a"}[20m])
|
||||
expect no_warn
|
||||
{path="/a"} {{sum:1 count:3 counter_reset_hint:gauge}}
|
||||
|
||||
eval instant at 20m idelta(http_requests_histogram{path="/b"}[20m])
|
||||
expect no_warn
|
||||
{path="/b"} {{sum:1 count:1 counter_reset_hint:gauge}}
|
||||
|
||||
eval instant at 20m idelta(http_requests_histogram{path="/b"}[6m])
|
||||
expect no_warn
|
||||
|
||||
eval_warn instant at 20m idelta(http_requests_histogram{path="/c"}[20m])
|
||||
eval instant at 20m idelta(http_requests_histogram{path="/c"}[20m])
|
||||
expect warn
|
||||
{path="/c"} {{sum:1 count:1 counter_reset_hint:gauge}}
|
||||
|
||||
eval_warn instant at 20m idelta(http_requests_histogram{path="/d"}[20m])
|
||||
eval instant at 20m idelta(http_requests_histogram{path="/d"}[20m])
|
||||
expect warn
|
||||
{path="/d"} {{sum:1 count:1 counter_reset_hint:gauge}}
|
||||
|
||||
eval_warn instant at 20m idelta(http_requests_histogram{path="/e"}[20m])
|
||||
eval instant at 20m idelta(http_requests_histogram{path="/e"}[20m])
|
||||
expect warn
|
||||
|
||||
eval_warn instant at 20m idelta(http_requests_histogram{path="/f"}[20m])
|
||||
eval instant at 20m idelta(http_requests_histogram{path="/f"}[20m])
|
||||
expect warn
|
||||
|
||||
eval_warn instant at 20m idelta(http_requests_histogram{path="/g"}[20m])
|
||||
eval instant at 20m idelta(http_requests_histogram{path="/g"}[20m])
|
||||
expect warn
|
||||
|
||||
clear
|
||||
|
||||
@ -341,28 +366,36 @@ load 5m
|
||||
|
||||
# deriv should return the same as rate in simple cases.
|
||||
eval instant at 50m rate(http_requests_total{group="canary", instance="1", job="app-server"}[50m])
|
||||
expect no_info
|
||||
{group="canary", instance="1", job="app-server"} 0.26666666666666666
|
||||
|
||||
eval instant at 50m deriv(http_requests_total{group="canary", instance="1", job="app-server"}[50m])
|
||||
expect no_info
|
||||
{group="canary", instance="1", job="app-server"} 0.26666666666666666
|
||||
|
||||
# deriv should return correct result.
|
||||
eval instant at 50m deriv(testcounter_reset_middle_total[100m])
|
||||
expect no_info
|
||||
{} 0.010606060606060607
|
||||
|
||||
# deriv should ignore histograms in a mixed range of floats and histograms, flagged by an info annotation.
|
||||
eval_info instant at 110m deriv(http_requests_mix{group="canary", instance="1", job="app-server"}[110m])
|
||||
eval instant at 110m deriv(http_requests_mix{group="canary", instance="1", job="app-server"}[110m])
|
||||
expect info
|
||||
{group="canary", instance="1", job="app-server"} 0.26666666666666666
|
||||
|
||||
eval_info instant at 100m deriv(testcounter_reset_middle_mix[110m])
|
||||
eval instant at 100m deriv(testcounter_reset_middle_mix[110m])
|
||||
expect info
|
||||
{} 0.010606060606060607
|
||||
|
||||
# deriv should silently ignore ranges consisting only of histograms.
|
||||
eval instant at 50m deriv(http_requests_histogram[60m])
|
||||
expect no_info
|
||||
expect no_warn
|
||||
#empty
|
||||
|
||||
# deriv should return NaN in case of +Inf or -Inf found.
|
||||
eval instant at 100m deriv(http_requests_inf[100m])
|
||||
expect no_info
|
||||
{job="app-server", instance="1", group="canary"} NaN
|
||||
|
||||
# predict_linear should return correct result.
|
||||
@ -380,35 +413,45 @@ eval instant at 100m deriv(http_requests_inf[100m])
|
||||
# intercept at t=3000: 38.63636363636364
|
||||
# intercept at t=3000+3600: 76.81818181818181
|
||||
eval instant at 50m predict_linear(testcounter_reset_middle_total[50m], 3600)
|
||||
expect no_info
|
||||
{} 70
|
||||
|
||||
eval instant at 50m predict_linear(testcounter_reset_middle_total[50m], 1h)
|
||||
expect no_info
|
||||
{} 70
|
||||
|
||||
# intercept at t = 3000+3600 = 6600
|
||||
eval instant at 50m predict_linear(testcounter_reset_middle_total[55m] @ 3000, 3600)
|
||||
expect no_info
|
||||
{} 76.81818181818181
|
||||
|
||||
eval instant at 50m predict_linear(testcounter_reset_middle_total[55m] @ 3000, 1h)
|
||||
expect no_info
|
||||
{} 76.81818181818181
|
||||
|
||||
# intercept at t = 600+3600 = 4200
|
||||
eval instant at 10m predict_linear(testcounter_reset_middle_total[55m] @ 3000, 3600)
|
||||
expect no_info
|
||||
{} 51.36363636363637
|
||||
|
||||
# intercept at t = 4200+3600 = 7800
|
||||
eval instant at 70m predict_linear(testcounter_reset_middle_total[55m] @ 3000, 3600)
|
||||
expect no_info
|
||||
{} 89.54545454545455
|
||||
|
||||
# predict_linear should ignore histograms in a mixed range of floats and histograms, flagged by an info annotation.
|
||||
eval_info instant at 60m predict_linear(testcounter_reset_middle_mix[60m], 3000)
|
||||
eval instant at 60m predict_linear(testcounter_reset_middle_mix[60m], 3000)
|
||||
expect info
|
||||
{} 70
|
||||
|
||||
eval_info instant at 60m predict_linear(testcounter_reset_middle_mix[60m], 50m)
|
||||
eval instant at 60m predict_linear(testcounter_reset_middle_mix[60m], 50m)
|
||||
expect info
|
||||
{} 70
|
||||
|
||||
# predict_linear should silently ignore ranges consisting only of histograms.
|
||||
eval instant at 60m predict_linear(http_requests_histogram[60m], 50m)
|
||||
expect no_info
|
||||
expect no_warn
|
||||
#empty
|
||||
|
||||
# predict_linear should return NaN in case of +Inf or -Inf found.
|
||||
@ -471,13 +514,16 @@ eval instant at 0m label_replace(testmetric, "dst", "", "dst", ".*")
|
||||
testmetric{src="source-value-20"} 1
|
||||
|
||||
# label_replace fails when the regex is invalid.
|
||||
eval_fail instant at 0m label_replace(testmetric, "dst", "value-$1", "src", "(.*")
|
||||
eval instant at 0m label_replace(testmetric, "dst", "value-$1", "src", "(.*")
|
||||
expect fail
|
||||
|
||||
# label_replace fails when the destination label name is not a valid Prometheus label name.
|
||||
eval_fail instant at 0m label_replace(testmetric, "\xff", "", "src", "(.*)")
|
||||
eval instant at 0m label_replace(testmetric, "\xff", "", "src", "(.*)")
|
||||
expect fail
|
||||
|
||||
# label_replace fails when there would be duplicated identical output label sets.
|
||||
eval_fail instant at 0m label_replace(testmetric, "src", "", "", "")
|
||||
eval instant at 0m label_replace(testmetric, "src", "", "", "")
|
||||
expect fail
|
||||
|
||||
clear
|
||||
|
||||
@ -540,8 +586,8 @@ eval instant at 0m label_join(testmetric1, "dst", ", ", "src", "src1", "src2")
|
||||
testmetric1{src="foo",src1="bar",src2="foobar",dst="foo, bar, foobar"} 0
|
||||
testmetric1{src="fizz",src1="buzz",src2="fizzbuzz",dst="fizz, buzz, fizzbuzz"} 1
|
||||
|
||||
eval_fail instant at 0m label_join(dup, "label", "", "this")
|
||||
expected_fail_message vector cannot contain metrics with the same labelset
|
||||
eval instant at 0m label_join(dup, "label", "", "this")
|
||||
expect fail msg:vector cannot contain metrics with the same labelset
|
||||
|
||||
clear
|
||||
|
||||
@ -652,7 +698,8 @@ load 5m
|
||||
http_requests{job="app-server", instance="1", group="canary"} 0+80x10
|
||||
http_requests{job="app-server", instance="2", group="canary"} {{schema:0 sum:1 count:1}}x15
|
||||
|
||||
eval_ordered instant at 50m sort(http_requests)
|
||||
eval instant at 50m sort(http_requests)
|
||||
expect ordered
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
http_requests{group="production", instance="1", job="api-server"} 200
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
@ -663,7 +710,8 @@ eval_ordered instant at 50m sort(http_requests)
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests{group="canary", instance="2", job="api-server"} NaN
|
||||
|
||||
eval_ordered instant at 50m sort_desc(http_requests)
|
||||
eval instant at 50m sort_desc(http_requests)
|
||||
expect ordered
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
@ -701,7 +749,8 @@ load 5m
|
||||
node_uname_info{job="node_exporter", instance="4m5", release="1.11.3"} 0+10x10
|
||||
node_uname_info{job="node_exporter", instance="4m1000", release="1.111.3"} 0+10x10
|
||||
|
||||
eval_ordered instant at 50m sort_by_label(http_requests, "instance")
|
||||
eval instant at 50m sort_by_label(http_requests, "instance")
|
||||
expect ordered
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
@ -713,7 +762,8 @@ eval_ordered instant at 50m sort_by_label(http_requests, "instance")
|
||||
http_requests{group="canary", instance="2", job="api-server"} NaN
|
||||
http_requests{group="production", instance="2", job="api-server"} 100
|
||||
|
||||
eval_ordered instant at 50m sort_by_label(http_requests, "instance", "group")
|
||||
eval instant at 50m sort_by_label(http_requests, "instance", "group")
|
||||
expect ordered
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
@ -725,7 +775,8 @@ eval_ordered instant at 50m sort_by_label(http_requests, "instance", "group")
|
||||
http_requests{group="canary", instance="2", job="api-server"} NaN
|
||||
http_requests{group="production", instance="2", job="api-server"} 100
|
||||
|
||||
eval_ordered instant at 50m sort_by_label(http_requests, "instance", "group")
|
||||
eval instant at 50m sort_by_label(http_requests, "instance", "group")
|
||||
expect ordered
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
@ -737,7 +788,8 @@ eval_ordered instant at 50m sort_by_label(http_requests, "instance", "group")
|
||||
http_requests{group="canary", instance="2", job="api-server"} NaN
|
||||
http_requests{group="production", instance="2", job="api-server"} 100
|
||||
|
||||
eval_ordered instant at 50m sort_by_label(http_requests, "group", "instance", "job")
|
||||
eval instant at 50m sort_by_label(http_requests, "group", "instance", "job")
|
||||
expect ordered
|
||||
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
|
||||
@ -749,7 +801,8 @@ eval_ordered instant at 50m sort_by_label(http_requests, "group", "instance", "j
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
http_requests{group="production", instance="2", job="api-server"} 100
|
||||
|
||||
eval_ordered instant at 50m sort_by_label(http_requests, "job", "instance", "group")
|
||||
eval instant at 50m sort_by_label(http_requests, "job", "instance", "group")
|
||||
expect ordered
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
http_requests{group="production", instance="0", job="api-server"} 100
|
||||
http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
@ -761,7 +814,8 @@ eval_ordered instant at 50m sort_by_label(http_requests, "job", "instance", "gro
|
||||
http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
|
||||
eval_ordered instant at 50m sort_by_label_desc(http_requests, "instance")
|
||||
eval instant at 50m sort_by_label_desc(http_requests, "instance")
|
||||
expect ordered
|
||||
http_requests{group="production", instance="2", job="api-server"} 100
|
||||
http_requests{group="canary", instance="2", job="api-server"} NaN
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
@ -773,7 +827,8 @@ eval_ordered instant at 50m sort_by_label_desc(http_requests, "instance")
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
|
||||
eval_ordered instant at 50m sort_by_label_desc(http_requests, "instance", "group")
|
||||
eval instant at 50m sort_by_label_desc(http_requests, "instance", "group")
|
||||
expect ordered
|
||||
http_requests{group="production", instance="2", job="api-server"} 100
|
||||
http_requests{group="canary", instance="2", job="api-server"} NaN
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
@ -785,7 +840,8 @@ eval_ordered instant at 50m sort_by_label_desc(http_requests, "instance", "group
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
|
||||
eval_ordered instant at 50m sort_by_label_desc(http_requests, "instance", "group", "job")
|
||||
eval instant at 50m sort_by_label_desc(http_requests, "instance", "group", "job")
|
||||
expect ordered
|
||||
http_requests{group="production", instance="2", job="api-server"} 100
|
||||
http_requests{group="canary", instance="2", job="api-server"} NaN
|
||||
http_requests{group="production", instance="1", job="app-server"} 600
|
||||
@ -797,7 +853,8 @@ eval_ordered instant at 50m sort_by_label_desc(http_requests, "instance", "group
|
||||
http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
|
||||
eval_ordered instant at 50m sort_by_label(cpu_time_total, "cpu")
|
||||
eval instant at 50m sort_by_label(cpu_time_total, "cpu")
|
||||
expect ordered
|
||||
cpu_time_total{job="cpu", cpu="0"} 100
|
||||
cpu_time_total{job="cpu", cpu="1"} 100
|
||||
cpu_time_total{job="cpu", cpu="2"} 100
|
||||
@ -809,12 +866,14 @@ eval_ordered instant at 50m sort_by_label(cpu_time_total, "cpu")
|
||||
cpu_time_total{job="cpu", cpu="21"} 100
|
||||
cpu_time_total{job="cpu", cpu="100"} 100
|
||||
|
||||
eval_ordered instant at 50m sort_by_label(node_uname_info, "instance")
|
||||
eval instant at 50m sort_by_label(node_uname_info, "instance")
|
||||
expect ordered
|
||||
node_uname_info{job="node_exporter", instance="4m5", release="1.11.3"} 100
|
||||
node_uname_info{job="node_exporter", instance="4m600", release="1.2.3"} 100
|
||||
node_uname_info{job="node_exporter", instance="4m1000", release="1.111.3"} 100
|
||||
|
||||
eval_ordered instant at 50m sort_by_label(node_uname_info, "release")
|
||||
eval instant at 50m sort_by_label(node_uname_info, "release")
|
||||
expect ordered
|
||||
node_uname_info{job="node_exporter", instance="4m600", release="1.2.3"} 100
|
||||
node_uname_info{job="node_exporter", instance="4m5", release="1.11.3"} 100
|
||||
node_uname_info{job="node_exporter", instance="4m1000", release="1.111.3"} 100
|
||||
@ -835,13 +894,15 @@ load 10s
|
||||
http_requests_histogram{job="api-server", instance="1", group="canary"} {{schema:0 count:1 sum:2}}x1000
|
||||
|
||||
eval instant at 8000s double_exponential_smoothing(http_requests[1m], 0.01, 0.1)
|
||||
expect no_info
|
||||
{job="api-server", instance="0", group="production"} 8000
|
||||
{job="api-server", instance="1", group="production"} 16000
|
||||
{job="api-server", instance="0", group="canary"} 24000
|
||||
{job="api-server", instance="1", group="canary"} 32000
|
||||
|
||||
# double_exponential_smoothing should ignore histograms in a mixed range of floats and histograms, flagged by an info annotation.
|
||||
eval_info instant at 20010s double_exponential_smoothing(http_requests_mix[1m], 0.01, 0.1)
|
||||
eval instant at 20010s double_exponential_smoothing(http_requests_mix[1m], 0.01, 0.1)
|
||||
expect info
|
||||
{job="api-server", instance="0", group="production"} 30100
|
||||
{job="api-server", instance="1", group="production"} 30200
|
||||
{job="api-server", instance="0", group="canary"} 80300
|
||||
@ -849,6 +910,7 @@ eval_info instant at 20010s double_exponential_smoothing(http_requests_mix[1m],
|
||||
|
||||
# double_exponential_smoothing should silently ignore ranges consisting only of histograms.
|
||||
eval instant at 10000s double_exponential_smoothing(http_requests_histogram[1m], 0.01, 0.1)
|
||||
expect no_info
|
||||
#empty
|
||||
|
||||
# negative trends
|
||||
@ -994,10 +1056,12 @@ eval instant at 55s sum_over_time(metric11[1m])/count_over_time(metric11[1m])
|
||||
{} NaN
|
||||
|
||||
# Tests for samples with mix of floats and histograms.
|
||||
eval_warn instant at 55s sum_over_time(metric12[1m])
|
||||
eval instant at 55s sum_over_time(metric12[1m])
|
||||
expect warn
|
||||
# no result.
|
||||
|
||||
eval_warn instant at 55s avg_over_time(metric12[1m])
|
||||
eval instant at 55s avg_over_time(metric12[1m])
|
||||
expect warn
|
||||
# no result.
|
||||
|
||||
# Tests for samples with only histograms.
|
||||
@ -1184,13 +1248,16 @@ eval instant at 1m stddev_over_time((metric[2m]))
|
||||
eval instant at 1m stddev_over_time(metric_histogram{type="only_histogram"}[2m])
|
||||
#empty
|
||||
|
||||
eval_info instant at 1m stddev_over_time(metric_histogram{type="mix"}[2m])
|
||||
eval instant at 1m stddev_over_time(metric_histogram{type="mix"}[2m])
|
||||
expect info
|
||||
{type="mix"} 0
|
||||
|
||||
eval instant at 1m stdvar_over_time(metric_histogram{type="only_histogram"}[2m])
|
||||
expect no_info
|
||||
#empty
|
||||
|
||||
eval_info instant at 1m stdvar_over_time(metric_histogram{type="mix"}[2m])
|
||||
eval instant at 1m stdvar_over_time(metric_histogram{type="mix"}[2m])
|
||||
expect info
|
||||
{type="mix"} 0
|
||||
|
||||
# Tests for stddev_over_time and stdvar_over_time #4927.
|
||||
@ -1212,12 +1279,15 @@ load 10s
|
||||
metric_histogram{type="mix"} 1 1 1 {{schema:1 sum:2 count:3}} {{schema:1 sum:2 count:3}}
|
||||
|
||||
eval instant at 70s mad_over_time(metric[70s])
|
||||
expect no_info
|
||||
{} 1
|
||||
|
||||
eval instant at 70s mad_over_time(metric_histogram{type="only_histogram"}[70s])
|
||||
expect no_info
|
||||
#empty
|
||||
|
||||
eval_info instant at 70s mad_over_time(metric_histogram{type="mix"}[70s])
|
||||
eval instant at 70s mad_over_time(metric_histogram{type="mix"}[70s])
|
||||
expect info
|
||||
{type="mix"} 0
|
||||
|
||||
# Tests for ts_of_max_over_time and ts_of_min_over_time. Using odd scrape interval to test for rounding bugs.
|
||||
@ -1261,49 +1331,69 @@ load 10s
|
||||
data_histogram{test="mix samples"} 0 1 2 {{schema:0 sum:1 count:2}}x2
|
||||
|
||||
eval instant at 1m quantile_over_time(0, data[2m])
|
||||
expect no_info
|
||||
expect no_warn
|
||||
{test="two samples"} 0
|
||||
{test="three samples"} 0
|
||||
{test="uneven samples"} 0
|
||||
|
||||
eval instant at 1m quantile_over_time(0.5, data[2m])
|
||||
expect no_info
|
||||
expect no_warn
|
||||
{test="two samples"} 0.5
|
||||
{test="three samples"} 1
|
||||
{test="uneven samples"} 1
|
||||
|
||||
eval instant at 1m quantile_over_time(0.75, data[2m])
|
||||
expect no_info
|
||||
expect no_warn
|
||||
{test="two samples"} 0.75
|
||||
{test="three samples"} 1.5
|
||||
{test="uneven samples"} 2.5
|
||||
|
||||
eval instant at 1m quantile_over_time(0.8, data[2m])
|
||||
expect no_info
|
||||
expect no_warn
|
||||
{test="two samples"} 0.8
|
||||
{test="three samples"} 1.6
|
||||
{test="uneven samples"} 2.8
|
||||
|
||||
eval instant at 1m quantile_over_time(1, data[2m])
|
||||
expect no_info
|
||||
expect no_warn
|
||||
{test="two samples"} 1
|
||||
{test="three samples"} 2
|
||||
{test="uneven samples"} 4
|
||||
|
||||
eval_warn instant at 1m quantile_over_time(-1, data[2m])
|
||||
eval instant at 1m quantile_over_time(-1, data[2m])
|
||||
expect no_info
|
||||
expect warn
|
||||
{test="two samples"} -Inf
|
||||
{test="three samples"} -Inf
|
||||
{test="uneven samples"} -Inf
|
||||
|
||||
eval_warn instant at 1m quantile_over_time(2, data[2m])
|
||||
eval instant at 1m quantile_over_time(2, data[2m])
|
||||
expect no_info
|
||||
expect warn
|
||||
{test="two samples"} +Inf
|
||||
{test="three samples"} +Inf
|
||||
{test="uneven samples"} +Inf
|
||||
|
||||
eval_warn instant at 1m (quantile_over_time(2, (data[2m])))
|
||||
eval instant at 1m (quantile_over_time(2, (data[2m])))
|
||||
expect no_info
|
||||
expect warn
|
||||
{test="two samples"} +Inf
|
||||
{test="three samples"} +Inf
|
||||
{test="uneven samples"} +Inf
|
||||
|
||||
eval instant at 1m quantile_over_time(0.5, data_histogram{test="only histogram samples"}[2m])
|
||||
expect no_info
|
||||
expect no_warn
|
||||
#empty
|
||||
|
||||
eval_info instant at 1m quantile_over_time(0.5, data_histogram{test="mix samples"}[2m])
|
||||
eval instant at 1m quantile_over_time(0.5, data_histogram{test="mix samples"}[2m])
|
||||
expect info
|
||||
expect no_warn
|
||||
{test="mix samples"} 1
|
||||
|
||||
clear
|
||||
@ -1417,7 +1507,8 @@ load 5m
|
||||
testmetric1{src="a",dst="b"} 0
|
||||
testmetric2{src="a",dst="b"} 1
|
||||
|
||||
eval_fail instant at 0m changes({__name__=~'testmetric1|testmetric2'}[5m])
|
||||
eval instant at 0m changes({__name__=~'testmetric1|testmetric2'}[5m])
|
||||
expect fail
|
||||
|
||||
clear
|
||||
|
||||
@ -1432,6 +1523,7 @@ load 10s
|
||||
data_histogram{type="mix_samples"} 0 1 {{schema:0 sum:1 count:2}} {{schema:0 sum:2 count:3}}
|
||||
|
||||
eval instant at 1m min_over_time(data[2m])
|
||||
expect no_info
|
||||
{type="numbers"} 0
|
||||
{type="some_nan"} 0
|
||||
{type="some_nan2"} 1
|
||||
@ -1439,12 +1531,15 @@ eval instant at 1m min_over_time(data[2m])
|
||||
{type="only_nan"} NaN
|
||||
|
||||
eval instant at 1m min_over_time(data_histogram{type="only_histogram"}[2m])
|
||||
expect no_info
|
||||
#empty
|
||||
|
||||
eval_info instant at 1m min_over_time(data_histogram{type="mix_samples"}[2m])
|
||||
eval instant at 1m min_over_time(data_histogram{type="mix_samples"}[2m])
|
||||
expect info
|
||||
{type="mix_samples"} 0
|
||||
|
||||
eval instant at 1m max_over_time(data[2m])
|
||||
expect no_info
|
||||
{type="numbers"} 3
|
||||
{type="some_nan"} 2
|
||||
{type="some_nan2"} 2
|
||||
@ -1452,12 +1547,15 @@ eval instant at 1m max_over_time(data[2m])
|
||||
{type="only_nan"} NaN
|
||||
|
||||
eval instant at 1m max_over_time(data_histogram{type="only_histogram"}[2m])
|
||||
expect no_info
|
||||
#empty
|
||||
|
||||
eval_info instant at 1m max_over_time(data_histogram{type="mix_samples"}[2m])
|
||||
eval instant at 1m max_over_time(data_histogram{type="mix_samples"}[2m])
|
||||
expect info
|
||||
{type="mix_samples"} 1
|
||||
|
||||
eval instant at 1m last_over_time({__name__=~"data(_histogram)?"}[2m])
|
||||
expect no_info
|
||||
data{type="numbers"} 3
|
||||
data{type="some_nan"} NaN
|
||||
data{type="some_nan2"} 1
|
||||
@ -1467,6 +1565,7 @@ eval instant at 1m last_over_time({__name__=~"data(_histogram)?"}[2m])
|
||||
data_histogram{type="mix_samples"} {{schema:0 sum:2 count:3}}
|
||||
|
||||
eval instant at 1m count_over_time({__name__=~"data(_histogram)?"}[2m])
|
||||
expect no_info
|
||||
{type="numbers"} 3
|
||||
{type="some_nan"} 3
|
||||
{type="some_nan2"} 3
|
||||
|
124
promql/promqltest/testdata/histograms.test
vendored
124
promql/promqltest/testdata/histograms.test
vendored
@ -70,74 +70,91 @@ load_with_nhcb 5m
|
||||
|
||||
# Test histogram_count.
|
||||
eval instant at 50m histogram_count(testhistogram3)
|
||||
expect no_warn
|
||||
{start="positive"} 110
|
||||
{start="negative"} 20
|
||||
|
||||
# Classic way of accessing the count still works.
|
||||
eval instant at 50m testhistogram3_count
|
||||
expect no_warn
|
||||
testhistogram3_count{start="positive"} 110
|
||||
testhistogram3_count{start="negative"} 20
|
||||
|
||||
# Test histogram_sum.
|
||||
eval instant at 50m histogram_sum(testhistogram3)
|
||||
expect no_warn
|
||||
{start="positive"} 330
|
||||
{start="negative"} 80
|
||||
|
||||
# Classic way of accessing the sum still works.
|
||||
eval instant at 50m testhistogram3_sum
|
||||
expect no_warn
|
||||
testhistogram3_sum{start="positive"} 330
|
||||
testhistogram3_sum{start="negative"} 80
|
||||
|
||||
# Test histogram_avg. This has no classic equivalent.
|
||||
eval instant at 50m histogram_avg(testhistogram3)
|
||||
expect no_warn
|
||||
{start="positive"} 3
|
||||
{start="negative"} 4
|
||||
|
||||
# Test histogram_stddev. This has no classic equivalent.
|
||||
eval instant at 50m histogram_stddev(testhistogram3)
|
||||
expect no_warn
|
||||
{start="positive"} 2.7435461458749795
|
||||
{start="negative"} 4.187667907081458
|
||||
|
||||
# Test histogram_stdvar. This has no classic equivalent.
|
||||
eval instant at 50m histogram_stdvar(testhistogram3)
|
||||
expect no_warn
|
||||
{start="positive"} 7.527045454545455
|
||||
{start="negative"} 17.5365625
|
||||
|
||||
# Test histogram_fraction.
|
||||
#
|
||||
eval instant at 50m histogram_fraction(0, 4, testhistogram2)
|
||||
expect no_warn
|
||||
{} 0.6666666666666666
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 4, testhistogram2_bucket)
|
||||
expect no_warn
|
||||
{} 0.6666666666666666
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 6, testhistogram2)
|
||||
expect no_warn
|
||||
{} 1
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 6, testhistogram2_bucket)
|
||||
expect no_warn
|
||||
{} 1
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 3.5, testhistogram2)
|
||||
expect no_warn
|
||||
{} 0.5833333333333334
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 3.5, testhistogram2_bucket)
|
||||
expect no_warn
|
||||
{} 0.5833333333333334
|
||||
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 0.2, testhistogram3)
|
||||
expect no_warn
|
||||
{start="positive"} 0.6363636363636364
|
||||
{start="negative"} 0
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 0.2, testhistogram3_bucket)
|
||||
expect no_warn
|
||||
{start="positive"} 0.6363636363636364
|
||||
{start="negative"} 0
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 0.2, rate(testhistogram3[10m]))
|
||||
expect no_warn
|
||||
{start="positive"} 0.6363636363636364
|
||||
{start="negative"} 0
|
||||
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 0.2, rate(testhistogram3_bucket[10m]))
|
||||
expect no_warn
|
||||
{start="positive"} 0.6363636363636364
|
||||
{start="negative"} 0
|
||||
|
||||
@ -145,276 +162,345 @@ eval instant at 50m histogram_fraction(0, 0.2, rate(testhistogram3_bucket[10m]))
|
||||
# it exists) and divide by the count to get the same result.
|
||||
|
||||
eval instant at 50m testhistogram3_bucket{le=".2"} / ignoring(le) testhistogram3_count
|
||||
expect no_warn
|
||||
{start="positive"} 0.6363636363636364
|
||||
|
||||
eval instant at 50m rate(testhistogram3_bucket{le=".2"}[10m]) / ignoring(le) rate(testhistogram3_count[10m])
|
||||
expect no_warn
|
||||
{start="positive"} 0.6363636363636364
|
||||
|
||||
# Test histogram_quantile, native and classic.
|
||||
|
||||
eval instant at 50m histogram_quantile(0, testhistogram3)
|
||||
expect no_warn
|
||||
{start="positive"} 0
|
||||
{start="negative"} -0.25
|
||||
|
||||
eval instant at 50m histogram_quantile(0, testhistogram3_bucket)
|
||||
expect no_warn
|
||||
{start="positive"} 0
|
||||
{start="negative"} -0.25
|
||||
|
||||
eval instant at 50m histogram_quantile(0.25, testhistogram3)
|
||||
expect no_warn
|
||||
{start="positive"} 0.055
|
||||
{start="negative"} -0.225
|
||||
|
||||
eval instant at 50m histogram_quantile(0.25, testhistogram3_bucket)
|
||||
expect no_warn
|
||||
{start="positive"} 0.055
|
||||
{start="negative"} -0.225
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, testhistogram3)
|
||||
expect no_warn
|
||||
{start="positive"} 0.125
|
||||
{start="negative"} -0.2
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, testhistogram3_bucket)
|
||||
expect no_warn
|
||||
{start="positive"} 0.125
|
||||
{start="negative"} -0.2
|
||||
|
||||
eval instant at 50m histogram_quantile(0.75, testhistogram3)
|
||||
expect no_warn
|
||||
{start="positive"} 0.45
|
||||
{start="negative"} -0.15
|
||||
|
||||
eval instant at 50m histogram_quantile(0.75, testhistogram3_bucket)
|
||||
expect no_warn
|
||||
{start="positive"} 0.45
|
||||
{start="negative"} -0.15
|
||||
|
||||
eval instant at 50m histogram_quantile(1, testhistogram3)
|
||||
expect no_warn
|
||||
{start="positive"} 1
|
||||
{start="negative"} -0.1
|
||||
|
||||
eval instant at 50m histogram_quantile(1, testhistogram3_bucket)
|
||||
expect no_warn
|
||||
{start="positive"} 1
|
||||
{start="negative"} -0.1
|
||||
|
||||
# Quantile too low.
|
||||
|
||||
eval_warn instant at 50m histogram_quantile(-0.1, testhistogram)
|
||||
eval instant at 50m histogram_quantile(-0.1, testhistogram)
|
||||
expect warn
|
||||
{start="positive"} -Inf
|
||||
{start="negative"} -Inf
|
||||
|
||||
eval_warn instant at 50m histogram_quantile(-0.1, testhistogram_bucket)
|
||||
eval instant at 50m histogram_quantile(-0.1, testhistogram_bucket)
|
||||
expect warn
|
||||
{start="positive"} -Inf
|
||||
{start="negative"} -Inf
|
||||
|
||||
# Quantile too high.
|
||||
|
||||
eval_warn instant at 50m histogram_quantile(1.01, testhistogram)
|
||||
eval instant at 50m histogram_quantile(1.01, testhistogram)
|
||||
expect warn
|
||||
{start="positive"} +Inf
|
||||
{start="negative"} +Inf
|
||||
|
||||
eval_warn instant at 50m histogram_quantile(1.01, testhistogram_bucket)
|
||||
eval instant at 50m histogram_quantile(1.01, testhistogram_bucket)
|
||||
expect warn
|
||||
{start="positive"} +Inf
|
||||
{start="negative"} +Inf
|
||||
|
||||
# Quantile invalid.
|
||||
|
||||
eval_warn instant at 50m histogram_quantile(NaN, testhistogram)
|
||||
eval instant at 50m histogram_quantile(NaN, testhistogram)
|
||||
expect warn
|
||||
{start="positive"} NaN
|
||||
{start="negative"} NaN
|
||||
|
||||
eval_warn instant at 50m histogram_quantile(NaN, testhistogram_bucket)
|
||||
eval instant at 50m histogram_quantile(NaN, testhistogram_bucket)
|
||||
expect warn
|
||||
{start="positive"} NaN
|
||||
{start="negative"} NaN
|
||||
|
||||
eval instant at 50m histogram_quantile(NaN, non_existent)
|
||||
expect warn msg: PromQL warning: quantile value should be between 0 and 1, got NaN
|
||||
|
||||
# Quantile value in lowest bucket.
|
||||
|
||||
eval instant at 50m histogram_quantile(0, testhistogram)
|
||||
expect no_warn
|
||||
{start="positive"} 0
|
||||
{start="negative"} -0.2
|
||||
|
||||
eval instant at 50m histogram_quantile(0, testhistogram_bucket)
|
||||
expect no_warn
|
||||
{start="positive"} 0
|
||||
{start="negative"} -0.2
|
||||
|
||||
# Quantile value in highest bucket.
|
||||
|
||||
eval instant at 50m histogram_quantile(1, testhistogram)
|
||||
expect no_warn
|
||||
{start="positive"} 1
|
||||
{start="negative"} 0.3
|
||||
|
||||
eval instant at 50m histogram_quantile(1, testhistogram_bucket)
|
||||
expect no_warn
|
||||
{start="positive"} 1
|
||||
{start="negative"} 0.3
|
||||
|
||||
# Finally some useful quantiles.
|
||||
|
||||
eval instant at 50m histogram_quantile(0.2, testhistogram)
|
||||
expect no_warn
|
||||
{start="positive"} 0.048
|
||||
{start="negative"} -0.2
|
||||
|
||||
eval instant at 50m histogram_quantile(0.2, testhistogram_bucket)
|
||||
expect no_warn
|
||||
{start="positive"} 0.048
|
||||
{start="negative"} -0.2
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, testhistogram)
|
||||
expect no_warn
|
||||
{start="positive"} 0.15
|
||||
{start="negative"} -0.15
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, testhistogram_bucket)
|
||||
expect no_warn
|
||||
{start="positive"} 0.15
|
||||
{start="negative"} -0.15
|
||||
|
||||
eval instant at 50m histogram_quantile(0.8, testhistogram)
|
||||
expect no_warn
|
||||
{start="positive"} 0.72
|
||||
{start="negative"} 0.3
|
||||
|
||||
eval instant at 50m histogram_quantile(0.8, testhistogram_bucket)
|
||||
expect no_warn
|
||||
{start="positive"} 0.72
|
||||
{start="negative"} 0.3
|
||||
|
||||
# More realistic with rates.
|
||||
eval instant at 50m histogram_quantile(0.2, rate(testhistogram[10m]))
|
||||
expect no_warn
|
||||
{start="positive"} 0.048
|
||||
{start="negative"} -0.2
|
||||
|
||||
eval instant at 50m histogram_quantile(0.2, rate(testhistogram_bucket[10m]))
|
||||
expect no_warn
|
||||
{start="positive"} 0.048
|
||||
{start="negative"} -0.2
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, rate(testhistogram[10m]))
|
||||
expect no_warn
|
||||
{start="positive"} 0.15
|
||||
{start="negative"} -0.15
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, rate(testhistogram_bucket[10m]))
|
||||
expect no_warn
|
||||
{start="positive"} 0.15
|
||||
{start="negative"} -0.15
|
||||
|
||||
eval instant at 50m histogram_quantile(0.8, rate(testhistogram[10m]))
|
||||
expect no_warn
|
||||
{start="positive"} 0.72
|
||||
{start="negative"} 0.3
|
||||
|
||||
eval instant at 50m histogram_quantile(0.8, rate(testhistogram_bucket[10m]))
|
||||
expect no_warn
|
||||
{start="positive"} 0.72
|
||||
{start="negative"} 0.3
|
||||
|
||||
# Want results exactly in the middle of the bucket.
|
||||
|
||||
eval instant at 7m histogram_quantile(1./6., testhistogram2)
|
||||
expect no_warn
|
||||
{} 1
|
||||
|
||||
eval instant at 7m histogram_quantile(1./6., testhistogram2_bucket)
|
||||
expect no_warn
|
||||
{} 1
|
||||
|
||||
eval instant at 7m histogram_quantile(0.5, testhistogram2)
|
||||
expect no_warn
|
||||
{} 3
|
||||
|
||||
eval instant at 7m histogram_quantile(0.5, testhistogram2_bucket)
|
||||
expect no_warn
|
||||
{} 3
|
||||
|
||||
eval instant at 7m histogram_quantile(5./6., testhistogram2)
|
||||
expect no_warn
|
||||
{} 5
|
||||
|
||||
eval instant at 7m histogram_quantile(5./6., testhistogram2_bucket)
|
||||
expect no_warn
|
||||
{} 5
|
||||
|
||||
eval instant at 47m histogram_quantile(1./6., rate(testhistogram2[15m]))
|
||||
expect no_warn
|
||||
{} 1
|
||||
|
||||
eval instant at 47m histogram_quantile(1./6., rate(testhistogram2_bucket[15m]))
|
||||
expect no_warn
|
||||
{} 1
|
||||
|
||||
eval instant at 47m histogram_quantile(0.5, rate(testhistogram2[15m]))
|
||||
expect no_warn
|
||||
{} 3
|
||||
|
||||
eval instant at 47m histogram_quantile(0.5, rate(testhistogram2_bucket[15m]))
|
||||
expect no_warn
|
||||
{} 3
|
||||
|
||||
eval instant at 47m histogram_quantile(5./6., rate(testhistogram2[15m]))
|
||||
expect no_warn
|
||||
{} 5
|
||||
|
||||
eval instant at 47m histogram_quantile(5./6., rate(testhistogram2_bucket[15m]))
|
||||
expect no_warn
|
||||
{} 5
|
||||
|
||||
# Aggregated histogram: Everything in one. Note how native histograms
|
||||
# don't require aggregation by le.
|
||||
|
||||
eval instant at 50m histogram_quantile(0.3, sum(rate(request_duration_seconds[10m])))
|
||||
expect no_warn
|
||||
{} 0.075
|
||||
|
||||
eval instant at 50m histogram_quantile(0.3, sum(rate(request_duration_seconds_bucket[10m])) by (le))
|
||||
expect no_warn
|
||||
{} 0.075
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, sum(rate(request_duration_seconds[10m])))
|
||||
expect no_warn
|
||||
{} 0.1277777777777778
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, sum(rate(request_duration_seconds_bucket[10m])) by (le))
|
||||
expect no_warn
|
||||
{} 0.1277777777777778
|
||||
|
||||
# Aggregated histogram: Everything in one. Now with avg, which does not change anything.
|
||||
|
||||
eval instant at 50m histogram_quantile(0.3, avg(rate(request_duration_seconds[10m])))
|
||||
expect no_warn
|
||||
{} 0.075
|
||||
|
||||
eval instant at 50m histogram_quantile(0.3, avg(rate(request_duration_seconds_bucket[10m])) by (le))
|
||||
expect no_warn
|
||||
{} 0.075
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, avg(rate(request_duration_seconds[10m])))
|
||||
expect no_warn
|
||||
{} 0.12777777777777778
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, avg(rate(request_duration_seconds_bucket[10m])) by (le))
|
||||
expect no_warn
|
||||
{} 0.12777777777777778
|
||||
|
||||
# Aggregated histogram: By instance.
|
||||
|
||||
eval instant at 50m histogram_quantile(0.3, sum(rate(request_duration_seconds[10m])) by (instance))
|
||||
expect no_warn
|
||||
{instance="ins1"} 0.075
|
||||
{instance="ins2"} 0.075
|
||||
|
||||
eval instant at 50m histogram_quantile(0.3, sum(rate(request_duration_seconds_bucket[10m])) by (le, instance))
|
||||
expect no_warn
|
||||
{instance="ins1"} 0.075
|
||||
{instance="ins2"} 0.075
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, sum(rate(request_duration_seconds[10m])) by (instance))
|
||||
expect no_warn
|
||||
{instance="ins1"} 0.1333333333
|
||||
{instance="ins2"} 0.125
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, sum(rate(request_duration_seconds_bucket[10m])) by (le, instance))
|
||||
expect no_warn
|
||||
{instance="ins1"} 0.1333333333
|
||||
{instance="ins2"} 0.125
|
||||
|
||||
# Aggregated histogram: By job.
|
||||
eval instant at 50m histogram_quantile(0.3, sum(rate(request_duration_seconds[10m])) by (job))
|
||||
expect no_warn
|
||||
{job="job1"} 0.1
|
||||
{job="job2"} 0.0642857142857143
|
||||
|
||||
eval instant at 50m histogram_quantile(0.3, sum(rate(request_duration_seconds_bucket[10m])) by (le, job))
|
||||
expect no_warn
|
||||
{job="job1"} 0.1
|
||||
{job="job2"} 0.0642857142857143
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, sum(rate(request_duration_seconds[10m])) by (job))
|
||||
expect no_warn
|
||||
{job="job1"} 0.14
|
||||
{job="job2"} 0.1125
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, sum(rate(request_duration_seconds_bucket[10m])) by (le, job))
|
||||
expect no_warn
|
||||
{job="job1"} 0.14
|
||||
{job="job2"} 0.1125
|
||||
|
||||
# Aggregated histogram: By job and instance.
|
||||
eval instant at 50m histogram_quantile(0.3, sum(rate(request_duration_seconds[10m])) by (job, instance))
|
||||
expect no_warn
|
||||
{instance="ins1", job="job1"} 0.11
|
||||
{instance="ins2", job="job1"} 0.09
|
||||
{instance="ins1", job="job2"} 0.06
|
||||
{instance="ins2", job="job2"} 0.0675
|
||||
|
||||
eval instant at 50m histogram_quantile(0.3, sum(rate(request_duration_seconds_bucket[10m])) by (le, job, instance))
|
||||
expect no_warn
|
||||
{instance="ins1", job="job1"} 0.11
|
||||
{instance="ins2", job="job1"} 0.09
|
||||
{instance="ins1", job="job2"} 0.06
|
||||
{instance="ins2", job="job2"} 0.0675
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, sum(rate(request_duration_seconds[10m])) by (job, instance))
|
||||
expect no_warn
|
||||
{instance="ins1", job="job1"} 0.15
|
||||
{instance="ins2", job="job1"} 0.1333333333333333
|
||||
{instance="ins1", job="job2"} 0.1
|
||||
{instance="ins2", job="job2"} 0.1166666666666667
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, sum(rate(request_duration_seconds_bucket[10m])) by (le, job, instance))
|
||||
expect no_warn
|
||||
{instance="ins1", job="job1"} 0.15
|
||||
{instance="ins2", job="job1"} 0.1333333333333333
|
||||
{instance="ins1", job="job2"} 0.1
|
||||
@ -422,24 +508,28 @@ eval instant at 50m histogram_quantile(0.5, sum(rate(request_duration_seconds_bu
|
||||
|
||||
# The unaggregated histogram for comparison. Same result as the previous one.
|
||||
eval instant at 50m histogram_quantile(0.3, rate(request_duration_seconds[10m]))
|
||||
expect no_warn
|
||||
{instance="ins1", job="job1"} 0.11
|
||||
{instance="ins2", job="job1"} 0.09
|
||||
{instance="ins1", job="job2"} 0.06
|
||||
{instance="ins2", job="job2"} 0.0675
|
||||
|
||||
eval instant at 50m histogram_quantile(0.3, rate(request_duration_seconds_bucket[10m]))
|
||||
expect no_warn
|
||||
{instance="ins1", job="job1"} 0.11
|
||||
{instance="ins2", job="job1"} 0.09
|
||||
{instance="ins1", job="job2"} 0.06
|
||||
{instance="ins2", job="job2"} 0.0675
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, rate(request_duration_seconds[10m]))
|
||||
expect no_warn
|
||||
{instance="ins1", job="job1"} 0.15
|
||||
{instance="ins2", job="job1"} 0.13333333333333333
|
||||
{instance="ins1", job="job2"} 0.1
|
||||
{instance="ins2", job="job2"} 0.11666666666666667
|
||||
|
||||
eval instant at 50m histogram_quantile(0.5, rate(request_duration_seconds_bucket[10m]))
|
||||
expect no_warn
|
||||
{instance="ins1", job="job1"} 0.15
|
||||
{instance="ins2", job="job1"} 0.13333333333333333
|
||||
{instance="ins1", job="job2"} 0.1
|
||||
@ -447,25 +537,32 @@ eval instant at 50m histogram_quantile(0.5, rate(request_duration_seconds_bucket
|
||||
|
||||
# All NHCBs summed into one.
|
||||
eval instant at 50m sum(request_duration_seconds)
|
||||
expect no_warn
|
||||
{} {{schema:-53 count:250 custom_values:[0.1 0.2] buckets:[100 90 60]}}
|
||||
|
||||
eval instant at 50m sum(request_duration_seconds{job="job1",instance="ins1"} + ignoring(job,instance) request_duration_seconds{job="job1",instance="ins2"} + ignoring(job,instance) request_duration_seconds{job="job2",instance="ins1"} + ignoring(job,instance) request_duration_seconds{job="job2",instance="ins2"})
|
||||
expect no_warn
|
||||
{} {{schema:-53 count:250 custom_values:[0.1 0.2] buckets:[100 90 60]}}
|
||||
|
||||
eval instant at 50m avg(request_duration_seconds)
|
||||
expect no_warn
|
||||
{} {{schema:-53 count:62.5 custom_values:[0.1 0.2] buckets:[25 22.5 15]}}
|
||||
|
||||
# To verify the result above, calculate from classic histogram as well.
|
||||
eval instant at 50m avg (request_duration_seconds_bucket{le="0.1"})
|
||||
expect no_warn
|
||||
{} 25
|
||||
|
||||
eval instant at 50m avg (request_duration_seconds_bucket{le="0.2"}) - avg (request_duration_seconds_bucket{le="0.1"})
|
||||
expect no_warn
|
||||
{} 22.5
|
||||
|
||||
eval instant at 50m avg (request_duration_seconds_bucket{le="+Inf"}) - avg (request_duration_seconds_bucket{le="0.2"})
|
||||
expect no_warn
|
||||
{} 15
|
||||
|
||||
eval instant at 50m count(request_duration_seconds)
|
||||
expect no_warn
|
||||
{} 4
|
||||
|
||||
# A histogram with nonmonotonic bucket counts. This may happen when recording
|
||||
@ -481,13 +578,16 @@ load 5m
|
||||
nonmonotonic_bucket{le="+Inf"} 0+8x10
|
||||
|
||||
# Nonmonotonic buckets, triggering an info annotation.
|
||||
eval_info instant at 50m histogram_quantile(0.01, nonmonotonic_bucket)
|
||||
eval instant at 50m histogram_quantile(0.01, nonmonotonic_bucket)
|
||||
expect info
|
||||
{} 0.0045
|
||||
|
||||
eval_info instant at 50m histogram_quantile(0.5, nonmonotonic_bucket)
|
||||
eval instant at 50m histogram_quantile(0.5, nonmonotonic_bucket)
|
||||
expect info
|
||||
{} 8.5
|
||||
|
||||
eval_info instant at 50m histogram_quantile(0.99, nonmonotonic_bucket)
|
||||
eval instant at 50m histogram_quantile(0.99, nonmonotonic_bucket)
|
||||
expect info
|
||||
{} 979.75
|
||||
|
||||
# Buckets with different representations of the same upper bound.
|
||||
@ -522,9 +622,11 @@ load_with_nhcb 5m
|
||||
request_duration_seconds2_bucket{job="job1", instance="ins1", le="0.2"} 0+3x10
|
||||
request_duration_seconds2_bucket{job="job1", instance="ins1", le="+Inf"} 0+4x10
|
||||
|
||||
eval_fail instant at 50m histogram_quantile(0.99, {__name__=~"request_duration_seconds\\d*_bucket"})
|
||||
eval instant at 50m histogram_quantile(0.99, {__name__=~"request_duration_seconds\\d*_bucket"})
|
||||
expect fail
|
||||
|
||||
eval_fail instant at 50m histogram_quantile(0.99, {__name__=~"request_duration_seconds\\d*"})
|
||||
eval instant at 50m histogram_quantile(0.99, {__name__=~"request_duration_seconds\\d*"})
|
||||
expect fail
|
||||
|
||||
# Histogram with constant buckets.
|
||||
load_with_nhcb 1m
|
||||
|
8
promql/promqltest/testdata/limit.test
vendored
8
promql/promqltest/testdata/limit.test
vendored
@ -15,10 +15,14 @@ load 5m
|
||||
bar 0 1 0 -1 0 1 0 -1 0 1 0
|
||||
|
||||
eval instant at 50m count(limitk by (group) (0, http_requests))
|
||||
# empty
|
||||
expect no_info
|
||||
expect no_warn
|
||||
# empty
|
||||
|
||||
eval instant at 50m count(limitk by (group) (-1, http_requests))
|
||||
# empty
|
||||
expect no_info
|
||||
expect no_warn
|
||||
# empty
|
||||
|
||||
# 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)
|
||||
|
@ -73,7 +73,8 @@ eval instant at 10m sum by (__name__, env) (metric_total{env="1"})
|
||||
|
||||
# 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]))
|
||||
eval instant at 10m sum by (__name__) (rate({env="1"}[10m]))
|
||||
expect fail
|
||||
|
||||
# Aggregation operators aggregate metrics with same labelset and to-be-dropped names.
|
||||
# This is an accidental side effect of delayed __name__ label dropping
|
||||
|
252
promql/promqltest/testdata/native_histograms.test
vendored
252
promql/promqltest/testdata/native_histograms.test
vendored
@ -398,35 +398,44 @@ clear
|
||||
load 10m
|
||||
histogram_quantile_1 {{schema:0 count:12 sum:100 z_bucket:2 z_bucket_w:0.001 buckets:[2 3 0 1 4]}}x1
|
||||
|
||||
eval_warn instant at 10m histogram_quantile(1.001, histogram_quantile_1)
|
||||
eval instant at 10m histogram_quantile(1.001, histogram_quantile_1)
|
||||
expect warn
|
||||
{} Inf
|
||||
|
||||
eval instant at 10m histogram_quantile(1, histogram_quantile_1)
|
||||
expect no_warn
|
||||
{} 16
|
||||
|
||||
# The following quantiles are within a bucket. Exponential
|
||||
# interpolation is applied (rather than linear, as it is done for
|
||||
# classic histograms), leading to slightly different quantile values.
|
||||
eval instant at 10m histogram_quantile(0.99, histogram_quantile_1)
|
||||
expect no_warn
|
||||
{} 15.67072476139083
|
||||
|
||||
eval instant at 10m histogram_quantile(0.9, histogram_quantile_1)
|
||||
expect no_warn
|
||||
{} 12.99603834169977
|
||||
|
||||
eval instant at 10m histogram_quantile(0.6, histogram_quantile_1)
|
||||
expect no_warn
|
||||
{} 4.594793419988138
|
||||
|
||||
eval instant at 10m histogram_quantile(0.5, histogram_quantile_1)
|
||||
expect no_warn
|
||||
{} 1.5874010519681994
|
||||
|
||||
# Linear interpolation within the zero bucket after all.
|
||||
eval instant at 10m histogram_quantile(0.1, histogram_quantile_1)
|
||||
expect no_warn
|
||||
{} 0.0006
|
||||
|
||||
eval instant at 10m histogram_quantile(0, histogram_quantile_1)
|
||||
expect no_warn
|
||||
{} 0
|
||||
|
||||
eval_warn instant at 10m histogram_quantile(-1, histogram_quantile_1)
|
||||
eval instant at 10m histogram_quantile(-1, histogram_quantile_1)
|
||||
expect warn
|
||||
{} -Inf
|
||||
|
||||
clear
|
||||
@ -435,31 +444,39 @@ clear
|
||||
load 10m
|
||||
histogram_quantile_2 {{schema:0 count:12 sum:100 z_bucket:2 z_bucket_w:0.001 n_buckets:[2 3 0 1 4]}}x1
|
||||
|
||||
eval_warn instant at 10m histogram_quantile(1.001, histogram_quantile_2)
|
||||
eval instant at 10m histogram_quantile(1.001, histogram_quantile_2)
|
||||
expect warn
|
||||
{} Inf
|
||||
|
||||
eval instant at 10m histogram_quantile(1, histogram_quantile_2)
|
||||
expect no_warn
|
||||
{} 0
|
||||
|
||||
# Again, the quantile values here are slightly different from what
|
||||
# they would be with linear interpolation. Note that quantiles
|
||||
# ending up in the zero bucket are linearly interpolated after all.
|
||||
eval instant at 10m histogram_quantile(0.99, histogram_quantile_2)
|
||||
expect no_warn
|
||||
{} -0.00006
|
||||
|
||||
eval instant at 10m histogram_quantile(0.9, histogram_quantile_2)
|
||||
expect no_warn
|
||||
{} -0.0006
|
||||
|
||||
eval instant at 10m histogram_quantile(0.5, histogram_quantile_2)
|
||||
expect no_warn
|
||||
{} -1.5874010519681996
|
||||
|
||||
eval instant at 10m histogram_quantile(0.1, histogram_quantile_2)
|
||||
expect no_warn
|
||||
{} -12.996038341699768
|
||||
|
||||
eval instant at 10m histogram_quantile(0, histogram_quantile_2)
|
||||
expect no_warn
|
||||
{} -16
|
||||
|
||||
eval_warn instant at 10m histogram_quantile(-1, histogram_quantile_2)
|
||||
eval instant at 10m histogram_quantile(-1, histogram_quantile_2)
|
||||
expect warn
|
||||
{} -Inf
|
||||
|
||||
clear
|
||||
@ -470,46 +487,59 @@ clear
|
||||
load 10m
|
||||
histogram_quantile_3 {{schema:0 count:24 sum:100 z_bucket:4 z_bucket_w:0.001 buckets:[2 3 0 1 4] n_buckets:[2 3 0 1 4]}}x1
|
||||
|
||||
eval_warn instant at 10m histogram_quantile(1.001, histogram_quantile_3)
|
||||
eval instant at 10m histogram_quantile(1.001, histogram_quantile_3)
|
||||
expect warn
|
||||
{} Inf
|
||||
|
||||
eval instant at 10m histogram_quantile(1, histogram_quantile_3)
|
||||
expect no_warn
|
||||
{} 16
|
||||
|
||||
eval instant at 10m histogram_quantile(0.99, histogram_quantile_3)
|
||||
expect no_warn
|
||||
{} 15.34822590920423
|
||||
|
||||
eval instant at 10m histogram_quantile(0.9, histogram_quantile_3)
|
||||
expect no_warn
|
||||
{} 10.556063286183155
|
||||
|
||||
eval instant at 10m histogram_quantile(0.7, histogram_quantile_3)
|
||||
expect no_warn
|
||||
{} 1.2030250360821164
|
||||
|
||||
# Linear interpolation in the zero bucket, symmetrically centered around
|
||||
# the zero point.
|
||||
eval instant at 10m histogram_quantile(0.55, histogram_quantile_3)
|
||||
expect no_warn
|
||||
{} 0.0006
|
||||
|
||||
eval instant at 10m histogram_quantile(0.5, histogram_quantile_3)
|
||||
expect no_warn
|
||||
{} 0
|
||||
|
||||
eval instant at 10m histogram_quantile(0.45, histogram_quantile_3)
|
||||
expect no_warn
|
||||
{} -0.0006
|
||||
|
||||
# Finally negative buckets with mirrored exponential interpolation.
|
||||
eval instant at 10m histogram_quantile(0.3, histogram_quantile_3)
|
||||
expect no_warn
|
||||
{} -1.2030250360821169
|
||||
|
||||
eval instant at 10m histogram_quantile(0.1, histogram_quantile_3)
|
||||
expect no_warn
|
||||
{} -10.556063286183155
|
||||
|
||||
eval instant at 10m histogram_quantile(0.01, histogram_quantile_3)
|
||||
expect no_warn
|
||||
{} -15.34822590920423
|
||||
|
||||
eval instant at 10m histogram_quantile(0, histogram_quantile_3)
|
||||
expect no_warn
|
||||
{} -16
|
||||
|
||||
eval_warn instant at 10m histogram_quantile(-1, histogram_quantile_3)
|
||||
eval instant at 10m histogram_quantile(-1, histogram_quantile_3)
|
||||
expect warn
|
||||
{} -Inf
|
||||
|
||||
clear
|
||||
@ -909,63 +939,84 @@ load 10m
|
||||
float_series_0 0+0x1
|
||||
|
||||
eval instant at 10m histogram_mul_div*3
|
||||
expect no_info
|
||||
{} {{schema:0 count:90 sum:99 z_bucket:9 z_bucket_w:0.001 buckets:[9 9 9] n_buckets:[18 18 18]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div*-1
|
||||
expect no_info
|
||||
{} {{schema:0 count:-30 sum:-33 z_bucket:-3 z_bucket_w:0.001 buckets:[-3 -3 -3] n_buckets:[-6 -6 -6]}}
|
||||
|
||||
eval instant at 10m -histogram_mul_div
|
||||
expect no_info
|
||||
{} {{schema:0 count:-30 sum:-33 z_bucket:-3 z_bucket_w:0.001 buckets:[-3 -3 -3] n_buckets:[-6 -6 -6]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div*-3
|
||||
expect no_info
|
||||
{} {{schema:0 count:-90 sum:-99 z_bucket:-9 z_bucket_w:0.001 buckets:[-9 -9 -9] n_buckets:[-18 -18 -18]}}
|
||||
|
||||
eval instant at 10m 3*histogram_mul_div
|
||||
expect no_info
|
||||
{} {{schema:0 count:90 sum:99 z_bucket:9 z_bucket_w:0.001 buckets:[9 9 9] n_buckets:[18 18 18]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div*float_series_3
|
||||
expect no_info
|
||||
{} {{schema:0 count:90 sum:99 z_bucket:9 z_bucket_w:0.001 buckets:[9 9 9] n_buckets:[18 18 18]}}
|
||||
|
||||
eval instant at 10m float_series_3*histogram_mul_div
|
||||
expect no_info
|
||||
{} {{schema:0 count:90 sum:99 z_bucket:9 z_bucket_w:0.001 buckets:[9 9 9] n_buckets:[18 18 18]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div/3
|
||||
expect no_info
|
||||
{} {{schema:0 count:10 sum:11 z_bucket:1 z_bucket_w:0.001 buckets:[1 1 1] n_buckets:[2 2 2]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div/-3
|
||||
expect no_info
|
||||
{} {{schema:0 count:-10 sum:-11 z_bucket:-1 z_bucket_w:0.001 buckets:[-1 -1 -1] n_buckets:[-2 -2 -2]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div/float_series_3
|
||||
expect no_info
|
||||
{} {{schema:0 count:10 sum:11 z_bucket:1 z_bucket_w:0.001 buckets:[1 1 1] n_buckets:[2 2 2]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div*0
|
||||
expect no_info
|
||||
{} {{schema:0 count:0 sum:0 z_bucket:0 z_bucket_w:0.001 buckets:[0 0 0] n_buckets:[0 0 0]}}
|
||||
|
||||
eval instant at 10m 0*histogram_mul_div
|
||||
expect no_info
|
||||
{} {{schema:0 count:0 sum:0 z_bucket:0 z_bucket_w:0.001 buckets:[0 0 0] n_buckets:[0 0 0]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div*float_series_0
|
||||
expect no_info
|
||||
{} {{schema:0 count:0 sum:0 z_bucket:0 z_bucket_w:0.001 buckets:[0 0 0] n_buckets:[0 0 0]}}
|
||||
|
||||
eval instant at 10m float_series_0*histogram_mul_div
|
||||
expect no_info
|
||||
{} {{schema:0 count:0 sum:0 z_bucket:0 z_bucket_w:0.001 buckets:[0 0 0] n_buckets:[0 0 0]}}
|
||||
|
||||
eval instant at 10m histogram_mul_div/0
|
||||
expect no_info
|
||||
{} {{schema:0 count:Inf sum:Inf z_bucket_w:0.001 z_bucket:Inf}}
|
||||
|
||||
eval instant at 10m histogram_mul_div/float_series_0
|
||||
expect no_info
|
||||
{} {{schema:0 count:Inf sum:Inf z_bucket_w:0.001 z_bucket:Inf}}
|
||||
|
||||
eval instant at 10m histogram_mul_div*0/0
|
||||
expect no_info
|
||||
{} {{schema:0 count:NaN sum:NaN z_bucket_w:0.001 z_bucket:NaN}}
|
||||
|
||||
eval_info instant at 10m histogram_mul_div*histogram_mul_div
|
||||
eval instant at 10m histogram_mul_div*histogram_mul_div
|
||||
expect info
|
||||
|
||||
eval_info instant at 10m histogram_mul_div/histogram_mul_div
|
||||
eval instant at 10m histogram_mul_div/histogram_mul_div
|
||||
expect info
|
||||
|
||||
eval_info instant at 10m float_series_3/histogram_mul_div
|
||||
eval instant at 10m float_series_3/histogram_mul_div
|
||||
expect info
|
||||
|
||||
eval_info instant at 10m 0/histogram_mul_div
|
||||
eval instant at 10m 0/histogram_mul_div
|
||||
expect info
|
||||
|
||||
clear
|
||||
|
||||
@ -976,13 +1027,17 @@ load 10m
|
||||
histogram_sample {{schema:0 count:24 sum:100 z_bucket:4 z_bucket_w:0.001 buckets:[2 3 0 1 4] n_buckets:[2 3 0 1 4]}}x1
|
||||
float_sample 0x1
|
||||
|
||||
eval_info instant at 10m float_sample+histogram_sample
|
||||
eval instant at 10m float_sample+histogram_sample
|
||||
expect info
|
||||
|
||||
eval_info instant at 10m histogram_sample+float_sample
|
||||
eval instant at 10m histogram_sample+float_sample
|
||||
expect info
|
||||
|
||||
eval_info instant at 10m float_sample-histogram_sample
|
||||
eval instant at 10m float_sample-histogram_sample
|
||||
expect info
|
||||
|
||||
eval_info instant at 10m histogram_sample-float_sample
|
||||
eval instant at 10m histogram_sample-float_sample
|
||||
expect info
|
||||
|
||||
# Counter reset only noticeable in a single bucket.
|
||||
load 5m
|
||||
@ -1020,11 +1075,13 @@ load 30s
|
||||
some_metric {{schema:0 sum:1 count:1 buckets:[1] counter_reset_hint:gauge}} {{schema:0 sum:2 count:2 buckets:[2] counter_reset_hint:gauge}} {{schema:0 sum:3 count:3 buckets:[3] counter_reset_hint:gauge}}
|
||||
|
||||
# Test the case where we only have two points for rate
|
||||
eval_warn instant at 30s rate(some_metric[1m])
|
||||
eval instant at 30s rate(some_metric[1m])
|
||||
expect warn
|
||||
{} {{count:0.03333333333333333 sum:0.03333333333333333 buckets:[0.03333333333333333]}}
|
||||
|
||||
# Test the case where we have more than two points for rate
|
||||
eval_warn instant at 1m rate(some_metric[1m30s])
|
||||
eval instant at 1m rate(some_metric[1m30s])
|
||||
expect warn
|
||||
{} {{count:0.03333333333333333 sum:0.03333333333333333 buckets:[0.03333333333333333]}}
|
||||
|
||||
clear
|
||||
@ -1034,18 +1091,24 @@ load 30s
|
||||
some_metric {{schema:0 sum:1 count:1 buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} {{schema:0 sum:5 count:4 buckets:[1 2 1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}}
|
||||
|
||||
# Start and end with exponential, with custom in the middle.
|
||||
eval_warn instant at 1m rate(some_metric[1m30s])
|
||||
eval instant at 1m rate(some_metric[1m30s])
|
||||
expect warn
|
||||
# Should produce no results.
|
||||
|
||||
# Start and end with custom, with exponential in the middle.
|
||||
eval_warn instant at 1m30s rate(some_metric[1m30s])
|
||||
eval instant at 1m30s rate(some_metric[1m30s])
|
||||
expect warn
|
||||
# Should produce no results.
|
||||
|
||||
# Start with custom, end with exponential. Return the exponential histogram divided by 30.
|
||||
# Start with custom, end with exponential. Return the exponential histogram divided by 48.
|
||||
# (The 1st sample is the NHCB with count:1. It is mostly ignored with the exception of the
|
||||
# count, which means the rate calculation extrapolates until the count hits 0.)
|
||||
eval instant at 1m rate(some_metric[1m])
|
||||
{} {{schema:0 sum:0.16666666666666666 count:0.13333333333333333 buckets:[0.03333333333333333 0.06666666666666667 0.03333333333333333]}}
|
||||
{} {{count:0.08333333333333333 sum:0.10416666666666666 counter_reset_hint:gauge buckets:[0.020833333333333332 0.041666666666666664 0.020833333333333332]}}
|
||||
|
||||
# Start with exponential, end with custom. Return the custom buckets histogram divided by 30.
|
||||
# (With the 2nd sample having a count of 1, the extrapolation to zero lands exactly at the
|
||||
# left boundary of the range, so no extrapolation limitation needed.)
|
||||
eval instant at 30s rate(some_metric[1m])
|
||||
{} {{schema:-53 sum:0.03333333333333333 count:0.03333333333333333 custom_values:[5 10] buckets:[0.03333333333333333]}}
|
||||
|
||||
@ -1107,10 +1170,12 @@ load 6m
|
||||
# T=0: only exponential
|
||||
# T=6: only custom
|
||||
# T=12: mixed, should be ignored and emit a warning
|
||||
eval_warn range from 0 to 12m step 6m sum(metric)
|
||||
eval range from 0 to 12m step 6m sum(metric)
|
||||
expect warn
|
||||
{} {{sum:7 count:5 buckets:[2 3 2]}} {{schema:-53 sum:16 count:3 custom_values:[5 10] buckets:[1 2]}} _
|
||||
|
||||
eval_warn range from 0 to 12m step 6m avg(metric)
|
||||
eval range from 0 to 12m step 6m avg(metric)
|
||||
expect warn
|
||||
{} {{sum:3.5 count:2.5 buckets:[1 1.5 1]}} {{schema:-53 sum:8 count:1.5 custom_values:[5 10] buckets:[0.5 1]}} _
|
||||
|
||||
clear
|
||||
@ -1124,10 +1189,12 @@ load 6m
|
||||
# T=0: incompatible, should be ignored and emit a warning
|
||||
# T=6: compatible
|
||||
# T=12: incompatible followed by compatible, should be ignored and emit a warning
|
||||
eval_warn range from 0 to 12m step 6m sum(metric)
|
||||
eval range from 0 to 12m step 6m sum(metric)
|
||||
expect warn
|
||||
{} _ {{schema:-53 sum:2 count:2 custom_values:[5 10] buckets:[2]}} _
|
||||
|
||||
eval_warn range from 0 to 12m step 6m avg(metric)
|
||||
eval range from 0 to 12m step 6m avg(metric)
|
||||
expect warn
|
||||
{} _ {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} _
|
||||
|
||||
# Test incompatible schemas with additional aggregation operators
|
||||
@ -1159,9 +1226,11 @@ eval range from 0 to 12m step 6m metric{series="1"} or ignoring(series) metric{s
|
||||
metric{series="2"} {{schema:-53 sum:1 count:1 custom_values:[2] buckets:[1]}} _ _
|
||||
|
||||
# Test incompatible schemas with arithmetic binary operators
|
||||
eval_warn range from 0 to 12m step 6m metric{series="2"} + ignoring (series) metric{series="3"}
|
||||
eval range from 0 to 12m step 6m metric{series="2"} + ignoring (series) metric{series="3"}
|
||||
expect warn
|
||||
|
||||
eval_warn range from 0 to 12m step 6m metric{series="2"} - ignoring (series) metric{series="3"}
|
||||
eval range from 0 to 12m step 6m metric{series="2"} - ignoring (series) metric{series="3"}
|
||||
expect warn
|
||||
|
||||
clear
|
||||
|
||||
@ -1171,12 +1240,15 @@ load 6m
|
||||
metric2 {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}}
|
||||
|
||||
eval range from 0 to 6m step 6m metric1 == metric2
|
||||
metric1{} _ {{schema:-53 count:1 sum:1 custom_values:[5 10] buckets:[1]}}
|
||||
expect no_info
|
||||
metric1{} _ {{schema:-53 count:1 sum:1 custom_values:[5 10] buckets:[1]}}
|
||||
|
||||
eval range from 0 to 6m step 6m metric1 != metric2
|
||||
metric1{} {{schema:-53 sum:1 count:1 custom_values:[2] buckets:[1]}} _
|
||||
expect no_info
|
||||
metric1{} {{schema:-53 sum:1 count:1 custom_values:[2] buckets:[1]}} _
|
||||
|
||||
eval_info range from 0 to 6m step 6m metric2 > metric2
|
||||
eval range from 0 to 6m step 6m metric2 > metric2
|
||||
expect info
|
||||
|
||||
clear
|
||||
|
||||
@ -1186,62 +1258,82 @@ load 6m
|
||||
# If evaluating at 12m, the first two NHCBs have the same custom values
|
||||
# while the 3rd one has different ones.
|
||||
|
||||
eval_warn instant at 12m sum_over_time(nhcb_metric[13m])
|
||||
eval instant at 12m sum_over_time(nhcb_metric[13m])
|
||||
expect warn
|
||||
|
||||
eval_warn instant at 12m avg_over_time(nhcb_metric[13m])
|
||||
eval instant at 12m avg_over_time(nhcb_metric[13m])
|
||||
expect warn
|
||||
|
||||
eval instant at 12m last_over_time(nhcb_metric[13m])
|
||||
nhcb_metric{} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}}
|
||||
expect no_warn
|
||||
nhcb_metric{} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}}
|
||||
|
||||
eval instant at 12m count_over_time(nhcb_metric[13m])
|
||||
{} 3
|
||||
expect no_warn
|
||||
{} 3
|
||||
|
||||
eval instant at 12m present_over_time(nhcb_metric[13m])
|
||||
{} 1
|
||||
expect no_warn
|
||||
{} 1
|
||||
|
||||
eval instant at 12m changes(nhcb_metric[13m])
|
||||
{} 1
|
||||
expect no_warn
|
||||
{} 1
|
||||
|
||||
eval_warn instant at 12m delta(nhcb_metric[13m])
|
||||
eval instant at 12m delta(nhcb_metric[13m])
|
||||
expect warn
|
||||
|
||||
eval_warn instant at 12m increase(nhcb_metric[13m])
|
||||
eval instant at 12m increase(nhcb_metric[13m])
|
||||
expect warn
|
||||
|
||||
eval_warn instant at 12m rate(nhcb_metric[13m])
|
||||
eval instant at 12m rate(nhcb_metric[13m])
|
||||
expect warn
|
||||
|
||||
eval instant at 12m resets(nhcb_metric[13m])
|
||||
{} 1
|
||||
expect no_warn
|
||||
{} 1
|
||||
|
||||
# Now doing the same again, but at 18m, where the first NHCB has
|
||||
# different custom_values compared to the other two. This now
|
||||
# works with no warning for increase() and rate(). No change
|
||||
# otherwise.
|
||||
|
||||
eval_warn instant at 18m sum_over_time(nhcb_metric[13m])
|
||||
eval instant at 18m sum_over_time(nhcb_metric[13m])
|
||||
expect warn
|
||||
|
||||
eval_warn instant at 18m avg_over_time(nhcb_metric[13m])
|
||||
eval instant at 18m avg_over_time(nhcb_metric[13m])
|
||||
expect warn
|
||||
|
||||
eval instant at 18m last_over_time(nhcb_metric[13m])
|
||||
nhcb_metric{} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}}
|
||||
expect no_warn
|
||||
nhcb_metric{} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}}
|
||||
|
||||
eval instant at 18m count_over_time(nhcb_metric[13m])
|
||||
{} 3
|
||||
expect no_warn
|
||||
{} 3
|
||||
|
||||
eval instant at 18m present_over_time(nhcb_metric[13m])
|
||||
{} 1
|
||||
expect no_warn
|
||||
{} 1
|
||||
|
||||
eval instant at 18m changes(nhcb_metric[13m])
|
||||
{} 1
|
||||
expect no_warn
|
||||
{} 1
|
||||
|
||||
eval_warn instant at 18m delta(nhcb_metric[13m])
|
||||
eval instant at 18m delta(nhcb_metric[13m])
|
||||
expect warn
|
||||
|
||||
eval instant at 18m increase(nhcb_metric[13m])
|
||||
{} {{schema:-53 count:1.0833333333333333 sum:1.0833333333333333 custom_values:[5 10] buckets:[1.0833333333333333]}}
|
||||
expect no_warn
|
||||
{} {{schema:-53 count:1.0833333333333333 sum:1.0833333333333333 custom_values:[5 10] buckets:[1.0833333333333333]}}
|
||||
|
||||
eval instant at 18m rate(nhcb_metric[13m])
|
||||
{} {{schema:-53 count:0.0013888888888888887 sum:0.0013888888888888887 custom_values:[5 10] buckets:[0.0013888888888888887]}}
|
||||
expect no_warn
|
||||
{} {{schema:-53 count:0.0013888888888888887 sum:0.0013888888888888887 custom_values:[5 10] buckets:[0.0013888888888888887]}}
|
||||
|
||||
eval instant at 18m resets(nhcb_metric[13m])
|
||||
{} 1
|
||||
expect no_warn
|
||||
{} 1
|
||||
|
||||
clear
|
||||
|
||||
@ -1259,7 +1351,8 @@ load 1m
|
||||
metric{group="incompatible-custom-histograms", series="1"} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}}
|
||||
metric{group="incompatible-custom-histograms", series="2"} {{schema:-53 sum:1 count:1 custom_values:[2] buckets:[1]}}
|
||||
|
||||
eval_warn instant at 0 sum by (group) (metric)
|
||||
eval instant at 0 sum by (group) (metric)
|
||||
expect warn
|
||||
{group="just-floats"} 5
|
||||
{group="just-exponential-histograms"} {{sum:5 count:7 buckets:[2 3 2]}}
|
||||
{group="just-custom-histograms"} {{schema:-53 sum:4 count:5 custom_values:[2] buckets:[8]}}
|
||||
@ -1275,17 +1368,22 @@ load 10m
|
||||
histogram_sum_float{idx="0"} 42.0x1
|
||||
|
||||
eval instant at 10m sum(histogram_sum)
|
||||
expect no_warn
|
||||
{} {{schema:0 count:107 sum:4691.2 z_bucket:14 z_bucket_w:0.001 buckets:[3 8 2 5 3 2 2] n_buckets:[2 6 8 4 15 9 0 0 0 10 10 4]}}
|
||||
|
||||
eval_warn instant at 10m sum({idx="0"})
|
||||
eval instant at 10m sum({idx="0"})
|
||||
expect warn
|
||||
|
||||
eval instant at 10m sum(histogram_sum{idx="0"} + ignoring(idx) histogram_sum{idx="1"} + ignoring(idx) histogram_sum{idx="2"} + ignoring(idx) histogram_sum{idx="3"})
|
||||
expect no_warn
|
||||
{} {{schema:0 count:107 sum:4691.2 z_bucket:14 z_bucket_w:0.001 buckets:[3 8 2 5 3 2 2] n_buckets:[2 6 8 4 15 9 0 0 0 10 10 4]}}
|
||||
|
||||
eval instant at 10m count(histogram_sum)
|
||||
expect no_warn
|
||||
{} 4
|
||||
|
||||
eval instant at 10m avg(histogram_sum)
|
||||
expect no_warn
|
||||
{} {{schema:0 count:26.75 sum:1172.8 z_bucket:3.5 z_bucket_w:0.001 buckets:[0.75 2 0.5 1.25 0.75 0.5 0.5] n_buckets:[0.5 1.5 2 1 3.75 2.25 0 0 0 2.5 2.5 1]}}
|
||||
|
||||
clear
|
||||
@ -1373,3 +1471,55 @@ eval instant at 1m histogram_fraction(-Inf, +Inf, histogram_nan)
|
||||
expect info msg: PromQL info: input to histogram_fraction has NaN observations, which are excluded from all fractions for metric name "histogram_nan"
|
||||
{case="100% NaNs"} 0.0
|
||||
{case="20% NaNs"} 0.8
|
||||
|
||||
clear
|
||||
|
||||
# Tests to demonstrate how an extrapolation below zero is prevented for both float counters and native counter histograms.
|
||||
# Note that the float counter behaves the same as the histogram count after `increase`.
|
||||
|
||||
load 1m
|
||||
metric{type="histogram"} {{schema:0 count:15 sum:25 buckets:[5 10]}} {{schema:0 count:2490 sum:75 buckets:[15 2475]}}x55
|
||||
metric{type="counter"} 15 2490x55
|
||||
|
||||
# End of range coincides with sample. Zero point of count is reached within the range.
|
||||
# Note that the 2nd bucket has an exaggerated increase of 2479.939393939394 (although
|
||||
# it has a value of only 2475 at the end of the range).
|
||||
eval instant at 55m increase(metric[90m])
|
||||
{type="histogram"} {{count:2490 sum:50.303030303030305 counter_reset_hint:gauge buckets:[10.06060606060606 2479.939393939394]}}
|
||||
{type="counter"} 2490
|
||||
|
||||
# End of range does not coincide with sample. Zero point of count is reached within the range.
|
||||
# The 2nd bucket again has an exaggerated increase, but it is less obvious because of the
|
||||
# right-side extrapolation.
|
||||
eval instant at 54m30s increase(metric[90m])
|
||||
{type="histogram"} {{count:2512.9166666666665 sum:50.76599326599326 counter_reset_hint:gauge buckets:[10.153198653198652 2502.7634680134674]}}
|
||||
{type="counter"} 2512.9166666666665
|
||||
|
||||
# End of range coincides with sample. Zero point of count is reached outside of (i.e. before) the range.
|
||||
# This means no change of extrapolation is required for the histogram count (and neither for the float counter),
|
||||
# however, the 2nd bucket's extrapolation will reach zero within the range. The overestimation is visible
|
||||
# easily here because the last sample in the range coincides with the boundary, where the 2nd bucket has
|
||||
# a value of 2475 but has increased by 2476.2045454545455 according to the returned result.
|
||||
eval instant at 55m increase(metric[55m15s])
|
||||
{type="histogram"} {{count:2486.25 sum:50.227272727272734 counter_reset_hint:gauge buckets:[10.045454545454547 2476.2045454545455]}}
|
||||
{type="counter"} 2486.25
|
||||
|
||||
# End of range does not coincide with sample. Zero point of count is reached outside of (i.e. before) the range.
|
||||
# This means no change of extrapolation is required for the histogram count (and neither for the float counter),
|
||||
# however, the 2nd bucket's extrapolation will reach zero within the range.
|
||||
eval instant at 54m30s increase(metric[54m45s])
|
||||
{type="histogram"} {{count:2509.375 sum:50.69444444444444 counter_reset_hint:gauge buckets:[10.13888888888889 2499.236111111111]}}
|
||||
{type="counter"} 2509.375
|
||||
|
||||
# Try the same, but now extract just the histogram count via `histogram_count`.
|
||||
eval instant at 55m histogram_count(increase(metric[90m]))
|
||||
{type="histogram"} 2490
|
||||
|
||||
eval instant at 54m30s histogram_count(increase(metric[90m]))
|
||||
{type="histogram"} 2512.9166666666665
|
||||
|
||||
eval instant at 55m histogram_count(increase(metric[55m15s]))
|
||||
{type="histogram"} 2486.25
|
||||
|
||||
eval instant at 54m30s histogram_count(increase(metric[54m45s]))
|
||||
{type="histogram"} 2509.375
|
||||
|
231
promql/promqltest/testdata/operators.test
vendored
231
promql/promqltest/testdata/operators.test
vendored
@ -289,24 +289,32 @@ eval instant at 50m http_requests_total{job="api-server", instance="0", group="p
|
||||
{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
|
||||
eval instant at 5m {job="app-server"} == 80
|
||||
expect info
|
||||
http_requests_total{group="canary", instance="1", job="app-server"} 80
|
||||
|
||||
eval_info instant at 5m http_requests_histogram != 80
|
||||
eval instant at 5m http_requests_histogram != 80
|
||||
expect info
|
||||
|
||||
eval_info instant at 5m http_requests_histogram > 80
|
||||
eval instant at 5m http_requests_histogram > 80
|
||||
expect info
|
||||
|
||||
eval_info instant at 5m http_requests_histogram < 80
|
||||
eval instant at 5m http_requests_histogram < 80
|
||||
expect info
|
||||
|
||||
eval_info instant at 5m http_requests_histogram >= 80
|
||||
eval instant at 5m http_requests_histogram >= 80
|
||||
expect info
|
||||
|
||||
eval_info instant at 5m http_requests_histogram <= 80
|
||||
eval instant at 5m http_requests_histogram <= 80
|
||||
expect info
|
||||
|
||||
# Should produce valid results in case of (in)equality between two histograms.
|
||||
eval instant at 5m http_requests_histogram == http_requests_histogram
|
||||
expect no_info
|
||||
http_requests_histogram{job="app-server", instance="1", group="production"} {{schema:1 sum:15 count:10 buckets:[3 2 5 7 9]}}
|
||||
|
||||
eval instant at 5m http_requests_histogram != http_requests_histogram
|
||||
expect no_info
|
||||
|
||||
# group_left/group_right.
|
||||
|
||||
@ -470,7 +478,8 @@ load 5m
|
||||
testmetric1{src="a",dst="b"} 0
|
||||
testmetric2{src="a",dst="b"} 1
|
||||
|
||||
eval_fail instant at 0m -{__name__=~'testmetric1|testmetric2'}
|
||||
eval instant at 0m -{__name__=~'testmetric1|testmetric2'}
|
||||
expect fail
|
||||
|
||||
clear
|
||||
|
||||
@ -520,290 +529,386 @@ load 6m
|
||||
right_floats_for_histograms 0 -1 2 3 4
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats == right_floats
|
||||
expect no_info
|
||||
left_floats _ _ _ _ 3 _ _ _ _ Inf -Inf
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats == bool right_floats
|
||||
expect no_info
|
||||
{} 0 _ _ _ 1 _ 0 0 0 1 1
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats == does_not_match
|
||||
expect no_info
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 24m step 6m left_histograms == right_histograms
|
||||
expect no_info
|
||||
left_histograms {{schema:3 sum:4 count:4 buckets:[1 2 1]}} _ _ _ _
|
||||
|
||||
eval range from 0 to 24m step 6m left_histograms == bool right_histograms
|
||||
expect no_info
|
||||
{} 1 0 _ _ _
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms == right_floats_for_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms == right_floats_for_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms == bool right_floats_for_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms == bool right_floats_for_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats != right_floats
|
||||
expect no_info
|
||||
left_floats 1 _ _ _ _ _ 4 5 NaN _ _
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats != bool right_floats
|
||||
expect no_info
|
||||
{} 1 _ _ _ 0 _ 1 1 1 0 0
|
||||
|
||||
eval range from 0 to 24m step 6m left_histograms != right_histograms
|
||||
expect no_info
|
||||
left_histograms _ {{schema:3 sum:4.5 count:5 buckets:[1 3 1]}} _ _ _
|
||||
|
||||
eval range from 0 to 24m step 6m left_histograms != bool right_histograms
|
||||
expect no_info
|
||||
{} 0 1 _ _ _
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms != right_floats_for_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms != right_floats_for_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms != bool right_floats_for_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms != bool right_floats_for_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats > right_floats
|
||||
expect no_info
|
||||
left_floats _ _ _ _ _ _ 4 _ _ _ _
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats > bool right_floats
|
||||
expect no_info
|
||||
{} 0 _ _ _ 0 _ 1 0 0 0 0
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms > right_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms > right_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms > bool right_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms > bool right_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms > right_floats_for_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms > right_floats_for_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms > bool right_floats_for_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms > bool right_floats_for_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats >= right_floats
|
||||
expect no_info
|
||||
left_floats _ _ _ _ 3 _ 4 _ _ Inf -Inf
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats >= bool right_floats
|
||||
expect no_info
|
||||
{} 0 _ _ _ 1 _ 1 0 0 1 1
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms >= right_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms >= right_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms >= bool right_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms >= bool right_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms >= right_floats_for_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms >= right_floats_for_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms >= bool right_floats_for_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms >= bool right_floats_for_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats < right_floats
|
||||
expect no_info
|
||||
left_floats 1 _ _ _ _ _ _ 5 _ _ _
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats < bool right_floats
|
||||
expect no_info
|
||||
{} 1 _ _ _ 0 _ 0 1 0 0 0
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms < right_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms < right_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms < bool right_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms < bool right_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms < right_floats_for_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms < right_floats_for_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms < bool right_floats_for_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms < bool right_floats_for_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats <= right_floats
|
||||
expect no_info
|
||||
left_floats 1 _ _ _ 3 _ _ 5 _ Inf -Inf
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats <= bool right_floats
|
||||
expect no_info
|
||||
{} 1 _ _ _ 1 _ 0 1 0 1 1
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms <= right_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms <= right_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms <= bool right_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms <= bool right_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms <= right_floats_for_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms <= right_floats_for_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms <= bool right_floats_for_histograms
|
||||
eval range from 0 to 24m step 6m left_histograms <= bool right_floats_for_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
# Vector / scalar combinations with scalar on right side
|
||||
eval range from 0 to 60m step 6m left_floats == 3
|
||||
expect no_info
|
||||
left_floats _ _ _ _ 3 _ _ _ _ _ _
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats != 3
|
||||
expect no_info
|
||||
left_floats 1 2 _ _ _ _ 4 5 NaN Inf -Inf
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats > 3
|
||||
expect no_info
|
||||
left_floats _ _ _ _ _ _ 4 5 _ Inf _
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats >= 3
|
||||
expect no_info
|
||||
left_floats _ _ _ _ 3 _ 4 5 _ Inf _
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats < 3
|
||||
expect no_info
|
||||
left_floats 1 2 _ _ _ _ _ _ _ _ -Inf
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats <= 3
|
||||
expect no_info
|
||||
left_floats 1 2 _ _ 3 _ _ _ _ _ -Inf
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats == bool 3
|
||||
expect no_info
|
||||
{} 0 0 _ _ 1 _ 0 0 0 0 0
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats == Inf
|
||||
expect no_info
|
||||
left_floats _ _ _ _ _ _ _ _ _ Inf _
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats == bool Inf
|
||||
expect no_info
|
||||
{} 0 0 _ _ 0 _ 0 0 0 1 0
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats == NaN
|
||||
expect no_info
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 60m step 6m left_floats == bool NaN
|
||||
expect no_info
|
||||
{} 0 0 _ _ 0 _ 0 0 0 0 0
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms == 3
|
||||
eval range from 0 to 24m step 6m left_histograms == 3
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms == 0
|
||||
eval range from 0 to 24m step 6m left_histograms == 0
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms != 3
|
||||
eval range from 0 to 24m step 6m left_histograms != 3
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms != 0
|
||||
eval range from 0 to 24m step 6m left_histograms != 0
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms > 3
|
||||
eval range from 0 to 24m step 6m left_histograms > 3
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms > 0
|
||||
eval range from 0 to 24m step 6m left_histograms > 0
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms >= 3
|
||||
eval range from 0 to 24m step 6m left_histograms >= 3
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms >= 0
|
||||
eval range from 0 to 24m step 6m left_histograms >= 0
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms < 3
|
||||
eval range from 0 to 24m step 6m left_histograms < 3
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms < 0
|
||||
eval range from 0 to 24m step 6m left_histograms < 0
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms <= 3
|
||||
eval range from 0 to 24m step 6m left_histograms <= 3
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms <= 0
|
||||
eval range from 0 to 24m step 6m left_histograms <= 0
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms == bool 3
|
||||
eval range from 0 to 24m step 6m left_histograms == bool 3
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms == bool 0
|
||||
eval range from 0 to 24m step 6m left_histograms == bool 0
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms != bool 3
|
||||
eval range from 0 to 24m step 6m left_histograms != bool 3
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms != bool 0
|
||||
eval range from 0 to 24m step 6m left_histograms != bool 0
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms > bool 3
|
||||
eval range from 0 to 24m step 6m left_histograms > bool 3
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms > bool 0
|
||||
eval range from 0 to 24m step 6m left_histograms > bool 0
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms >= bool 3
|
||||
eval range from 0 to 24m step 6m left_histograms >= bool 3
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms >= bool 0
|
||||
eval range from 0 to 24m step 6m left_histograms >= bool 0
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms < bool 3
|
||||
eval range from 0 to 24m step 6m left_histograms < bool 3
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms < bool 0
|
||||
eval range from 0 to 24m step 6m left_histograms < bool 0
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms <= bool 3
|
||||
eval range from 0 to 24m step 6m left_histograms <= bool 3
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m left_histograms <= bool 0
|
||||
eval range from 0 to 24m step 6m left_histograms <= bool 0
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
# Vector / scalar combinations with scalar on left side
|
||||
eval range from 0 to 60m step 6m 3 == left_floats
|
||||
expect no_info
|
||||
left_floats _ _ _ _ 3 _ _ _ _ _ _
|
||||
|
||||
eval range from 0 to 60m step 6m 3 != left_floats
|
||||
expect no_info
|
||||
left_floats 1 2 _ _ _ _ 4 5 NaN Inf -Inf
|
||||
|
||||
eval range from 0 to 60m step 6m 3 < left_floats
|
||||
expect no_info
|
||||
left_floats _ _ _ _ _ _ 4 5 _ Inf _
|
||||
|
||||
eval range from 0 to 60m step 6m 3 <= left_floats
|
||||
expect no_info
|
||||
left_floats _ _ _ _ 3 _ 4 5 _ Inf _
|
||||
|
||||
eval range from 0 to 60m step 6m 3 > left_floats
|
||||
expect no_info
|
||||
left_floats 1 2 _ _ _ _ _ _ _ _ -Inf
|
||||
|
||||
eval range from 0 to 60m step 6m 3 >= left_floats
|
||||
expect no_info
|
||||
left_floats 1 2 _ _ 3 _ _ _ _ _ -Inf
|
||||
|
||||
eval range from 0 to 60m step 6m 3 == bool left_floats
|
||||
expect no_info
|
||||
{} 0 0 _ _ 1 _ 0 0 0 0 0
|
||||
|
||||
eval range from 0 to 60m step 6m Inf == left_floats
|
||||
expect no_info
|
||||
left_floats _ _ _ _ _ _ _ _ _ Inf _
|
||||
|
||||
eval range from 0 to 60m step 6m Inf == bool left_floats
|
||||
expect no_info
|
||||
{} 0 0 _ _ 0 _ 0 0 0 1 0
|
||||
|
||||
eval range from 0 to 60m step 6m NaN == left_floats
|
||||
expect no_info
|
||||
expect no_warn
|
||||
# No results.
|
||||
|
||||
eval range from 0 to 60m step 6m NaN == bool left_floats
|
||||
expect no_info
|
||||
{} 0 0 _ _ 0 _ 0 0 0 0 0
|
||||
|
||||
eval_info range from 0 to 24m step 6m 3 == left_histograms
|
||||
eval range from 0 to 24m step 6m 3 == left_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m 0 == left_histograms
|
||||
eval range from 0 to 24m step 6m 0 == left_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m 3 != left_histograms
|
||||
eval range from 0 to 24m step 6m 3 != left_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m 0 != left_histograms
|
||||
eval range from 0 to 24m step 6m 0 != left_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m 3 < left_histograms
|
||||
eval range from 0 to 24m step 6m 3 < left_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m 0 < left_histograms
|
||||
eval range from 0 to 24m step 6m 0 < left_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m 3 < left_histograms
|
||||
eval range from 0 to 24m step 6m 3 < left_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m 0 < left_histograms
|
||||
eval range from 0 to 24m step 6m 0 < left_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m 3 > left_histograms
|
||||
eval range from 0 to 24m step 6m 3 > left_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m 0 > left_histograms
|
||||
eval range from 0 to 24m step 6m 0 > left_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m 3 >= left_histograms
|
||||
eval range from 0 to 24m step 6m 3 >= left_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
eval_info range from 0 to 24m step 6m 0 >= left_histograms
|
||||
eval range from 0 to 24m step 6m 0 >= left_histograms
|
||||
expect info
|
||||
# No results.
|
||||
|
||||
clear
|
||||
|
2
promql/promqltest/testdata/subquery.test
vendored
2
promql/promqltest/testdata/subquery.test
vendored
@ -156,4 +156,6 @@ load 5m
|
||||
foo 3+0x10
|
||||
|
||||
eval instant at 12m min_over_time((topk(1, foo))[1m:5m])
|
||||
expect no_info
|
||||
expect no_warn
|
||||
#empty
|
||||
|
@ -127,7 +127,10 @@ func (m *Manager) Run(tsets <-chan map[string][]*targetgroup.Group) error {
|
||||
go m.reloader()
|
||||
for {
|
||||
select {
|
||||
case ts := <-tsets:
|
||||
case ts, ok := <-tsets:
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
m.updateTsets(ts)
|
||||
|
||||
select {
|
||||
|
@ -149,12 +149,8 @@ func newScrapePool(cfg *config.ScrapeConfig, app storage.Appendable, offsetSeed
|
||||
return nil, fmt.Errorf("error creating HTTP client: %w", err)
|
||||
}
|
||||
|
||||
validationScheme, err := config.ToValidationScheme(cfg.MetricNameValidationScheme)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid metric name validation scheme: %w", err)
|
||||
}
|
||||
var escapingScheme model.EscapingScheme
|
||||
escapingScheme, err = config.ToEscapingScheme(cfg.MetricNameEscapingScheme, validationScheme)
|
||||
escapingScheme, err = config.ToEscapingScheme(cfg.MetricNameEscapingScheme, cfg.MetricNameValidationScheme)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid metric name escaping scheme, %w", err)
|
||||
}
|
||||
@ -172,7 +168,7 @@ func newScrapePool(cfg *config.ScrapeConfig, app storage.Appendable, offsetSeed
|
||||
logger: logger,
|
||||
metrics: metrics,
|
||||
httpOpts: options.HTTPClientOptions,
|
||||
validationScheme: validationScheme,
|
||||
validationScheme: cfg.MetricNameValidationScheme,
|
||||
escapingScheme: escapingScheme,
|
||||
}
|
||||
sp.newLoop = func(opts scrapeLoopOptions) loop {
|
||||
@ -325,11 +321,7 @@ func (sp *scrapePool) reload(cfg *config.ScrapeConfig) error {
|
||||
sp.config = cfg
|
||||
oldClient := sp.client
|
||||
sp.client = client
|
||||
validationScheme, err := config.ToValidationScheme(cfg.MetricNameValidationScheme)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid metric name validation scheme: %w", err)
|
||||
}
|
||||
sp.validationScheme = validationScheme
|
||||
sp.validationScheme = cfg.MetricNameValidationScheme
|
||||
var escapingScheme model.EscapingScheme
|
||||
escapingScheme, err = model.ToEscapingScheme(cfg.MetricNameEscapingScheme)
|
||||
if err != nil {
|
||||
|
@ -84,7 +84,7 @@ func TestNewScrapePool(t *testing.T) {
|
||||
var (
|
||||
app = &nopAppendable{}
|
||||
cfg = &config.ScrapeConfig{
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
sp, err = newScrapePool(cfg, app, 0, nil, nil, &Options{}, newTestScrapeMetrics(t))
|
||||
@ -327,7 +327,7 @@ func TestDroppedTargetsList(t *testing.T) {
|
||||
cfg = &config.ScrapeConfig{
|
||||
JobName: "dropMe",
|
||||
ScrapeInterval: model.Duration(1),
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
RelabelConfigs: []*relabel.Config{
|
||||
{
|
||||
@ -374,7 +374,7 @@ func TestDiscoveredLabelsUpdate(t *testing.T) {
|
||||
sp.config = &config.ScrapeConfig{
|
||||
ScrapeInterval: model.Duration(1),
|
||||
ScrapeTimeout: model.Duration(1),
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
sp.activeTargets = make(map[uint64]*Target)
|
||||
@ -506,7 +506,7 @@ func TestScrapePoolReload(t *testing.T) {
|
||||
reloadCfg := &config.ScrapeConfig{
|
||||
ScrapeInterval: model.Duration(3 * time.Second),
|
||||
ScrapeTimeout: model.Duration(2 * time.Second),
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
// On starting to run, new loops created on reload check whether their preceding
|
||||
@ -600,7 +600,7 @@ func TestScrapePoolReloadPreserveRelabeledIntervalTimeout(t *testing.T) {
|
||||
reloadCfg := &config.ScrapeConfig{
|
||||
ScrapeInterval: model.Duration(3 * time.Second),
|
||||
ScrapeTimeout: model.Duration(2 * time.Second),
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
newLoop := func(opts scrapeLoopOptions) loop {
|
||||
@ -701,7 +701,7 @@ func TestScrapePoolTargetLimit(t *testing.T) {
|
||||
require.NoError(t, sp.reload(&config.ScrapeConfig{
|
||||
ScrapeInterval: model.Duration(3 * time.Second),
|
||||
ScrapeTimeout: model.Duration(2 * time.Second),
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
TargetLimit: l,
|
||||
}))
|
||||
@ -791,7 +791,7 @@ func TestScrapePoolTargetLimit(t *testing.T) {
|
||||
|
||||
func TestScrapePoolAppender(t *testing.T) {
|
||||
cfg := &config.ScrapeConfig{
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
app := &nopAppendable{}
|
||||
@ -869,7 +869,7 @@ func TestScrapePoolRaces(t *testing.T) {
|
||||
return &config.ScrapeConfig{
|
||||
ScrapeInterval: interval,
|
||||
ScrapeTimeout: timeout,
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
}
|
||||
@ -943,7 +943,7 @@ func TestScrapePoolScrapeLoopsStarted(t *testing.T) {
|
||||
require.NoError(t, sp.reload(&config.ScrapeConfig{
|
||||
ScrapeInterval: model.Duration(3 * time.Second),
|
||||
ScrapeTimeout: model.Duration(2 * time.Second),
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}))
|
||||
sp.Sync(tgs)
|
||||
@ -3621,14 +3621,14 @@ func TestReuseScrapeCache(t *testing.T) {
|
||||
ScrapeTimeout: model.Duration(5 * time.Second),
|
||||
ScrapeInterval: model.Duration(5 * time.Second),
|
||||
MetricsPath: "/metrics",
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
sp, _ = newScrapePool(cfg, app, 0, nil, nil, &Options{}, newTestScrapeMetrics(t))
|
||||
t1 = &Target{
|
||||
labels: labels.FromStrings("labelNew", "nameNew", "labelNew1", "nameNew1", "labelNew2", "nameNew2"),
|
||||
scrapeConfig: &config.ScrapeConfig{
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
},
|
||||
}
|
||||
@ -3648,7 +3648,7 @@ func TestReuseScrapeCache(t *testing.T) {
|
||||
ScrapeInterval: model.Duration(5 * time.Second),
|
||||
ScrapeTimeout: model.Duration(5 * time.Second),
|
||||
MetricsPath: "/metrics",
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
},
|
||||
},
|
||||
@ -3659,7 +3659,7 @@ func TestReuseScrapeCache(t *testing.T) {
|
||||
ScrapeInterval: model.Duration(5 * time.Second),
|
||||
ScrapeTimeout: model.Duration(15 * time.Second),
|
||||
MetricsPath: "/metrics2",
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
},
|
||||
},
|
||||
@ -3671,7 +3671,7 @@ func TestReuseScrapeCache(t *testing.T) {
|
||||
ScrapeInterval: model.Duration(5 * time.Second),
|
||||
ScrapeTimeout: model.Duration(15 * time.Second),
|
||||
MetricsPath: "/metrics2",
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
},
|
||||
},
|
||||
@ -3684,7 +3684,7 @@ func TestReuseScrapeCache(t *testing.T) {
|
||||
ScrapeInterval: model.Duration(5 * time.Second),
|
||||
ScrapeTimeout: model.Duration(15 * time.Second),
|
||||
MetricsPath: "/metrics2",
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
},
|
||||
},
|
||||
@ -3700,7 +3700,7 @@ func TestReuseScrapeCache(t *testing.T) {
|
||||
ScrapeInterval: model.Duration(5 * time.Second),
|
||||
ScrapeTimeout: model.Duration(15 * time.Second),
|
||||
MetricsPath: "/metrics2",
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
},
|
||||
},
|
||||
@ -3714,7 +3714,7 @@ func TestReuseScrapeCache(t *testing.T) {
|
||||
ScrapeInterval: model.Duration(5 * time.Second),
|
||||
ScrapeTimeout: model.Duration(15 * time.Second),
|
||||
MetricsPath: "/metrics2",
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
},
|
||||
},
|
||||
@ -3726,7 +3726,7 @@ func TestReuseScrapeCache(t *testing.T) {
|
||||
ScrapeTimeout: model.Duration(15 * time.Second),
|
||||
MetricsPath: "/metrics",
|
||||
LabelLimit: 1,
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
},
|
||||
},
|
||||
@ -3738,7 +3738,7 @@ func TestReuseScrapeCache(t *testing.T) {
|
||||
ScrapeTimeout: model.Duration(15 * time.Second),
|
||||
MetricsPath: "/metrics",
|
||||
LabelLimit: 15,
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
},
|
||||
},
|
||||
@ -3751,7 +3751,7 @@ func TestReuseScrapeCache(t *testing.T) {
|
||||
MetricsPath: "/metrics",
|
||||
LabelLimit: 15,
|
||||
LabelNameLengthLimit: 5,
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
},
|
||||
},
|
||||
@ -3765,7 +3765,7 @@ func TestReuseScrapeCache(t *testing.T) {
|
||||
LabelLimit: 15,
|
||||
LabelNameLengthLimit: 5,
|
||||
LabelValueLengthLimit: 7,
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
},
|
||||
},
|
||||
@ -3830,7 +3830,7 @@ func TestReuseCacheRace(t *testing.T) {
|
||||
ScrapeTimeout: model.Duration(5 * time.Second),
|
||||
ScrapeInterval: model.Duration(5 * time.Second),
|
||||
MetricsPath: "/metrics",
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
buffers = pool.New(1e3, 100e6, 3, func(sz int) interface{} { return make([]byte, 0, sz) })
|
||||
@ -3854,7 +3854,7 @@ func TestReuseCacheRace(t *testing.T) {
|
||||
ScrapeInterval: model.Duration(1 * time.Millisecond),
|
||||
MetricsPath: "/metrics",
|
||||
SampleLimit: i,
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
})
|
||||
}
|
||||
@ -3932,7 +3932,7 @@ func TestScrapeReportLimit(t *testing.T) {
|
||||
Scheme: "http",
|
||||
ScrapeInterval: model.Duration(100 * time.Millisecond),
|
||||
ScrapeTimeout: model.Duration(100 * time.Millisecond),
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
|
||||
@ -3988,7 +3988,7 @@ func TestScrapeUTF8(t *testing.T) {
|
||||
Scheme: "http",
|
||||
ScrapeInterval: model.Duration(100 * time.Millisecond),
|
||||
ScrapeTimeout: model.Duration(100 * time.Millisecond),
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
ts, scrapedTwice := newScrapableServer("{\"with.dots\"} 42\n")
|
||||
@ -4124,7 +4124,7 @@ func TestTargetScrapeIntervalAndTimeoutRelabel(t *testing.T) {
|
||||
config := &config.ScrapeConfig{
|
||||
ScrapeInterval: interval,
|
||||
ScrapeTimeout: timeout,
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
RelabelConfigs: []*relabel.Config{
|
||||
{
|
||||
@ -4186,7 +4186,7 @@ func TestLeQuantileReLabel(t *testing.T) {
|
||||
Scheme: "http",
|
||||
ScrapeInterval: model.Duration(100 * time.Millisecond),
|
||||
ScrapeTimeout: model.Duration(100 * time.Millisecond),
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
|
||||
@ -4695,10 +4695,10 @@ metric: <
|
||||
SampleLimit: 100,
|
||||
Scheme: "http",
|
||||
ScrapeInterval: model.Duration(50 * time.Millisecond),
|
||||
ScrapeTimeout: model.Duration(25 * time.Millisecond),
|
||||
ScrapeTimeout: model.Duration(49 * time.Millisecond),
|
||||
AlwaysScrapeClassicHistograms: tc.alwaysScrapeClassicHistograms,
|
||||
ConvertClassicHistogramsToNHCB: tc.convertClassicHistToNHCB,
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
|
||||
@ -4747,7 +4747,7 @@ metric: <
|
||||
Targets: []model.LabelSet{{model.AddressLabel: model.LabelValue(testURL.Host)}},
|
||||
},
|
||||
})
|
||||
require.Len(t, sp.ActiveTargets(), 1)
|
||||
require.Eventually(t, func() bool { return len(sp.ActiveTargets()) == 1 }, 5*time.Second, 50*time.Millisecond)
|
||||
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
@ -4826,7 +4826,7 @@ func TestTypeUnitReLabel(t *testing.T) {
|
||||
Scheme: "http",
|
||||
ScrapeInterval: model.Duration(100 * time.Millisecond),
|
||||
ScrapeTimeout: model.Duration(100 * time.Millisecond),
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
|
||||
@ -4967,7 +4967,7 @@ func TestScrapeLoopCompression(t *testing.T) {
|
||||
ScrapeInterval: model.Duration(100 * time.Millisecond),
|
||||
ScrapeTimeout: model.Duration(100 * time.Millisecond),
|
||||
EnableCompression: tc.enableCompression,
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
}
|
||||
|
||||
@ -5123,7 +5123,7 @@ func BenchmarkTargetScraperGzip(b *testing.B) {
|
||||
model.AddressLabel, serverURL.Host,
|
||||
),
|
||||
scrapeConfig: &config.ScrapeConfig{
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
Params: url.Values{"count": []string{strconv.Itoa(scenario.metricsCount)}},
|
||||
},
|
||||
@ -5379,7 +5379,7 @@ func TestTargetScrapeConfigWithLabels(t *testing.T) {
|
||||
JobName: jobName,
|
||||
Scheme: httpScheme,
|
||||
MetricsPath: expectedPath,
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
},
|
||||
targets: []*targetgroup.Group{
|
||||
@ -5398,7 +5398,7 @@ func TestTargetScrapeConfigWithLabels(t *testing.T) {
|
||||
JobName: jobName,
|
||||
Scheme: httpScheme,
|
||||
MetricsPath: secondPath,
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
Params: url.Values{"param": []string{secondParam}},
|
||||
},
|
||||
@ -5423,7 +5423,7 @@ func TestTargetScrapeConfigWithLabels(t *testing.T) {
|
||||
JobName: jobName,
|
||||
Scheme: httpScheme,
|
||||
MetricsPath: secondPath,
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
Params: url.Values{"param": []string{secondParam}},
|
||||
RelabelConfigs: []*relabel.Config{
|
||||
@ -5504,7 +5504,7 @@ func TestScrapePoolScrapeAfterReload(t *testing.T) {
|
||||
Scheme: "http",
|
||||
ScrapeInterval: model.Duration(100 * time.Millisecond),
|
||||
ScrapeTimeout: model.Duration(100 * time.Millisecond),
|
||||
MetricNameValidationScheme: config.UTF8ValidationConfig,
|
||||
MetricNameValidationScheme: model.UTF8Validation,
|
||||
MetricNameEscapingScheme: model.AllowUTF8,
|
||||
EnableCompression: false,
|
||||
ServiceDiscoveryConfigs: discovery.Configs{
|
||||
|
@ -25,15 +25,17 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
- name: Install snmp_exporter/generator dependencies
|
||||
run: sudo apt-get update && sudo apt-get -y install libsnmp-dev
|
||||
if: github.repository == 'prometheus/snmp_exporter'
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v7.0.0
|
||||
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
|
||||
with:
|
||||
args: --verbose
|
||||
version: v2.1.5
|
||||
version: v2.2.1
|
||||
|
@ -223,6 +223,19 @@ type SelectHints struct {
|
||||
// When disabled, the result may contain samples outside the queried time range but Select() performances
|
||||
// may be improved.
|
||||
DisableTrimming bool
|
||||
|
||||
// Projection hints. They are currently unused in the Prometheus promql engine but can be used by different
|
||||
// implementations of the Queryable interface and engines.
|
||||
// These hints are useful for queries like `sum by (label) (rate(metric[5m]))` - we can safely evaluate it
|
||||
// even if we only fetch the `label` label. For some storage implementations this is beneficial.
|
||||
|
||||
// ProjectionLabels are the minimum amount of labels required to be fetched for this Select call
|
||||
// When honored it is required to add an __series_hash__ label containing the hash of all labels
|
||||
// of a particular series so that the engine can still perform horizontal joins.
|
||||
ProjectionLabels []string
|
||||
|
||||
// ProjectionInclude defines if we have to include or exclude the labels from the ProjectLabels field.
|
||||
ProjectionInclude bool
|
||||
}
|
||||
|
||||
// LabelHints specifies hints passed for label reads.
|
||||
|
@ -547,8 +547,9 @@ type chunkedSeriesSet struct {
|
||||
mint, maxt int64
|
||||
cancel func(error)
|
||||
|
||||
current storage.Series
|
||||
err error
|
||||
current storage.Series
|
||||
err error
|
||||
exhausted bool
|
||||
}
|
||||
|
||||
func NewChunkedSeriesSet(chunkedReader *ChunkedReader, respBody io.ReadCloser, mint, maxt int64, cancel func(error)) storage.SeriesSet {
|
||||
@ -564,6 +565,12 @@ func NewChunkedSeriesSet(chunkedReader *ChunkedReader, respBody io.ReadCloser, m
|
||||
// Next return true if there is a next series and false otherwise. It will
|
||||
// block until the next series is available.
|
||||
func (s *chunkedSeriesSet) Next() bool {
|
||||
if s.exhausted {
|
||||
// Don't try to read the next series again.
|
||||
// This prevents errors like "http: read on closed response body" if Next() is called after it has already returned false.
|
||||
return false
|
||||
}
|
||||
|
||||
res := &prompb.ChunkedReadResponse{}
|
||||
|
||||
err := s.chunkedReader.NextProto(res)
|
||||
@ -575,6 +582,7 @@ func (s *chunkedSeriesSet) Next() bool {
|
||||
|
||||
_ = s.respBody.Close()
|
||||
s.cancel(err)
|
||||
s.exhausted = true
|
||||
|
||||
return false
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ package remote
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
@ -892,7 +893,8 @@ func TestChunkedSeriesSet(t *testing.T) {
|
||||
flusher := &mockFlusher{}
|
||||
|
||||
w := NewChunkedWriter(buf, flusher)
|
||||
r := NewChunkedReader(buf, config.DefaultChunkedReadLimit, nil)
|
||||
wrappedReader := newOneShotCloser(buf)
|
||||
r := NewChunkedReader(wrappedReader, config.DefaultChunkedReadLimit, nil)
|
||||
|
||||
chks := buildTestChunks(t)
|
||||
l := []prompb.Label{
|
||||
@ -913,7 +915,7 @@ func TestChunkedSeriesSet(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
ss := NewChunkedSeriesSet(r, io.NopCloser(buf), 0, 14000, func(error) {})
|
||||
ss := NewChunkedSeriesSet(r, wrappedReader, 0, 14000, func(error) {})
|
||||
require.NoError(t, ss.Err())
|
||||
require.Nil(t, ss.Warnings())
|
||||
|
||||
@ -938,6 +940,9 @@ func TestChunkedSeriesSet(t *testing.T) {
|
||||
}
|
||||
require.Equal(t, numTestChunks, numResponses)
|
||||
require.NoError(t, ss.Err())
|
||||
|
||||
require.False(t, ss.Next(), "Next() should still return false after it previously returned false")
|
||||
require.NoError(t, ss.Err(), "Err() should not return an error if Next() is called again after it previously returned false")
|
||||
})
|
||||
|
||||
t.Run("chunked reader error", func(t *testing.T) {
|
||||
@ -983,6 +988,32 @@ type mockFlusher struct{}
|
||||
|
||||
func (f *mockFlusher) Flush() {}
|
||||
|
||||
type oneShotCloser struct {
|
||||
r io.Reader
|
||||
closed bool
|
||||
}
|
||||
|
||||
func newOneShotCloser(r io.Reader) io.ReadCloser {
|
||||
return &oneShotCloser{r, false}
|
||||
}
|
||||
|
||||
func (c *oneShotCloser) Read(p []byte) (n int, err error) {
|
||||
if c.closed {
|
||||
return 0, errors.New("already closed")
|
||||
}
|
||||
|
||||
return c.r.Read(p)
|
||||
}
|
||||
|
||||
func (c *oneShotCloser) Close() error {
|
||||
if c.closed {
|
||||
return errors.New("already closed")
|
||||
}
|
||||
|
||||
c.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
numTestChunks = 3
|
||||
numSamplesPerTestChunk = 5
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
@ -53,10 +54,11 @@ const (
|
||||
maxExemplarRunes = 128
|
||||
// Trace and Span id keys are defined as part of the spec:
|
||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification%2Fmetrics%2Fdatamodel.md#exemplars-2
|
||||
traceIDKey = "trace_id"
|
||||
spanIDKey = "span_id"
|
||||
infoType = "info"
|
||||
targetMetricName = "target_info"
|
||||
traceIDKey = "trace_id"
|
||||
spanIDKey = "span_id"
|
||||
infoType = "info"
|
||||
targetMetricName = "target_info"
|
||||
defaultLookbackDelta = 5 * time.Minute
|
||||
)
|
||||
|
||||
type bucketBoundsData struct {
|
||||
@ -149,11 +151,9 @@ func createAttributes(resource pcommon.Resource, attributes pcommon.Map, setting
|
||||
|
||||
// map ensures no duplicate label names.
|
||||
l := make(map[string]string, maxLabelCount)
|
||||
labelNamer := otlptranslator.LabelNamer{UTF8Allowed: settings.AllowUTF8}
|
||||
for _, label := range labels {
|
||||
finalKey := label.Name
|
||||
if !settings.AllowUTF8 {
|
||||
finalKey = otlptranslator.NormalizeLabel(finalKey)
|
||||
}
|
||||
finalKey := labelNamer.Build(label.Name)
|
||||
if existingValue, alreadyExists := l[finalKey]; alreadyExists {
|
||||
l[finalKey] = existingValue + ";" + label.Value
|
||||
} else {
|
||||
@ -162,10 +162,7 @@ func createAttributes(resource pcommon.Resource, attributes pcommon.Map, setting
|
||||
}
|
||||
|
||||
for _, lbl := range promotedAttrs {
|
||||
normalized := lbl.Name
|
||||
if !settings.AllowUTF8 {
|
||||
normalized = otlptranslator.NormalizeLabel(normalized)
|
||||
}
|
||||
normalized := labelNamer.Build(lbl.Name)
|
||||
if _, exists := l[normalized]; !exists {
|
||||
l[normalized] = lbl.Value
|
||||
}
|
||||
@ -203,8 +200,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 !settings.AllowUTF8 && (len(name) <= 4 || name[:2] != "__" || name[len(name)-2:] != "__") {
|
||||
name = otlptranslator.NormalizeLabel(name)
|
||||
if len(name) <= 4 || name[:2] != "__" || name[len(name)-2:] != "__" {
|
||||
name = labelNamer.Build(name)
|
||||
}
|
||||
l[name] = extras[i+1]
|
||||
}
|
||||
@ -404,39 +401,49 @@ func getPromExemplars[T exemplarType](ctx context.Context, everyN *everyNTimes,
|
||||
return promExemplars, nil
|
||||
}
|
||||
|
||||
// mostRecentTimestampInMetric returns the latest timestamp in a batch of metrics.
|
||||
func mostRecentTimestampInMetric(metric pmetric.Metric) pcommon.Timestamp {
|
||||
var ts pcommon.Timestamp
|
||||
// findMinAndMaxTimestamps returns the minimum of minTimestamp and the earliest timestamp in metric and
|
||||
// the maximum of maxTimestamp and the latest timestamp in metric, respectively.
|
||||
func findMinAndMaxTimestamps(metric pmetric.Metric, minTimestamp, maxTimestamp pcommon.Timestamp) (pcommon.Timestamp, pcommon.Timestamp) {
|
||||
// handle individual metric based on type
|
||||
//exhaustive:enforce
|
||||
switch metric.Type() {
|
||||
case pmetric.MetricTypeGauge:
|
||||
dataPoints := metric.Gauge().DataPoints()
|
||||
for x := 0; x < dataPoints.Len(); x++ {
|
||||
ts = max(ts, dataPoints.At(x).Timestamp())
|
||||
ts := dataPoints.At(x).Timestamp()
|
||||
minTimestamp = min(minTimestamp, ts)
|
||||
maxTimestamp = max(maxTimestamp, ts)
|
||||
}
|
||||
case pmetric.MetricTypeSum:
|
||||
dataPoints := metric.Sum().DataPoints()
|
||||
for x := 0; x < dataPoints.Len(); x++ {
|
||||
ts = max(ts, dataPoints.At(x).Timestamp())
|
||||
ts := dataPoints.At(x).Timestamp()
|
||||
minTimestamp = min(minTimestamp, ts)
|
||||
maxTimestamp = max(maxTimestamp, ts)
|
||||
}
|
||||
case pmetric.MetricTypeHistogram:
|
||||
dataPoints := metric.Histogram().DataPoints()
|
||||
for x := 0; x < dataPoints.Len(); x++ {
|
||||
ts = max(ts, dataPoints.At(x).Timestamp())
|
||||
ts := dataPoints.At(x).Timestamp()
|
||||
minTimestamp = min(minTimestamp, ts)
|
||||
maxTimestamp = max(maxTimestamp, ts)
|
||||
}
|
||||
case pmetric.MetricTypeExponentialHistogram:
|
||||
dataPoints := metric.ExponentialHistogram().DataPoints()
|
||||
for x := 0; x < dataPoints.Len(); x++ {
|
||||
ts = max(ts, dataPoints.At(x).Timestamp())
|
||||
ts := dataPoints.At(x).Timestamp()
|
||||
minTimestamp = min(minTimestamp, ts)
|
||||
maxTimestamp = max(maxTimestamp, ts)
|
||||
}
|
||||
case pmetric.MetricTypeSummary:
|
||||
dataPoints := metric.Summary().DataPoints()
|
||||
for x := 0; x < dataPoints.Len(); x++ {
|
||||
ts = max(ts, dataPoints.At(x).Timestamp())
|
||||
ts := dataPoints.At(x).Timestamp()
|
||||
minTimestamp = min(minTimestamp, ts)
|
||||
maxTimestamp = max(maxTimestamp, ts)
|
||||
}
|
||||
}
|
||||
return ts
|
||||
return minTimestamp, maxTimestamp
|
||||
}
|
||||
|
||||
func (c *PrometheusConverter) addSummaryDataPoints(ctx context.Context, dataPoints pmetric.SummaryDataPointSlice, resource pcommon.Resource,
|
||||
@ -569,8 +576,8 @@ func (c *PrometheusConverter) addTimeSeriesIfNeeded(lbls []prompb.Label, startTi
|
||||
}
|
||||
|
||||
// addResourceTargetInfo converts the resource to the target info metric.
|
||||
func addResourceTargetInfo(resource pcommon.Resource, settings Settings, timestamp pcommon.Timestamp, converter *PrometheusConverter) {
|
||||
if settings.DisableTargetInfo || timestamp == 0 {
|
||||
func addResourceTargetInfo(resource pcommon.Resource, settings Settings, earliestTimestamp, latestTimestamp time.Time, converter *PrometheusConverter) {
|
||||
if settings.DisableTargetInfo {
|
||||
return
|
||||
}
|
||||
|
||||
@ -616,12 +623,27 @@ func addResourceTargetInfo(resource pcommon.Resource, settings Settings, timesta
|
||||
return
|
||||
}
|
||||
|
||||
sample := &prompb.Sample{
|
||||
Value: float64(1),
|
||||
// convert ns to ms
|
||||
Timestamp: convertTimeStamp(timestamp),
|
||||
// Generate target_info samples starting at earliestTimestamp and ending at latestTimestamp,
|
||||
// with a sample at every interval between them.
|
||||
// Use an interval corresponding to half of the lookback delta, to ensure that target_info samples are found
|
||||
// for the entirety of the relevant period.
|
||||
if settings.LookbackDelta == 0 {
|
||||
settings.LookbackDelta = defaultLookbackDelta
|
||||
}
|
||||
interval := settings.LookbackDelta / 2
|
||||
ts, _ := converter.getOrCreateTimeSeries(labels)
|
||||
for timestamp := earliestTimestamp; timestamp.Before(latestTimestamp); timestamp = timestamp.Add(interval) {
|
||||
ts.Samples = append(ts.Samples, prompb.Sample{
|
||||
Value: float64(1),
|
||||
Timestamp: timestamp.UnixMilli(),
|
||||
})
|
||||
}
|
||||
if len(ts.Samples) == 0 || ts.Samples[len(ts.Samples)-1].Timestamp < latestTimestamp.UnixMilli() {
|
||||
ts.Samples = append(ts.Samples, prompb.Sample{
|
||||
Value: float64(1),
|
||||
Timestamp: latestTimestamp.UnixMilli(),
|
||||
})
|
||||
}
|
||||
converter.addSample(sample, labels)
|
||||
}
|
||||
|
||||
// convertTimeStamp converts OTLP timestamp in ns to timestamp in ms.
|
||||
|
@ -20,7 +20,9 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/otlptranslator"
|
||||
"go.opentelemetry.io/collector/pdata/pcommon"
|
||||
@ -48,6 +50,8 @@ type Settings struct {
|
||||
KeepIdentifyingResourceAttributes bool
|
||||
ConvertHistogramsToNHCB bool
|
||||
AllowDeltaTemporality bool
|
||||
// LookbackDelta is the PromQL engine lookback delta.
|
||||
LookbackDelta time.Duration
|
||||
}
|
||||
|
||||
// PrometheusConverter converts from OTel write format to Prometheus remote write format.
|
||||
@ -113,9 +117,10 @@ func (c *PrometheusConverter) FromMetrics(ctx context.Context, md pmetric.Metric
|
||||
resourceMetrics := resourceMetricsSlice.At(i)
|
||||
resource := resourceMetrics.Resource()
|
||||
scopeMetricsSlice := resourceMetrics.ScopeMetrics()
|
||||
// keep track of the most recent timestamp in the ResourceMetrics for
|
||||
// keep track of the earliest and latest timestamp in the ResourceMetrics for
|
||||
// use with the "target" info metric
|
||||
var mostRecentTimestamp pcommon.Timestamp
|
||||
earliestTimestamp := pcommon.Timestamp(math.MaxUint64)
|
||||
latestTimestamp := pcommon.Timestamp(0)
|
||||
for j := 0; j < scopeMetricsSlice.Len(); j++ {
|
||||
metricSlice := scopeMetricsSlice.At(j).Metrics()
|
||||
|
||||
@ -127,7 +132,7 @@ func (c *PrometheusConverter) FromMetrics(ctx context.Context, md pmetric.Metric
|
||||
}
|
||||
|
||||
metric := metricSlice.At(k)
|
||||
mostRecentTimestamp = max(mostRecentTimestamp, mostRecentTimestampInMetric(metric))
|
||||
earliestTimestamp, latestTimestamp = findMinAndMaxTimestamps(metric, earliestTimestamp, latestTimestamp)
|
||||
temporality, hasTemporality, err := aggregationTemporality(metric)
|
||||
if err != nil {
|
||||
errs = multierr.Append(errs, err)
|
||||
@ -242,7 +247,11 @@ func (c *PrometheusConverter) FromMetrics(ctx context.Context, md pmetric.Metric
|
||||
}
|
||||
}
|
||||
}
|
||||
addResourceTargetInfo(resource, settings, mostRecentTimestamp, c)
|
||||
if earliestTimestamp < pcommon.Timestamp(math.MaxUint64) {
|
||||
// We have at least one metric sample for this resource.
|
||||
// Generate a corresponding target_info series.
|
||||
addResourceTargetInfo(resource, settings, earliestTimestamp.AsTime(), latestTimestamp.AsTime(), c)
|
||||
}
|
||||
}
|
||||
|
||||
return annots, errs
|
||||
|
@ -275,6 +275,100 @@ func TestFromMetrics(t *testing.T) {
|
||||
"histogram data point has zero count, but non-zero sum: 155.000000",
|
||||
}, ws)
|
||||
})
|
||||
|
||||
t.Run("target_info's samples starts at the earliest metric sample timestamp and ends at the latest sample timestamp of the corresponding resource, with one sample every lookback delta/2 timestamps between", func(t *testing.T) {
|
||||
request := pmetricotlp.NewExportRequest()
|
||||
rm := request.Metrics().ResourceMetrics().AppendEmpty()
|
||||
generateAttributes(rm.Resource().Attributes(), "resource", 5)
|
||||
|
||||
// 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())
|
||||
|
||||
var expMetadata []prompb.MetricMetadata
|
||||
for i := range 3 {
|
||||
m := metrics.AppendEmpty()
|
||||
m.SetEmptyGauge()
|
||||
m.SetName(fmt.Sprintf("gauge-%v", i+1))
|
||||
m.SetDescription("gauge")
|
||||
m.SetUnit("unit")
|
||||
// Add samples every lookback delta / 4 timestamps.
|
||||
curTs := ts.AsTime()
|
||||
for range 6 {
|
||||
point := m.Gauge().DataPoints().AppendEmpty()
|
||||
point.SetTimestamp(pcommon.NewTimestampFromTime(curTs))
|
||||
point.SetDoubleValue(1.23)
|
||||
generateAttributes(point.Attributes(), "series", 2)
|
||||
curTs = curTs.Add(defaultLookbackDelta / 4)
|
||||
}
|
||||
|
||||
namer := otlptranslator.MetricNamer{}
|
||||
expMetadata = append(expMetadata, prompb.MetricMetadata{
|
||||
Type: otelMetricTypeToPromMetricType(m),
|
||||
MetricFamilyName: namer.Build(TranslatorMetricFromOtelMetric(m)),
|
||||
Help: m.Description(),
|
||||
Unit: m.Unit(),
|
||||
})
|
||||
}
|
||||
|
||||
converter := NewPrometheusConverter()
|
||||
annots, err := converter.FromMetrics(
|
||||
context.Background(),
|
||||
request.Metrics(),
|
||||
Settings{
|
||||
LookbackDelta: defaultLookbackDelta,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, annots)
|
||||
|
||||
testutil.RequireEqual(t, expMetadata, converter.Metadata())
|
||||
|
||||
timeSeries := converter.TimeSeries()
|
||||
tgtInfoCount := 0
|
||||
for _, s := range timeSeries {
|
||||
b := labels.NewScratchBuilder(2)
|
||||
lbls := s.ToLabels(&b, nil)
|
||||
if lbls.Get(labels.MetricName) != "target_info" {
|
||||
continue
|
||||
}
|
||||
|
||||
tgtInfoCount++
|
||||
require.Equal(t, "test-namespace/test-service", lbls.Get("job"))
|
||||
require.Equal(t, "id1234", lbls.Get("instance"))
|
||||
require.False(t, lbls.Has("service_name"))
|
||||
require.False(t, lbls.Has("service_namespace"))
|
||||
require.False(t, lbls.Has("service_instance_id"))
|
||||
// There should be a target_info sample at the earliest metric timestamp, then two spaced lookback delta/2 apart,
|
||||
// then one at the latest metric timestamp.
|
||||
testutil.RequireEqual(t, []prompb.Sample{
|
||||
{
|
||||
Value: 1,
|
||||
Timestamp: ts.AsTime().UnixMilli(),
|
||||
},
|
||||
{
|
||||
Value: 1,
|
||||
Timestamp: ts.AsTime().Add(defaultLookbackDelta / 2).UnixMilli(),
|
||||
},
|
||||
{
|
||||
Value: 1,
|
||||
Timestamp: ts.AsTime().Add(defaultLookbackDelta).UnixMilli(),
|
||||
},
|
||||
{
|
||||
Value: 1,
|
||||
Timestamp: ts.AsTime().Add(defaultLookbackDelta + defaultLookbackDelta/4).UnixMilli(),
|
||||
},
|
||||
}, s.Samples)
|
||||
}
|
||||
require.Equal(t, 1, tgtInfoCount)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTemporality(t *testing.T) {
|
||||
|
@ -530,6 +530,9 @@ type OTLPOptions struct {
|
||||
// marking the metric type as unknown for now).
|
||||
// We're in an early phase of implementing delta support (proposal: https://github.com/prometheus/proposals/pull/48/)
|
||||
NativeDelta bool
|
||||
// LookbackDelta is the query lookback delta.
|
||||
// Used to calculate the target_info sample timestamp interval.
|
||||
LookbackDelta time.Duration
|
||||
}
|
||||
|
||||
// NewOTLPWriteHandler creates a http.Handler that accepts OTLP write requests and
|
||||
@ -547,6 +550,7 @@ func NewOTLPWriteHandler(logger *slog.Logger, _ prometheus.Registerer, appendabl
|
||||
},
|
||||
config: configFunc,
|
||||
allowDeltaTemporality: opts.NativeDelta,
|
||||
lookbackDelta: opts.LookbackDelta,
|
||||
}
|
||||
|
||||
wh := &otlpWriteHandler{logger: logger, defaultConsumer: ex}
|
||||
@ -583,19 +587,22 @@ type rwExporter struct {
|
||||
*writeHandler
|
||||
config func() config.Config
|
||||
allowDeltaTemporality bool
|
||||
lookbackDelta time.Duration
|
||||
}
|
||||
|
||||
func (rw *rwExporter) ConsumeMetrics(ctx context.Context, md pmetric.Metrics) error {
|
||||
otlpCfg := rw.config().OTLPConfig
|
||||
|
||||
converter := otlptranslator.NewPrometheusConverter()
|
||||
|
||||
annots, err := converter.FromMetrics(ctx, md, otlptranslator.Settings{
|
||||
AddMetricSuffixes: otlpCfg.TranslationStrategy != config.NoTranslation,
|
||||
AllowUTF8: otlpCfg.TranslationStrategy != config.UnderscoreEscapingWithSuffixes,
|
||||
AddMetricSuffixes: otlpCfg.TranslationStrategy.ShouldAddSuffixes(),
|
||||
AllowUTF8: !otlpCfg.TranslationStrategy.ShouldEscape(),
|
||||
PromoteResourceAttributes: otlptranslator.NewPromoteResourceAttributes(otlpCfg),
|
||||
KeepIdentifyingResourceAttributes: otlpCfg.KeepIdentifyingResourceAttributes,
|
||||
ConvertHistogramsToNHCB: otlpCfg.ConvertHistogramsToNHCB,
|
||||
AllowDeltaTemporality: rw.allowDeltaTemporality,
|
||||
LookbackDelta: rw.lookbackDelta,
|
||||
})
|
||||
if err != nil {
|
||||
rw.logger.Warn("Error translating OTLP metrics to Prometheus write request", "err", err)
|
||||
|
@ -440,7 +440,32 @@ func TestOTLPWriteHandler(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "UnderscoreEscapingWithoutSuffixes",
|
||||
otlpCfg: config.OTLPConfig{
|
||||
TranslationStrategy: config.UnderscoreEscapingWithoutSuffixes,
|
||||
},
|
||||
expectedSamples: []mockSample{
|
||||
{
|
||||
l: labels.New(labels.Label{Name: "__name__", Value: "test_counter"},
|
||||
labels.Label{Name: "foo_bar", Value: "baz"},
|
||||
labels.Label{Name: "instance", Value: "test-instance"},
|
||||
labels.Label{Name: "job", Value: "test-service"}),
|
||||
t: timestamp.UnixMilli(),
|
||||
v: 10.0,
|
||||
},
|
||||
{
|
||||
l: labels.New(
|
||||
labels.Label{Name: "__name__", Value: "target_info"},
|
||||
labels.Label{Name: "host_name", Value: "test-host"},
|
||||
labels.Label{Name: "instance", Value: "test-instance"},
|
||||
labels.Label{Name: "job", Value: "test-service"},
|
||||
),
|
||||
t: timestamp.UnixMilli(),
|
||||
v: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NoUTF8EscapingWithSuffixes",
|
||||
otlpCfg: config.OTLPConfig{
|
||||
|
@ -263,6 +263,17 @@ func NewTemplateExpander(
|
||||
|
||||
return floatToTime(v)
|
||||
},
|
||||
"toDuration": func(i interface{}) (*time.Duration, error) {
|
||||
v, err := common_templates.ConvertToFloat(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d := time.Duration(v * float64(time.Second))
|
||||
return &d, nil
|
||||
},
|
||||
"now": func() float64 {
|
||||
return float64(timestamp) / 1000.0
|
||||
},
|
||||
"pathPrefix": func() string {
|
||||
return externalURL.Path
|
||||
},
|
||||
@ -270,7 +281,7 @@ func NewTemplateExpander(
|
||||
return externalURL.String()
|
||||
},
|
||||
"parseDuration": func(d string) (float64, error) {
|
||||
v, err := model.ParseDuration(d)
|
||||
v, err := model.ParseDurationAllowNegative(d)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/prometheus/prometheus/model/histogram"
|
||||
@ -467,6 +468,31 @@ func TestTemplateExpansion(t *testing.T) {
|
||||
text: `{{ ("1435065584.128" | toTime).Format "2006" }}`,
|
||||
output: "2015",
|
||||
},
|
||||
{
|
||||
// toDuration - input as float64 seconds, returns *time.Duration.
|
||||
text: `{{ (1800 | toDuration).String }}`,
|
||||
output: "30m0s",
|
||||
},
|
||||
{
|
||||
// toDuration - input as string seconds, returns *time.Duration.
|
||||
text: `{{ ("1800" | toDuration).String }}`,
|
||||
output: "30m0s",
|
||||
},
|
||||
{
|
||||
// now - returns fixed timestamp as float64 seconds.
|
||||
text: `{{ now }}`,
|
||||
output: "1.353755652e+09",
|
||||
},
|
||||
{
|
||||
// now - returns fixed timestamp converted to formatted time string.
|
||||
text: `{{ (now | toTime).Format "Mon Jan 2 15:04:05 2006" }}`,
|
||||
output: "Sat Nov 24 11:14:12 2012",
|
||||
},
|
||||
{
|
||||
// returns Unix milliseconds timestamp for 30 minutes ago.
|
||||
text: `{{ ("-30m" | parseDuration | toDuration | (now | toTime).Add).UnixMilli }}`,
|
||||
output: "1353753852000",
|
||||
},
|
||||
{
|
||||
// Title.
|
||||
text: "{{ \"aa bb CC\" | title }}",
|
||||
@ -514,10 +540,15 @@ func TestTemplateExpansion(t *testing.T) {
|
||||
output: "http://testhost:9090/path/prefix",
|
||||
},
|
||||
{
|
||||
// parseDuration (using printf to ensure the return is a string).
|
||||
// parseDuration with positive duration (using printf to ensure the return is a string).
|
||||
text: "{{ printf \"%0.2f\" (parseDuration \"1h2m10ms\") }}",
|
||||
output: "3720.01",
|
||||
},
|
||||
{
|
||||
// parseDuration with negative duration (using printf to ensure the return is a string).
|
||||
text: "{{ printf \"%0.2f\" (parseDuration \"-1h2m10ms\") }}",
|
||||
output: "-3720.01",
|
||||
},
|
||||
{
|
||||
// Simple hostname.
|
||||
text: "{{ \"foo.example.com\" | stripDomain }}",
|
||||
@ -579,7 +610,7 @@ func testTemplateExpansion(t *testing.T, scenarios []scenario) {
|
||||
}
|
||||
var result string
|
||||
var err error
|
||||
expander := NewTemplateExpander(context.Background(), s.text, "test", s.input, 0, queryFunc, extURL, s.options)
|
||||
expander := NewTemplateExpander(context.Background(), s.text, "test", s.input, model.Time(1353755652000), queryFunc, extURL, s.options)
|
||||
if s.html {
|
||||
result, err = expander.ExpandHTML(nil)
|
||||
} else {
|
||||
|
@ -188,10 +188,12 @@ type BlockMeta struct {
|
||||
|
||||
// BlockStats contains stats about contents of a block.
|
||||
type BlockStats struct {
|
||||
NumSamples uint64 `json:"numSamples,omitempty"`
|
||||
NumSeries uint64 `json:"numSeries,omitempty"`
|
||||
NumChunks uint64 `json:"numChunks,omitempty"`
|
||||
NumTombstones uint64 `json:"numTombstones,omitempty"`
|
||||
NumSamples uint64 `json:"numSamples,omitempty"`
|
||||
NumFloatSamples uint64 `json:"numFloatSamples,omitempty"`
|
||||
NumHistogramSamples uint64 `json:"numHistogramSamples,omitempty"`
|
||||
NumSeries uint64 `json:"numSeries,omitempty"`
|
||||
NumChunks uint64 `json:"numChunks,omitempty"`
|
||||
NumTombstones uint64 `json:"numTombstones,omitempty"`
|
||||
}
|
||||
|
||||
// BlockDesc describes a block by ULID and time range.
|
||||
|
@ -182,14 +182,6 @@ func DefaultPostingsDecoderFactory(_ *BlockMeta) index.PostingsDecoder {
|
||||
return index.DecodePostingsRaw
|
||||
}
|
||||
|
||||
func NewLeveledCompactorWithChunkSize(ctx context.Context, r prometheus.Registerer, l *slog.Logger, ranges []int64, pool chunkenc.Pool, maxBlockChunkSegmentSize int64, mergeFunc storage.VerticalChunkSeriesMergeFunc) (*LeveledCompactor, error) {
|
||||
return NewLeveledCompactorWithOptions(ctx, r, l, ranges, pool, LeveledCompactorOptions{
|
||||
MaxBlockChunkSegmentSize: maxBlockChunkSegmentSize,
|
||||
MergeFunc: mergeFunc,
|
||||
EnableOverlappingCompaction: true,
|
||||
})
|
||||
}
|
||||
|
||||
func NewLeveledCompactor(ctx context.Context, r prometheus.Registerer, l *slog.Logger, ranges []int64, pool chunkenc.Pool, mergeFunc storage.VerticalChunkSeriesMergeFunc) (*LeveledCompactor, error) {
|
||||
return NewLeveledCompactorWithOptions(ctx, r, l, ranges, pool, LeveledCompactorOptions{
|
||||
MergeFunc: mergeFunc,
|
||||
@ -895,7 +887,14 @@ func (c DefaultBlockPopulator) PopulateBlock(ctx context.Context, metrics *Compa
|
||||
meta.Stats.NumChunks += uint64(len(chks))
|
||||
meta.Stats.NumSeries++
|
||||
for _, chk := range chks {
|
||||
meta.Stats.NumSamples += uint64(chk.Chunk.NumSamples())
|
||||
samples := uint64(chk.Chunk.NumSamples())
|
||||
meta.Stats.NumSamples += samples
|
||||
switch chk.Chunk.Encoding() {
|
||||
case chunkenc.EncHistogram, chunkenc.EncFloatHistogram:
|
||||
meta.Stats.NumHistogramSamples += samples
|
||||
case chunkenc.EncXOR:
|
||||
meta.Stats.NumFloatSamples += samples
|
||||
}
|
||||
}
|
||||
|
||||
for _, chk := range chks {
|
||||
|
@ -1098,6 +1098,13 @@ func TestCompaction_populateBlock(t *testing.T) {
|
||||
s.NumChunks += uint64(len(series.chunks))
|
||||
for _, chk := range series.chunks {
|
||||
s.NumSamples += uint64(len(chk))
|
||||
for _, smpl := range chk {
|
||||
if smpl.h != nil || smpl.fh != nil {
|
||||
s.NumHistogramSamples++
|
||||
} else {
|
||||
s.NumFloatSamples++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
require.Equal(t, s, meta.Stats)
|
||||
|
@ -9066,7 +9066,7 @@ func TestOOONativeHistogramsSettings(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// compareSeries essentially replaces `require.Equal(t, expected, actual) in
|
||||
// compareSeries essentially replaces `require.Equal(t, expected, actual)` in
|
||||
// situations where the actual series might contain more counter reset hints
|
||||
// "unknown" than the expected series. This can easily happen for long series
|
||||
// that trigger new chunks. This function therefore tolerates counter reset
|
||||
|
@ -799,13 +799,17 @@ func (a *headAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, lset l
|
||||
if errors.Is(err, storage.ErrOutOfOrderSample) {
|
||||
return 0, storage.ErrOutOfOrderCT
|
||||
}
|
||||
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// OOO is not allowed because after the first scrape, CT will be the same for most (if not all) future samples.
|
||||
// This is to prevent the injected zero from being marked as OOO forever.
|
||||
if isOOO {
|
||||
s.Unlock()
|
||||
return 0, storage.ErrOutOfOrderCT
|
||||
}
|
||||
|
||||
s.pendingCommit = true
|
||||
s.Unlock()
|
||||
a.histograms = append(a.histograms, record.RefHistogramSample{
|
||||
@ -832,13 +836,17 @@ func (a *headAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, lset l
|
||||
if errors.Is(err, storage.ErrOutOfOrderSample) {
|
||||
return 0, storage.ErrOutOfOrderCT
|
||||
}
|
||||
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// OOO is not allowed because after the first scrape, CT will be the same for most (if not all) future samples.
|
||||
// This is to prevent the injected zero from being marked as OOO forever.
|
||||
if isOOO {
|
||||
s.Unlock()
|
||||
return 0, storage.ErrOutOfOrderCT
|
||||
}
|
||||
|
||||
s.pendingCommit = true
|
||||
s.Unlock()
|
||||
a.floatHistograms = append(a.floatHistograms, record.RefFloatHistogramSample{
|
||||
@ -852,6 +860,7 @@ func (a *headAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, lset l
|
||||
if ct > a.maxt {
|
||||
a.maxt = ct
|
||||
}
|
||||
|
||||
return storage.SeriesRef(s.ref), nil
|
||||
}
|
||||
|
||||
|
@ -6717,6 +6717,75 @@ func TestHeadAppender_AppendCT(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeadAppender_AppendHistogramCTZeroSample(t *testing.T) {
|
||||
type appendableSamples struct {
|
||||
ts int64
|
||||
h *histogram.Histogram
|
||||
fh *histogram.FloatHistogram
|
||||
ct int64 // 0 if no created timestamp.
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
appendableSamples []appendableSamples
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "integer histogram CT lower than minValidTime initiates ErrOutOfBounds",
|
||||
appendableSamples: []appendableSamples{
|
||||
{ts: 100, h: tsdbutil.GenerateTestHistogram(1), ct: -1},
|
||||
},
|
||||
expectedError: storage.ErrOutOfBounds,
|
||||
},
|
||||
{
|
||||
name: "float histograms CT lower than minValidTime initiates ErrOutOfBounds",
|
||||
appendableSamples: []appendableSamples{
|
||||
{ts: 100, fh: tsdbutil.GenerateTestFloatHistogram(1), ct: -1},
|
||||
},
|
||||
expectedError: storage.ErrOutOfBounds,
|
||||
},
|
||||
{
|
||||
name: "integer histogram CT duplicates an existing sample",
|
||||
appendableSamples: []appendableSamples{
|
||||
{ts: 100, h: tsdbutil.GenerateTestHistogram(1)},
|
||||
{ts: 200, h: tsdbutil.GenerateTestHistogram(1), ct: 100},
|
||||
},
|
||||
expectedError: storage.ErrDuplicateSampleForTimestamp,
|
||||
},
|
||||
{
|
||||
name: "float histogram CT duplicates an existing sample",
|
||||
appendableSamples: []appendableSamples{
|
||||
{ts: 100, fh: tsdbutil.GenerateTestFloatHistogram(1)},
|
||||
{ts: 200, fh: tsdbutil.GenerateTestFloatHistogram(1), ct: 100},
|
||||
},
|
||||
expectedError: storage.ErrDuplicateSampleForTimestamp,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
h, _ := newTestHead(t, DefaultBlockDuration, compression.None, false)
|
||||
|
||||
defer func() {
|
||||
require.NoError(t, h.Close())
|
||||
}()
|
||||
|
||||
lbls := labels.FromStrings("foo", "bar")
|
||||
|
||||
var ref storage.SeriesRef
|
||||
for _, sample := range tc.appendableSamples {
|
||||
a := h.Appender(context.Background())
|
||||
var err error
|
||||
if sample.ct != 0 {
|
||||
ref, err = a.AppendHistogramCTZeroSample(ref, lbls, sample.ts, sample.ct, sample.h, sample.fh)
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
}
|
||||
|
||||
ref, err = a.AppendHistogram(ref, lbls, sample.ts, sample.h, sample.fh)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, a.Commit())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeadCompactableDoesNotCompactEmptyHead(t *testing.T) {
|
||||
// Use a chunk range of 1 here so that if we attempted to determine if the head
|
||||
// was compactable using default values for min and max times, `Head.compactable()`
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
@ -2274,15 +2275,19 @@ func (m mockIndex) LabelValues(_ context.Context, name string, hints *storage.La
|
||||
}
|
||||
|
||||
for _, series := range m.series {
|
||||
matches := true
|
||||
for _, matcher := range matchers {
|
||||
if matcher.Matches(series.l.Get(matcher.Name)) {
|
||||
// TODO(colega): shouldn't we check all the matchers before adding this to the values?
|
||||
values = append(values, series.l.Get(name))
|
||||
if hints != nil && hints.Limit > 0 && len(values) >= hints.Limit {
|
||||
break
|
||||
}
|
||||
matches = matches && matcher.Matches(series.l.Get(matcher.Name))
|
||||
if !matches {
|
||||
break
|
||||
}
|
||||
}
|
||||
if matches && !slices.Contains(values, series.l.Get(name)) {
|
||||
values = append(values, series.l.Get(name))
|
||||
}
|
||||
if hints != nil && hints.Limit > 0 && len(values) >= hints.Limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return values, nil
|
||||
@ -2392,7 +2397,7 @@ func (m mockIndex) LabelNames(_ context.Context, matchers ...*labels.Matcher) ([
|
||||
for _, series := range m.series {
|
||||
matches := true
|
||||
for _, matcher := range matchers {
|
||||
matches = matches || matcher.Matches(series.l.Get(matcher.Name))
|
||||
matches = matches && matcher.Matches(series.l.Get(matcher.Name))
|
||||
if !matches {
|
||||
break
|
||||
}
|
||||
|
@ -264,6 +264,7 @@ func NewAPI(
|
||||
acceptRemoteWriteProtoMsgs []config.RemoteWriteProtoMsg,
|
||||
otlpEnabled, otlpDeltaToCumulative, otlpNativeDeltaIngestion bool,
|
||||
ctZeroIngestionEnabled bool,
|
||||
lookbackDelta time.Duration,
|
||||
) *API {
|
||||
a := &API{
|
||||
QueryEngine: qe,
|
||||
@ -310,7 +311,11 @@ func NewAPI(
|
||||
a.remoteWriteHandler = remote.NewWriteHandler(logger, registerer, ap, acceptRemoteWriteProtoMsgs, ctZeroIngestionEnabled)
|
||||
}
|
||||
if otlpEnabled {
|
||||
a.otlpWriteHandler = remote.NewOTLPWriteHandler(logger, registerer, ap, configFunc, remote.OTLPOptions{ConvertDelta: otlpDeltaToCumulative, NativeDelta: otlpNativeDeltaIngestion})
|
||||
a.otlpWriteHandler = remote.NewOTLPWriteHandler(logger, registerer, ap, configFunc, remote.OTLPOptions{
|
||||
ConvertDelta: otlpDeltaToCumulative,
|
||||
NativeDelta: otlpNativeDeltaIngestion,
|
||||
LookbackDelta: lookbackDelta,
|
||||
})
|
||||
}
|
||||
|
||||
return a
|
||||
|
@ -145,6 +145,7 @@ func createPrometheusAPI(t *testing.T, q storage.SampleAndChunkQueryable) *route
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
5*time.Minute,
|
||||
)
|
||||
|
||||
promRouter := route.New().WithPrefix("/api/v1")
|
||||
|
@ -156,7 +156,7 @@ func marshalSampleJSON(s promql.Sample, stream *jsoniter.Stream) {
|
||||
stream.WriteObjectEnd()
|
||||
}
|
||||
|
||||
// marshalFPointJSON writes `[ts, "1.234"]`.
|
||||
// unsafeMarshalFPointJSON writes `[ts, "1.234"]`.
|
||||
func unsafeMarshalFPointJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
p := *((*promql.FPoint)(ptr))
|
||||
marshalFPointJSON(p, stream)
|
||||
@ -170,7 +170,7 @@ func marshalFPointJSON(p promql.FPoint, stream *jsoniter.Stream) {
|
||||
stream.WriteArrayEnd()
|
||||
}
|
||||
|
||||
// marshalHPointJSON writes `[ts, { < histogram, see jsonutil.MarshalHistogram > } ]`.
|
||||
// unsafeMarshalHPointJSON writes `[ts, { < histogram, see jsonutil.MarshalHistogram > } ]`.
|
||||
func unsafeMarshalHPointJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
p := *((*promql.HPoint)(ptr))
|
||||
marshalHPointJSON(p, stream)
|
||||
|
@ -3,7 +3,6 @@
|
||||
/release/**/test/
|
||||
/scripts/
|
||||
/.circleci/
|
||||
/src/
|
||||
/test/
|
||||
/examples/
|
||||
/gulpfile.js
|
||||
|
@ -3,4 +3,3 @@ generate-types.sh
|
||||
jest.config.cjs
|
||||
rollup.config.js
|
||||
/test/
|
||||
/src/
|
||||
|
@ -392,6 +392,7 @@ func New(logger *slog.Logger, o *Options) *Handler {
|
||||
o.ConvertOTLPDelta,
|
||||
o.NativeOTLPDeltaIngestion,
|
||||
o.CTZeroIngestionEnabled,
|
||||
o.LookbackDelta,
|
||||
)
|
||||
|
||||
if o.RoutePrefix != "/" {
|
||||
|
Loading…
Reference in New Issue
Block a user