mirror of
https://github.com/prometheus/prometheus.git
synced 2025-12-18 21:26:32 -05:00
API: Add a /api/v1/features endpoint
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
This commit is contained in:
parent
0279e14d4a
commit
a5671a002f
20 changed files with 830 additions and 6 deletions
5
Makefile
5
Makefile
|
|
@ -184,6 +184,11 @@ check-go-mod-version:
|
|||
@echo ">> checking go.mod version matching"
|
||||
@./scripts/check-go-mod-version.sh
|
||||
|
||||
.PHONY: update-features-testdata
|
||||
update-features-testdata:
|
||||
@echo ">> updating features testdata"
|
||||
@$(GO) test ./cmd/prometheus -run TestFeaturesAPI -update-features
|
||||
|
||||
.PHONY: update-all-go-deps
|
||||
update-all-go-deps:
|
||||
@$(MAKE) update-go-deps
|
||||
|
|
|
|||
125
cmd/prometheus/features_test.go
Normal file
125
cmd/prometheus/features_test.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright 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.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/prometheus/prometheus/util/testutil"
|
||||
)
|
||||
|
||||
var updateFeatures = flag.Bool("update-features", false, "update features.json golden file")
|
||||
|
||||
func TestFeaturesAPI(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
t.Parallel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
configFile := filepath.Join(tmpDir, "prometheus.yml")
|
||||
require.NoError(t, os.WriteFile(configFile, []byte{}, 0o644))
|
||||
|
||||
port := testutil.RandomUnprivilegedPort(t)
|
||||
prom := prometheusCommandWithLogging(
|
||||
t,
|
||||
configFile,
|
||||
port,
|
||||
fmt.Sprintf("--storage.tsdb.path=%s", tmpDir),
|
||||
)
|
||||
require.NoError(t, prom.Start())
|
||||
|
||||
baseURL := fmt.Sprintf("http://127.0.0.1:%d", port)
|
||||
|
||||
// Wait for Prometheus to be ready.
|
||||
require.Eventually(t, func() bool {
|
||||
resp, err := http.Get(baseURL + "/-/ready")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return resp.StatusCode == http.StatusOK
|
||||
}, 10*time.Second, 100*time.Millisecond, "Prometheus didn't become ready in time")
|
||||
|
||||
// Fetch features from the API.
|
||||
resp, err := http.Get(baseURL + "/api/v1/features")
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Parse API response.
|
||||
var apiResponse struct {
|
||||
Status string `json:"status"`
|
||||
Data map[string]map[string]bool `json:"data"`
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(body, &apiResponse))
|
||||
require.Equal(t, "success", apiResponse.Status)
|
||||
|
||||
goldenPath := filepath.Join("testdata", "features.json")
|
||||
|
||||
// If update flag is set, write the current features to the golden file.
|
||||
if *updateFeatures {
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
encoder.SetEscapeHTML(false)
|
||||
encoder.SetIndent("", " ")
|
||||
require.NoError(t, encoder.Encode(apiResponse.Data))
|
||||
// Ensure testdata directory exists.
|
||||
require.NoError(t, os.MkdirAll(filepath.Dir(goldenPath), 0o755))
|
||||
require.NoError(t, os.WriteFile(goldenPath, buf.Bytes(), 0o644))
|
||||
t.Logf("Updated golden file: %s", goldenPath)
|
||||
return
|
||||
}
|
||||
|
||||
// Load golden file.
|
||||
goldenData, err := os.ReadFile(goldenPath)
|
||||
require.NoError(t, err, "Failed to read golden file %s. Run 'make update-features-testdata' to generate it.", goldenPath)
|
||||
|
||||
var expectedFeatures map[string]map[string]bool
|
||||
require.NoError(t, json.Unmarshal(goldenData, &expectedFeatures))
|
||||
|
||||
// The labels implementation depends on build tags (stringlabels, slicelabels, or dedupelabels).
|
||||
// We need to update the expected features to match the current build.
|
||||
if prometheusFeatures, ok := expectedFeatures["prometheus"]; ok {
|
||||
// Remove all label implementation features from expected.
|
||||
delete(prometheusFeatures, "stringlabels")
|
||||
delete(prometheusFeatures, "slicelabels")
|
||||
delete(prometheusFeatures, "dedupelabels")
|
||||
// Add the current implementation.
|
||||
if actualPrometheus, ok := apiResponse.Data["prometheus"]; ok {
|
||||
for _, impl := range []string{"stringlabels", "slicelabels", "dedupelabels"} {
|
||||
if actualPrometheus[impl] {
|
||||
prometheusFeatures[impl] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compare the features data with the golden file.
|
||||
require.Equal(t, expectedFeatures, apiResponse.Data, "Features mismatch. Run 'make update-features-testdata' to update the golden file.")
|
||||
}
|
||||
|
|
@ -73,11 +73,13 @@ import (
|
|||
"github.com/prometheus/prometheus/scrape"
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
"github.com/prometheus/prometheus/storage/remote"
|
||||
"github.com/prometheus/prometheus/template"
|
||||
"github.com/prometheus/prometheus/tracing"
|
||||
"github.com/prometheus/prometheus/tsdb"
|
||||
"github.com/prometheus/prometheus/tsdb/agent"
|
||||
"github.com/prometheus/prometheus/util/compression"
|
||||
"github.com/prometheus/prometheus/util/documentcli"
|
||||
"github.com/prometheus/prometheus/util/features"
|
||||
"github.com/prometheus/prometheus/util/logging"
|
||||
"github.com/prometheus/prometheus/util/notifications"
|
||||
prom_runtime "github.com/prometheus/prometheus/util/runtime"
|
||||
|
|
@ -236,6 +238,7 @@ func (c *flagConfig) setFeatureListOptions(logger *slog.Logger) error {
|
|||
case "metadata-wal-records":
|
||||
c.scrape.AppendMetadata = true
|
||||
c.web.AppendMetadata = true
|
||||
features.Enable(features.TSDB, "metadata_wal_records")
|
||||
logger.Info("Experimental metadata records in WAL enabled")
|
||||
case "promql-per-step-stats":
|
||||
c.enablePerStepStats = true
|
||||
|
|
@ -342,10 +345,14 @@ func main() {
|
|||
Registerer: prometheus.DefaultRegisterer,
|
||||
},
|
||||
web: web.Options{
|
||||
Registerer: prometheus.DefaultRegisterer,
|
||||
Gatherer: prometheus.DefaultGatherer,
|
||||
Registerer: prometheus.DefaultRegisterer,
|
||||
Gatherer: prometheus.DefaultGatherer,
|
||||
FeatureRegistry: features.DefaultRegistry,
|
||||
},
|
||||
promslogConfig: promslog.Config{},
|
||||
scrape: scrape.Options{
|
||||
FeatureRegistry: features.DefaultRegistry,
|
||||
},
|
||||
}
|
||||
|
||||
a := kingpin.New(filepath.Base(os.Args[0]), "The Prometheus monitoring server").UsageWriter(os.Stdout)
|
||||
|
|
@ -797,6 +804,12 @@ func main() {
|
|||
"vm_limits", prom_runtime.VMLimits(),
|
||||
)
|
||||
|
||||
features.Set(features.Prometheus, "agent_mode", agentMode)
|
||||
features.Set(features.Prometheus, "server_mode", !agentMode)
|
||||
features.Set(features.Prometheus, "auto_reload_config", cfg.enableAutoReload)
|
||||
features.Enable(features.Prometheus, labels.ImplementationName)
|
||||
template.RegisterFeatures(features.DefaultRegistry)
|
||||
|
||||
var (
|
||||
localStorage = &readyStorage{stats: tsdb.NewDBStats()}
|
||||
scraper = &readyScrapeManager{}
|
||||
|
|
@ -833,13 +846,13 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
discoveryManagerScrape = discovery.NewManager(ctxScrape, logger.With("component", "discovery manager scrape"), prometheus.DefaultRegisterer, sdMetrics, discovery.Name("scrape"))
|
||||
discoveryManagerScrape = discovery.NewManager(ctxScrape, logger.With("component", "discovery manager scrape"), prometheus.DefaultRegisterer, sdMetrics, discovery.Name("scrape"), discovery.FeatureRegistry(features.DefaultRegistry))
|
||||
if discoveryManagerScrape == nil {
|
||||
logger.Error("failed to create a discovery manager scrape")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
discoveryManagerNotify = discovery.NewManager(ctxNotify, logger.With("component", "discovery manager notify"), prometheus.DefaultRegisterer, sdMetrics, discovery.Name("notify"))
|
||||
discoveryManagerNotify = discovery.NewManager(ctxNotify, logger.With("component", "discovery manager notify"), prometheus.DefaultRegisterer, sdMetrics, discovery.Name("notify"), discovery.FeatureRegistry(features.DefaultRegistry))
|
||||
if discoveryManagerNotify == nil {
|
||||
logger.Error("failed to create a discovery manager notify")
|
||||
os.Exit(1)
|
||||
|
|
@ -880,6 +893,7 @@ func main() {
|
|||
EnablePerStepStats: cfg.enablePerStepStats,
|
||||
EnableDelayedNameRemoval: cfg.promqlEnableDelayedNameRemoval,
|
||||
EnableTypeAndUnitLabels: cfg.scrape.EnableTypeAndUnitLabels,
|
||||
FeatureRegistry: features.DefaultRegistry,
|
||||
}
|
||||
|
||||
queryEngine = promql.NewEngine(opts)
|
||||
|
|
@ -902,6 +916,7 @@ func main() {
|
|||
DefaultRuleQueryOffset: func() time.Duration {
|
||||
return time.Duration(cfgFile.GlobalConfig.RuleQueryOffset)
|
||||
},
|
||||
FeatureRegistry: features.DefaultRegistry,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1919,6 +1934,7 @@ func (opts tsdbOptions) ToTSDBOptions() tsdb.Options {
|
|||
EnableOverlappingCompaction: opts.EnableOverlappingCompaction,
|
||||
UseUncachedIO: opts.UseUncachedIO,
|
||||
BlockCompactionExcludeFunc: opts.BlockCompactionExcludeFunc,
|
||||
FeatureRegistry: features.DefaultRegistry,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
249
cmd/prometheus/testdata/features.json
vendored
Normal file
249
cmd/prometheus/testdata/features.json
vendored
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
{
|
||||
"api": {
|
||||
"admin": false,
|
||||
"exclude_alerts": true,
|
||||
"label_values_match": true,
|
||||
"lifecycle": false,
|
||||
"otlp_write_receiver": false,
|
||||
"query_stats": true,
|
||||
"query_warnings": true,
|
||||
"remote_write_receiver": false,
|
||||
"time_range_labels": true,
|
||||
"time_range_series": true
|
||||
},
|
||||
"otlp_receiver": {
|
||||
"delta_conversion": false,
|
||||
"native_delta_ingestion": false
|
||||
},
|
||||
"prometheus": {
|
||||
"agent_mode": false,
|
||||
"auto_reload_config": false,
|
||||
"server_mode": true,
|
||||
"stringlabels": true
|
||||
},
|
||||
"promql": {
|
||||
"anchored": false,
|
||||
"at_modifier": true,
|
||||
"bool": true,
|
||||
"by": true,
|
||||
"delayed_name_removal": false,
|
||||
"duration_expr": false,
|
||||
"group_left": true,
|
||||
"group_right": true,
|
||||
"ignoring": true,
|
||||
"negative_offset": true,
|
||||
"offset": true,
|
||||
"on": true,
|
||||
"per_query_lookback_delta": true,
|
||||
"per_step_stats": false,
|
||||
"smoothed": false,
|
||||
"subqueries": true,
|
||||
"type_and_unit_labels": false,
|
||||
"without": true
|
||||
},
|
||||
"promql_functions": {
|
||||
"abs": true,
|
||||
"absent": true,
|
||||
"absent_over_time": true,
|
||||
"acos": true,
|
||||
"acosh": true,
|
||||
"asin": true,
|
||||
"asinh": true,
|
||||
"atan": true,
|
||||
"atanh": true,
|
||||
"avg_over_time": true,
|
||||
"ceil": true,
|
||||
"changes": true,
|
||||
"clamp": true,
|
||||
"clamp_max": true,
|
||||
"clamp_min": true,
|
||||
"cos": true,
|
||||
"cosh": true,
|
||||
"count_over_time": true,
|
||||
"day_of_month": true,
|
||||
"day_of_week": true,
|
||||
"day_of_year": true,
|
||||
"days_in_month": true,
|
||||
"deg": true,
|
||||
"delta": true,
|
||||
"deriv": true,
|
||||
"double_exponential_smoothing": false,
|
||||
"exp": true,
|
||||
"first_over_time": false,
|
||||
"floor": true,
|
||||
"histogram_avg": true,
|
||||
"histogram_count": true,
|
||||
"histogram_fraction": true,
|
||||
"histogram_quantile": true,
|
||||
"histogram_stddev": true,
|
||||
"histogram_stdvar": true,
|
||||
"histogram_sum": true,
|
||||
"hour": true,
|
||||
"idelta": true,
|
||||
"increase": true,
|
||||
"info": false,
|
||||
"irate": true,
|
||||
"label_join": true,
|
||||
"label_replace": true,
|
||||
"last_over_time": true,
|
||||
"ln": true,
|
||||
"log10": true,
|
||||
"log2": true,
|
||||
"mad_over_time": false,
|
||||
"max_over_time": true,
|
||||
"min_over_time": true,
|
||||
"minute": true,
|
||||
"month": true,
|
||||
"pi": true,
|
||||
"predict_linear": true,
|
||||
"present_over_time": true,
|
||||
"quantile_over_time": true,
|
||||
"rad": true,
|
||||
"rate": true,
|
||||
"resets": true,
|
||||
"round": true,
|
||||
"scalar": true,
|
||||
"sgn": true,
|
||||
"sin": true,
|
||||
"sinh": true,
|
||||
"sort": true,
|
||||
"sort_by_label": false,
|
||||
"sort_by_label_desc": false,
|
||||
"sort_desc": true,
|
||||
"sqrt": true,
|
||||
"stddev_over_time": true,
|
||||
"stdvar_over_time": true,
|
||||
"sum_over_time": true,
|
||||
"tan": true,
|
||||
"tanh": true,
|
||||
"time": true,
|
||||
"timestamp": true,
|
||||
"ts_of_first_over_time": false,
|
||||
"ts_of_last_over_time": false,
|
||||
"ts_of_max_over_time": false,
|
||||
"ts_of_min_over_time": false,
|
||||
"vector": true,
|
||||
"year": true
|
||||
},
|
||||
"promql_operators": {
|
||||
"!=": true,
|
||||
"!~": true,
|
||||
"%": true,
|
||||
"*": true,
|
||||
"+": true,
|
||||
"-": true,
|
||||
"/": true,
|
||||
"<": true,
|
||||
"<=": true,
|
||||
"==": true,
|
||||
"=~": true,
|
||||
">": true,
|
||||
">=": true,
|
||||
"@": true,
|
||||
"^": true,
|
||||
"and": true,
|
||||
"atan2": true,
|
||||
"avg": true,
|
||||
"bottomk": true,
|
||||
"count": true,
|
||||
"count_values": true,
|
||||
"group": true,
|
||||
"limit_ratio": false,
|
||||
"limitk": false,
|
||||
"max": true,
|
||||
"min": true,
|
||||
"or": true,
|
||||
"quantile": true,
|
||||
"stddev": true,
|
||||
"stdvar": true,
|
||||
"sum": true,
|
||||
"topk": true,
|
||||
"unless": true
|
||||
},
|
||||
"rules": {
|
||||
"concurrent_rule_eval": false,
|
||||
"keep_firing_for": true,
|
||||
"query_offset": true
|
||||
},
|
||||
"scrape": {
|
||||
"extra_scrape_metrics": false,
|
||||
"start_timestamp_zero_ingestion": false,
|
||||
"type_and_unit_labels": false
|
||||
},
|
||||
"service_discovery_providers": {
|
||||
"aws": true,
|
||||
"azure": true,
|
||||
"consul": true,
|
||||
"digitalocean": true,
|
||||
"dns": true,
|
||||
"docker": true,
|
||||
"dockerswarm": true,
|
||||
"ec2": true,
|
||||
"ecs": true,
|
||||
"eureka": true,
|
||||
"file": true,
|
||||
"gce": true,
|
||||
"hetzner": true,
|
||||
"http": true,
|
||||
"ionos": true,
|
||||
"kubernetes": true,
|
||||
"kuma": true,
|
||||
"lightsail": true,
|
||||
"linode": true,
|
||||
"marathon": true,
|
||||
"nerve": true,
|
||||
"nomad": true,
|
||||
"openstack": true,
|
||||
"ovhcloud": true,
|
||||
"puppetdb": true,
|
||||
"scaleway": true,
|
||||
"serverset": true,
|
||||
"stackit": true,
|
||||
"static": true,
|
||||
"triton": true,
|
||||
"uyuni": true,
|
||||
"vultr": true
|
||||
},
|
||||
"templating_functions": {
|
||||
"args": true,
|
||||
"externalURL": true,
|
||||
"first": true,
|
||||
"graphLink": true,
|
||||
"humanize": true,
|
||||
"humanize1024": true,
|
||||
"humanizeDuration": true,
|
||||
"humanizePercentage": true,
|
||||
"humanizeTimestamp": true,
|
||||
"label": true,
|
||||
"match": true,
|
||||
"now": true,
|
||||
"parseDuration": true,
|
||||
"pathPrefix": true,
|
||||
"query": true,
|
||||
"reReplaceAll": true,
|
||||
"safeHtml": true,
|
||||
"sortByLabel": true,
|
||||
"stripDomain": true,
|
||||
"stripPort": true,
|
||||
"strvalue": true,
|
||||
"tableLink": true,
|
||||
"title": true,
|
||||
"toDuration": true,
|
||||
"toLower": true,
|
||||
"toTime": true,
|
||||
"toUpper": true,
|
||||
"urlQueryEscape": true,
|
||||
"value": true
|
||||
},
|
||||
"tsdb": {
|
||||
"delayed_compaction": false,
|
||||
"exemplar_storage": false,
|
||||
"isolation": true,
|
||||
"native_histograms": true,
|
||||
"use_uncached_io": false
|
||||
},
|
||||
"ui": {
|
||||
"ui_v2": false,
|
||||
"ui_v3": true
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/prometheus/common/promslog"
|
||||
|
||||
"github.com/prometheus/prometheus/discovery/targetgroup"
|
||||
"github.com/prometheus/prometheus/util/features"
|
||||
)
|
||||
|
||||
type poolKey struct {
|
||||
|
|
@ -111,6 +112,13 @@ func NewManager(ctx context.Context, logger *slog.Logger, registerer prometheus.
|
|||
}
|
||||
mgr.metrics = metrics
|
||||
|
||||
// Register all available service discovery providers with the feature registry.
|
||||
if mgr.featureRegistry != nil {
|
||||
for _, sdName := range RegisteredConfigNames() {
|
||||
mgr.featureRegistry.Enable(features.ServiceDiscoveryProviders, sdName)
|
||||
}
|
||||
}
|
||||
|
||||
return mgr
|
||||
}
|
||||
|
||||
|
|
@ -141,6 +149,15 @@ func HTTPClientOptions(opts ...config.HTTPClientOption) func(*Manager) {
|
|||
}
|
||||
}
|
||||
|
||||
// FeatureRegistry sets the feature registry for the manager.
|
||||
func FeatureRegistry(fr features.Collector) func(*Manager) {
|
||||
return func(m *Manager) {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
m.featureRegistry = fr
|
||||
}
|
||||
}
|
||||
|
||||
// Manager maintains a set of discovery providers and sends each update to a map channel.
|
||||
// Targets are grouped by the target set name.
|
||||
type Manager struct {
|
||||
|
|
@ -175,6 +192,9 @@ type Manager struct {
|
|||
|
||||
metrics *Metrics
|
||||
sdMetrics map[string]DiscovererMetrics
|
||||
|
||||
// featureRegistry is used to track which service discovery providers are configured.
|
||||
featureRegistry features.Collector
|
||||
}
|
||||
|
||||
// Providers returns the currently configured SD providers.
|
||||
|
|
|
|||
|
|
@ -280,3 +280,13 @@ func RegisterSDMetrics(registerer prometheus.Registerer, rmm RefreshMetricsManag
|
|||
}
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
// RegisteredConfigNames returns the names of all registered service discovery providers.
|
||||
func RegisteredConfigNames() []string {
|
||||
names := make([]string, 0, len(configNames))
|
||||
for name := range configNames {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1700,3 +1700,80 @@ GET /api/v1/notifications/live
|
|||
```
|
||||
|
||||
*New in v3.0*
|
||||
|
||||
### Features
|
||||
|
||||
The following endpoint returns a list of enabled features in the Prometheus server:
|
||||
|
||||
```
|
||||
GET /api/v1/features
|
||||
```
|
||||
|
||||
This endpoint provides information about which features are currently enabled or disabled in the Prometheus instance. Features are organized into categories such as `api`, `promql`, `promql_functions`, etc.
|
||||
|
||||
The `data` section contains a map where each key is a feature category, and each value is a map of feature names to their enabled status (boolean).
|
||||
|
||||
```bash
|
||||
curl http://localhost:9090/api/v1/features
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"api": {
|
||||
"admin": false,
|
||||
"exclude_alerts": true
|
||||
},
|
||||
"otlp_receiver": {
|
||||
"delta_conversion": false,
|
||||
"native_delta_ingestion": false
|
||||
},
|
||||
"prometheus": {
|
||||
"agent_mode": false,
|
||||
"auto_reload_config": false
|
||||
},
|
||||
"promql": {
|
||||
"anchored": false,
|
||||
"at_modifier": true
|
||||
},
|
||||
"promql_functions": {
|
||||
"abs": true,
|
||||
"absent": true
|
||||
},
|
||||
"promql_operators": {
|
||||
"!=": true,
|
||||
"!~": true
|
||||
},
|
||||
"rules": {
|
||||
"concurrent_rule_eval": false,
|
||||
"keep_firing_for": true
|
||||
},
|
||||
"scrape": {
|
||||
"start_timestamp_zero_ingestion": false,
|
||||
"extra_metrics": false
|
||||
},
|
||||
"service_discovery": {
|
||||
"azure": true,
|
||||
"consul": true
|
||||
},
|
||||
"templating": {
|
||||
"args": true,
|
||||
"externalURL": true
|
||||
},
|
||||
"tsdb": {
|
||||
"delayed_compaction": false,
|
||||
"exemplar_storage": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- All feature names use `snake_case` naming convention
|
||||
- Features set to `false` may be omitted from the response
|
||||
- Clients should treat absent features as equivalent to `false`
|
||||
- Clients must ignore unknown feature names and categories for forward compatibility
|
||||
|
||||
*New in v3.8*
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ import (
|
|||
"github.com/cespare/xxhash/v2"
|
||||
)
|
||||
|
||||
// ImplementationName is the name of the labels implementation.
|
||||
const ImplementationName = "dedupelabels"
|
||||
|
||||
// Labels is implemented by a SymbolTable and string holding name/value
|
||||
// pairs encoded as indexes into the table in varint encoding.
|
||||
// Names are in alphabetical order.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ import (
|
|||
"github.com/cespare/xxhash/v2"
|
||||
)
|
||||
|
||||
// ImplementationName is the name of the labels implementation.
|
||||
const ImplementationName = "slicelabels"
|
||||
|
||||
// Labels is a sorted set of labels. Order has to be guaranteed upon
|
||||
// instantiation.
|
||||
type Labels []Label
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ import (
|
|||
"github.com/cespare/xxhash/v2"
|
||||
)
|
||||
|
||||
// ImplementationName is the name of the labels implementation.
|
||||
const ImplementationName = "stringlabels"
|
||||
|
||||
// Labels is implemented by a single flat string holding name/value pairs.
|
||||
// Each name and value is preceded by its length, encoded as a single byte
|
||||
// for size 0-254, or the following 3 bytes little-endian, if the first byte is 255.
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import (
|
|||
"github.com/prometheus/prometheus/storage"
|
||||
"github.com/prometheus/prometheus/tsdb/chunkenc"
|
||||
"github.com/prometheus/prometheus/util/annotations"
|
||||
"github.com/prometheus/prometheus/util/features"
|
||||
"github.com/prometheus/prometheus/util/logging"
|
||||
"github.com/prometheus/prometheus/util/stats"
|
||||
"github.com/prometheus/prometheus/util/zeropool"
|
||||
|
|
@ -330,6 +331,9 @@ type EngineOpts struct {
|
|||
EnableDelayedNameRemoval bool
|
||||
// EnableTypeAndUnitLabels will allow PromQL Engine to make decisions based on the type and unit labels.
|
||||
EnableTypeAndUnitLabels bool
|
||||
|
||||
// FeatureRegistry is the registry for tracking enabled/disabled features.
|
||||
FeatureRegistry features.Collector
|
||||
}
|
||||
|
||||
// Engine handles the lifetime of queries from beginning to end.
|
||||
|
|
@ -446,6 +450,18 @@ func NewEngine(opts EngineOpts) *Engine {
|
|||
)
|
||||
}
|
||||
|
||||
if r := opts.FeatureRegistry; r != nil {
|
||||
r.Set(features.PromQL, "at_modifier", opts.EnableAtModifier)
|
||||
r.Set(features.PromQL, "negative_offset", opts.EnableNegativeOffset)
|
||||
r.Set(features.PromQL, "per_step_stats", opts.EnablePerStepStats)
|
||||
r.Set(features.PromQL, "delayed_name_removal", opts.EnableDelayedNameRemoval)
|
||||
r.Set(features.PromQL, "type_and_unit_labels", opts.EnableTypeAndUnitLabels)
|
||||
r.Enable(features.PromQL, "per_query_lookback_delta")
|
||||
r.Enable(features.PromQL, "subqueries")
|
||||
|
||||
parser.RegisterFeatures(r)
|
||||
}
|
||||
|
||||
return &Engine{
|
||||
timeout: opts.Timeout,
|
||||
logger: opts.Logger,
|
||||
|
|
|
|||
57
promql/parser/features.go
Normal file
57
promql/parser/features.go
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 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.
|
||||
|
||||
package parser
|
||||
|
||||
import "github.com/prometheus/prometheus/util/features"
|
||||
|
||||
// RegisterFeatures registers all PromQL features with the feature registry.
|
||||
// This includes operators (arithmetic and comparison/set), aggregators (standard
|
||||
// and experimental), and functions.
|
||||
func RegisterFeatures(r features.Collector) {
|
||||
// Register core PromQL language keywords.
|
||||
for keyword, itemType := range key {
|
||||
if itemType.IsKeyword() {
|
||||
// Handle experimental keywords separately.
|
||||
switch keyword {
|
||||
case "anchored", "smoothed":
|
||||
r.Set(features.PromQL, keyword, EnableExtendedRangeSelectors)
|
||||
default:
|
||||
r.Enable(features.PromQL, keyword)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register operators.
|
||||
for o := ItemType(operatorsStart + 1); o < operatorsEnd; o++ {
|
||||
if o.IsOperator() {
|
||||
r.Set(features.PromQLOperators, o.String(), true)
|
||||
}
|
||||
}
|
||||
|
||||
// Register aggregators.
|
||||
for a := ItemType(aggregatorsStart + 1); a < aggregatorsEnd; a++ {
|
||||
if a.IsAggregator() {
|
||||
experimental := a.IsExperimentalAggregator() && !EnableExperimentalFunctions
|
||||
r.Set(features.PromQLOperators, a.String(), !experimental)
|
||||
}
|
||||
}
|
||||
|
||||
// Register functions.
|
||||
for f, fc := range Functions {
|
||||
r.Set(features.PromQLFunctions, f, !fc.Experimental || EnableExperimentalFunctions)
|
||||
}
|
||||
|
||||
// Register experimental parser features.
|
||||
r.Set(features.PromQL, "duration_expr", ExperimentalDurationExpr)
|
||||
}
|
||||
|
|
@ -37,6 +37,7 @@ import (
|
|||
"github.com/prometheus/prometheus/promql"
|
||||
"github.com/prometheus/prometheus/promql/parser"
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
"github.com/prometheus/prometheus/util/features"
|
||||
"github.com/prometheus/prometheus/util/strutil"
|
||||
)
|
||||
|
||||
|
|
@ -134,6 +135,9 @@ type ManagerOptions struct {
|
|||
RestoreNewRuleGroups bool
|
||||
|
||||
Metrics *Metrics
|
||||
|
||||
// FeatureRegistry is used to register rule manager features.
|
||||
FeatureRegistry features.Collector
|
||||
}
|
||||
|
||||
// NewManager returns an implementation of Manager, ready to be started
|
||||
|
|
@ -174,6 +178,13 @@ func NewManager(o *ManagerOptions) *Manager {
|
|||
o.Logger = promslog.NewNopLogger()
|
||||
}
|
||||
|
||||
// Register rule manager features if a registry is provided.
|
||||
if o.FeatureRegistry != nil {
|
||||
o.FeatureRegistry.Set(features.Rules, "concurrent_rule_eval", o.ConcurrentEvalsEnabled)
|
||||
o.FeatureRegistry.Enable(features.Rules, "query_offset")
|
||||
o.FeatureRegistry.Enable(features.Rules, "keep_firing_for")
|
||||
}
|
||||
|
||||
m := &Manager{
|
||||
groups: map[string]*Group{},
|
||||
opts: o,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/prometheus/prometheus/discovery/targetgroup"
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
"github.com/prometheus/prometheus/util/features"
|
||||
"github.com/prometheus/prometheus/util/logging"
|
||||
"github.com/prometheus/prometheus/util/osutil"
|
||||
"github.com/prometheus/prometheus/util/pool"
|
||||
|
|
@ -67,6 +68,13 @@ func NewManager(o *Options, logger *slog.Logger, newScrapeFailureLogger func(str
|
|||
|
||||
m.metrics.setTargetMetadataCacheGatherer(m)
|
||||
|
||||
// Register scrape features.
|
||||
if r := o.FeatureRegistry; r != nil {
|
||||
r.Set(features.Scrape, "extra_scrape_metrics", o.ExtraMetrics)
|
||||
r.Set(features.Scrape, "start_timestamp_zero_ingestion", o.EnableStartTimestampZeroIngestion)
|
||||
r.Set(features.Scrape, "type_and_unit_labels", o.EnableTypeAndUnitLabels)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
|
|
@ -93,6 +101,9 @@ type Options struct {
|
|||
// Optional HTTP client options to use when scraping.
|
||||
HTTPClientOptions []config_util.HTTPClientOption
|
||||
|
||||
// FeatureRegistry is the registry for tracking enabled/disabled features.
|
||||
FeatureRegistry features.Collector
|
||||
|
||||
// private option for testability.
|
||||
skipOffsetting bool
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import (
|
|||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
"github.com/prometheus/prometheus/util/features"
|
||||
"github.com/prometheus/prometheus/util/strutil"
|
||||
)
|
||||
|
||||
|
|
@ -413,3 +414,29 @@ func floatToTime(v float64) (*time.Time, error) {
|
|||
t := model.TimeFromUnixNano(int64(timestamp)).Time().UTC()
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
// templateFunctions returns a representative funcMap with all available template functions.
|
||||
// This is used to discover which functions are available for feature registration.
|
||||
func templateFunctions() text_template.FuncMap {
|
||||
// Create a dummy expander to get the function map.
|
||||
expander := NewTemplateExpander(
|
||||
context.Background(),
|
||||
"",
|
||||
"",
|
||||
nil,
|
||||
0,
|
||||
nil,
|
||||
&url.URL{},
|
||||
nil,
|
||||
)
|
||||
return expander.funcMap
|
||||
}
|
||||
|
||||
// RegisterFeatures registers all template functions with the feature registry.
|
||||
func RegisterFeatures(r features.Collector) {
|
||||
// Get all function names from the template function map.
|
||||
funcMap := templateFunctions()
|
||||
for name := range funcMap {
|
||||
r.Enable(features.TemplatingFunctions, name)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
13
tsdb/db.go
13
tsdb/db.go
|
|
@ -47,6 +47,7 @@ import (
|
|||
"github.com/prometheus/prometheus/tsdb/tsdbutil"
|
||||
"github.com/prometheus/prometheus/tsdb/wlog"
|
||||
"github.com/prometheus/prometheus/util/compression"
|
||||
"github.com/prometheus/prometheus/util/features"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -223,6 +224,9 @@ type Options struct {
|
|||
// BlockCompactionExcludeFunc is a function which returns true for blocks that should NOT be compacted.
|
||||
// It's passed down to the TSDB compactor.
|
||||
BlockCompactionExcludeFunc BlockExcludeFilterFunc
|
||||
|
||||
// FeatureRegistry is used to register TSDB features.
|
||||
FeatureRegistry features.Collector
|
||||
}
|
||||
|
||||
type NewCompactorFunc func(ctx context.Context, r prometheus.Registerer, l *slog.Logger, ranges []int64, pool chunkenc.Pool, opts *Options) (Compactor, error)
|
||||
|
|
@ -783,6 +787,15 @@ func Open(dir string, l *slog.Logger, r prometheus.Registerer, opts *Options, st
|
|||
var rngs []int64
|
||||
opts, rngs = validateOpts(opts, nil)
|
||||
|
||||
// Register TSDB features if a registry is provided.
|
||||
if opts.FeatureRegistry != nil {
|
||||
opts.FeatureRegistry.Set(features.TSDB, "exemplar_storage", opts.EnableExemplarStorage)
|
||||
opts.FeatureRegistry.Set(features.TSDB, "delayed_compaction", opts.EnableDelayedCompaction)
|
||||
opts.FeatureRegistry.Set(features.TSDB, "isolation", !opts.IsolationDisabled)
|
||||
opts.FeatureRegistry.Set(features.TSDB, "use_uncached_io", opts.UseUncachedIO)
|
||||
opts.FeatureRegistry.Enable(features.TSDB, "native_histograms")
|
||||
}
|
||||
|
||||
return open(dir, l, r, opts, rngs, stats)
|
||||
}
|
||||
|
||||
|
|
|
|||
127
util/features/features.go
Normal file
127
util/features/features.go
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// Copyright 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.
|
||||
|
||||
package features
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Category constants define the standard feature flag categories used in Prometheus.
|
||||
const (
|
||||
API = "api"
|
||||
OTLPReceiver = "otlp_receiver"
|
||||
Prometheus = "prometheus"
|
||||
PromQL = "promql"
|
||||
PromQLFunctions = "promql_functions"
|
||||
PromQLOperators = "promql_operators"
|
||||
Rules = "rules"
|
||||
Scrape = "scrape"
|
||||
ServiceDiscoveryProviders = "service_discovery_providers"
|
||||
TemplatingFunctions = "templating_functions"
|
||||
TSDB = "tsdb"
|
||||
UI = "ui"
|
||||
)
|
||||
|
||||
// Collector defines the interface for collecting and managing feature flags.
|
||||
// It provides methods to enable, disable, and retrieve feature states.
|
||||
type Collector interface {
|
||||
// Enable marks a feature as enabled in the registry.
|
||||
// The category and name should use snake_case naming convention.
|
||||
Enable(category, name string)
|
||||
|
||||
// Disable marks a feature as disabled in the registry.
|
||||
// The category and name should use snake_case naming convention.
|
||||
Disable(category, name string)
|
||||
|
||||
// Set sets a feature to the specified enabled state.
|
||||
// The category and name should use snake_case naming convention.
|
||||
Set(category, name string, enabled bool)
|
||||
|
||||
// Get returns a copy of all registered features organized by category.
|
||||
// Returns a map where the keys are category names and values are maps
|
||||
// of feature names to their enabled status.
|
||||
Get() map[string]map[string]bool
|
||||
}
|
||||
|
||||
// registry is the private implementation of the Collector interface.
|
||||
// It stores feature information organized by category.
|
||||
type registry struct {
|
||||
mu sync.RWMutex
|
||||
features map[string]map[string]bool
|
||||
}
|
||||
|
||||
// DefaultRegistry is the package-level registry used by Prometheus.
|
||||
var DefaultRegistry = NewRegistry()
|
||||
|
||||
// NewRegistry creates a new feature registry.
|
||||
func NewRegistry() Collector {
|
||||
return ®istry{
|
||||
features: make(map[string]map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
// Enable marks a feature as enabled in the registry.
|
||||
func (r *registry) Enable(category, name string) {
|
||||
r.Set(category, name, true)
|
||||
}
|
||||
|
||||
// Disable marks a feature as disabled in the registry.
|
||||
func (r *registry) Disable(category, name string) {
|
||||
r.Set(category, name, false)
|
||||
}
|
||||
|
||||
// Set sets a feature to the specified enabled state.
|
||||
func (r *registry) Set(category, name string, enabled bool) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if r.features[category] == nil {
|
||||
r.features[category] = make(map[string]bool)
|
||||
}
|
||||
r.features[category][name] = enabled
|
||||
}
|
||||
|
||||
// Get returns a copy of all registered features organized by category.
|
||||
func (r *registry) Get() map[string]map[string]bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
result := make(map[string]map[string]bool, len(r.features))
|
||||
for category, features := range r.features {
|
||||
result[category] = make(map[string]bool, len(features))
|
||||
maps.Copy(result[category], features)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Enable marks a feature as enabled in the default registry.
|
||||
func Enable(category, name string) {
|
||||
DefaultRegistry.Enable(category, name)
|
||||
}
|
||||
|
||||
// Disable marks a feature as disabled in the default registry.
|
||||
func Disable(category, name string) {
|
||||
DefaultRegistry.Disable(category, name)
|
||||
}
|
||||
|
||||
// Set sets a feature to the specified enabled state in the default registry.
|
||||
func Set(category, name string, enabled bool) {
|
||||
DefaultRegistry.Set(category, name, enabled)
|
||||
}
|
||||
|
||||
// Get returns all features from the default registry.
|
||||
func Get() map[string]map[string]bool {
|
||||
return DefaultRegistry.Get()
|
||||
}
|
||||
|
|
@ -56,6 +56,7 @@ import (
|
|||
"github.com/prometheus/prometheus/tsdb"
|
||||
"github.com/prometheus/prometheus/tsdb/index"
|
||||
"github.com/prometheus/prometheus/util/annotations"
|
||||
"github.com/prometheus/prometheus/util/features"
|
||||
"github.com/prometheus/prometheus/util/httputil"
|
||||
"github.com/prometheus/prometheus/util/notifications"
|
||||
"github.com/prometheus/prometheus/util/stats"
|
||||
|
|
@ -255,6 +256,8 @@ type API struct {
|
|||
otlpWriteHandler http.Handler
|
||||
|
||||
codecs []Codec
|
||||
|
||||
featureRegistry features.Collector
|
||||
}
|
||||
|
||||
// NewAPI returns an initialized API type.
|
||||
|
|
@ -295,6 +298,7 @@ func NewAPI(
|
|||
enableTypeAndUnitLabels bool,
|
||||
appendMetadata bool,
|
||||
overrideErrorCode OverrideErrorCode,
|
||||
featureRegistry features.Collector,
|
||||
) *API {
|
||||
a := &API{
|
||||
QueryEngine: qe,
|
||||
|
|
@ -324,6 +328,7 @@ func NewAPI(
|
|||
notificationsGetter: notificationsGetter,
|
||||
notificationsSub: notificationsSub,
|
||||
overrideErrorCode: overrideErrorCode,
|
||||
featureRegistry: featureRegistry,
|
||||
|
||||
remoteReadHandler: remote.NewReadHandler(logger, registerer, q, configFunc, remoteReadSampleLimit, remoteReadConcurrencyLimit, remoteReadMaxBytesInFrame),
|
||||
}
|
||||
|
|
@ -445,6 +450,7 @@ func (api *API) Register(r *route.Router) {
|
|||
r.Get("/status/flags", wrap(api.serveFlags))
|
||||
r.Get("/status/tsdb", wrapAgent(api.serveTSDBStatus))
|
||||
r.Get("/status/tsdb/blocks", wrapAgent(api.serveTSDBBlocks))
|
||||
r.Get("/features", wrap(api.features))
|
||||
r.Get("/status/walreplay", api.serveWALReplayStatus)
|
||||
r.Get("/notifications", api.notifications)
|
||||
r.Get("/notifications/live", api.notificationsSSE)
|
||||
|
|
@ -1789,6 +1795,29 @@ func (api *API) serveFlags(*http.Request) apiFuncResult {
|
|||
return apiFuncResult{api.flagsMap, nil, nil, nil}
|
||||
}
|
||||
|
||||
// featuresData wraps feature flags data to provide custom JSON marshaling without HTML escaping.
|
||||
// featuresData does not contain user-provided input, and it is more convenient to have unescaped
|
||||
// representation of PromQL operators like >=.
|
||||
type featuresData struct {
|
||||
data map[string]map[string]bool
|
||||
}
|
||||
|
||||
func (f featuresData) MarshalJSON() ([]byte, error) {
|
||||
json := jsoniter.Config{
|
||||
EscapeHTML: false,
|
||||
SortMapKeys: true,
|
||||
ValidateJsonRawMessage: true,
|
||||
}.Froze()
|
||||
return json.Marshal(f.data)
|
||||
}
|
||||
|
||||
func (api *API) features(*http.Request) apiFuncResult {
|
||||
if api.featureRegistry == nil {
|
||||
return apiFuncResult{nil, &apiError{errorInternal, errors.New("feature registry not configured")}, nil, nil}
|
||||
}
|
||||
return apiFuncResult{featuresData{data: api.featureRegistry.Get()}, nil, nil, nil}
|
||||
}
|
||||
|
||||
// TSDBStat holds the information about individual cardinality.
|
||||
type TSDBStat struct {
|
||||
Name string `json:"name"`
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ func createPrometheusAPI(t *testing.T, q storage.SampleAndChunkQueryable, overri
|
|||
false,
|
||||
false,
|
||||
overrideErrorCode,
|
||||
nil,
|
||||
)
|
||||
|
||||
promRouter := route.New().WithPrefix("/api/v1")
|
||||
|
|
|
|||
25
web/web.go
25
web/web.go
|
|
@ -57,6 +57,7 @@ import (
|
|||
"github.com/prometheus/prometheus/scrape"
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
"github.com/prometheus/prometheus/template"
|
||||
"github.com/prometheus/prometheus/util/features"
|
||||
"github.com/prometheus/prometheus/util/httputil"
|
||||
"github.com/prometheus/prometheus/util/netconnlimit"
|
||||
"github.com/prometheus/prometheus/util/notifications"
|
||||
|
|
@ -300,8 +301,9 @@ type Options struct {
|
|||
|
||||
AcceptRemoteWriteProtoMsgs remoteapi.MessageTypes
|
||||
|
||||
Gatherer prometheus.Gatherer
|
||||
Registerer prometheus.Registerer
|
||||
Gatherer prometheus.Gatherer
|
||||
Registerer prometheus.Registerer
|
||||
FeatureRegistry features.Collector
|
||||
}
|
||||
|
||||
// New initializes a new web Handler.
|
||||
|
|
@ -399,8 +401,27 @@ func New(logger *slog.Logger, o *Options) *Handler {
|
|||
o.EnableTypeAndUnitLabels,
|
||||
o.AppendMetadata,
|
||||
nil,
|
||||
o.FeatureRegistry,
|
||||
)
|
||||
|
||||
if r := o.FeatureRegistry; r != nil {
|
||||
// Set dynamic API features (based on configuration).
|
||||
r.Set(features.API, "lifecycle", o.EnableLifecycle)
|
||||
r.Set(features.API, "admin", o.EnableAdminAPI)
|
||||
r.Set(features.API, "remote_write_receiver", o.EnableRemoteWriteReceiver)
|
||||
r.Set(features.API, "otlp_write_receiver", o.EnableOTLPWriteReceiver)
|
||||
r.Set(features.OTLPReceiver, "delta_conversion", o.ConvertOTLPDelta)
|
||||
r.Set(features.OTLPReceiver, "native_delta_ingestion", o.NativeOTLPDeltaIngestion)
|
||||
r.Enable(features.API, "label_values_match") // match[] parameter for label values endpoint.
|
||||
r.Enable(features.API, "query_warnings") // warnings in query responses.
|
||||
r.Enable(features.API, "query_stats") // stats parameter for query endpoints.
|
||||
r.Enable(features.API, "time_range_series") // start/end parameters for /series endpoint.
|
||||
r.Enable(features.API, "time_range_labels") // start/end parameters for /labels endpoints.
|
||||
r.Enable(features.API, "exclude_alerts") // exclude_alerts parameter for /rules endpoint.
|
||||
r.Set(features.UI, "ui_v3", !o.UseOldUI)
|
||||
r.Set(features.UI, "ui_v2", o.UseOldUI)
|
||||
}
|
||||
|
||||
if o.RoutePrefix != "/" {
|
||||
// If the prefix is missing for the root path, prepend it.
|
||||
router.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue