Skip to content

Commit 239c40e

Browse files
authored
feat: add opt-in support for Prometheus native histograms (#4108)
Adds opt-in support for Prometheus native histograms, as discussed in #3898. - New options: -enable-prometheus-native-histograms and -prometheus-native-histogram-bucket-factor (default 1.1, exported as metrics.DefaultNativeHistogramFactor) - Dual emission: classic bucketed histograms are kept, so this is fully backward compatible with existing dashboards/alerts; when the flag is off there is no behavior change - When a native bucket factor is set and `Buckets` is empty, client_golang drops classic buckets instead of applying defaults — the helper falls back to `prometheus.DefBuckets` to preserve them - `NativeHistogramMaxBucketNumber=160` and `NativeHistogramMinResetDuration=1h`, per the [Prometheus docs](https://prometheus.io/docs/specs/native_histograms/) and OTEL/KEP recommendation, are set to bound memory; happy to make these configurable if preferred - Native histograms are only transmitted via the protobuf exposition format, so text `/metrics` output is unchanged; docs updated accordingly Fixes #3898 --------- Signed-off-by: Ashrafahmed9 <ashrafahmed1232@gmail.com>
1 parent d6668f8 commit 239c40e

7 files changed

Lines changed: 245 additions & 146 deletions

File tree

config/config.go

Lines changed: 81 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -33,45 +33,47 @@ type Config struct {
3333
Flags *flag.FlagSet
3434

3535
// generic:
36-
Address string `yaml:"address"`
37-
InsecureAddress string `yaml:"insecure-address"`
38-
EnableTCPQueue bool `yaml:"enable-tcp-queue"`
39-
ExpectedBytesPerRequest int `yaml:"expected-bytes-per-request"`
40-
MaxTCPListenerConcurrency int `yaml:"max-tcp-listener-concurrency"`
41-
MaxTCPListenerQueue int `yaml:"max-tcp-listener-queue"`
42-
EnableCopyStreamPoolExperimental bool `yaml:"enable-copy-stream-pool"`
43-
IgnoreTrailingSlash bool `yaml:"ignore-trailing-slash"`
44-
Insecure bool `yaml:"insecure"`
45-
ProxyPreserveHost bool `yaml:"proxy-preserve-host"`
46-
DevMode bool `yaml:"dev-mode"`
47-
SupportListener string `yaml:"support-listener"`
48-
DebugListener string `yaml:"debug-listener"`
49-
CertPathTLS string `yaml:"tls-cert"`
50-
KeyPathTLS string `yaml:"tls-key"`
51-
StatusChecks *listFlag `yaml:"status-checks"`
52-
PrintVersion bool `yaml:"version"`
53-
MaxLoopbacks int `yaml:"max-loopbacks"`
54-
DefaultHTTPStatus int `yaml:"default-http-status"`
55-
PluginDir string `yaml:"plugindir"`
56-
LoadBalancerHealthCheckInterval time.Duration `yaml:"lb-healthcheck-interval"`
57-
ReverseSourcePredicate bool `yaml:"reverse-source-predicate"`
58-
RemoveHopHeaders bool `yaml:"remove-hop-headers"`
59-
RfcPatchPath bool `yaml:"rfc-patch-path"`
60-
MaxAuditBody int `yaml:"max-audit-body"`
61-
MaxMatcherBufferSize uint64 `yaml:"max-matcher-buffer-size"`
62-
EnableBreakers bool `yaml:"enable-breakers"`
63-
Breakers breakerFlags `yaml:"breaker"`
64-
EnableRatelimiters bool `yaml:"enable-ratelimits"`
65-
Ratelimits ratelimitFlags `yaml:"ratelimits"`
66-
EnableRouteFIFOMetrics bool `yaml:"enable-route-fifo-metrics"`
67-
EnableRouteLIFOMetrics bool `yaml:"enable-route-lifo-metrics"`
68-
MetricsFlavour *listFlag `yaml:"metrics-flavour"`
69-
DisableMetricsCompression bool `yaml:"disable-metrics-compression"`
70-
FilterPlugins *pluginFlag `yaml:"filter-plugin"`
71-
PredicatePlugins *pluginFlag `yaml:"predicate-plugin"`
72-
DataclientPlugins *pluginFlag `yaml:"dataclient-plugin"`
73-
MultiPlugins *pluginFlag `yaml:"multi-plugin"`
74-
CompressEncodings *listFlag `yaml:"compress-encodings"`
36+
Address string `yaml:"address"`
37+
InsecureAddress string `yaml:"insecure-address"`
38+
EnableTCPQueue bool `yaml:"enable-tcp-queue"`
39+
ExpectedBytesPerRequest int `yaml:"expected-bytes-per-request"`
40+
MaxTCPListenerConcurrency int `yaml:"max-tcp-listener-concurrency"`
41+
MaxTCPListenerQueue int `yaml:"max-tcp-listener-queue"`
42+
EnableCopyStreamPoolExperimental bool `yaml:"enable-copy-stream-pool"`
43+
IgnoreTrailingSlash bool `yaml:"ignore-trailing-slash"`
44+
Insecure bool `yaml:"insecure"`
45+
ProxyPreserveHost bool `yaml:"proxy-preserve-host"`
46+
DevMode bool `yaml:"dev-mode"`
47+
SupportListener string `yaml:"support-listener"`
48+
DebugListener string `yaml:"debug-listener"`
49+
CertPathTLS string `yaml:"tls-cert"`
50+
KeyPathTLS string `yaml:"tls-key"`
51+
StatusChecks *listFlag `yaml:"status-checks"`
52+
PrintVersion bool `yaml:"version"`
53+
MaxLoopbacks int `yaml:"max-loopbacks"`
54+
DefaultHTTPStatus int `yaml:"default-http-status"`
55+
PluginDir string `yaml:"plugindir"`
56+
LoadBalancerHealthCheckInterval time.Duration `yaml:"lb-healthcheck-interval"`
57+
ReverseSourcePredicate bool `yaml:"reverse-source-predicate"`
58+
RemoveHopHeaders bool `yaml:"remove-hop-headers"`
59+
RfcPatchPath bool `yaml:"rfc-patch-path"`
60+
MaxAuditBody int `yaml:"max-audit-body"`
61+
MaxMatcherBufferSize uint64 `yaml:"max-matcher-buffer-size"`
62+
EnableBreakers bool `yaml:"enable-breakers"`
63+
Breakers breakerFlags `yaml:"breaker"`
64+
EnableRatelimiters bool `yaml:"enable-ratelimits"`
65+
Ratelimits ratelimitFlags `yaml:"ratelimits"`
66+
EnableRouteFIFOMetrics bool `yaml:"enable-route-fifo-metrics"`
67+
EnableRouteLIFOMetrics bool `yaml:"enable-route-lifo-metrics"`
68+
MetricsFlavour *listFlag `yaml:"metrics-flavour"`
69+
DisableMetricsCompression bool `yaml:"disable-metrics-compression"`
70+
EnablePrometheusNativeHistograms bool `yaml:"enable-prometheus-native-histograms"`
71+
PrometheusNativeHistogramBucketFactor float64 `yaml:"prometheus-native-histogram-bucket-factor"`
72+
FilterPlugins *pluginFlag `yaml:"filter-plugin"`
73+
PredicatePlugins *pluginFlag `yaml:"predicate-plugin"`
74+
DataclientPlugins *pluginFlag `yaml:"dataclient-plugin"`
75+
MultiPlugins *pluginFlag `yaml:"multi-plugin"`
76+
CompressEncodings *listFlag `yaml:"compress-encodings"`
7577

7678
// logging, metrics, profiling, tracing:
7779
EnablePrometheusMetrics bool `yaml:"enable-prometheus-metrics"`
@@ -448,6 +450,8 @@ func NewConfig() *Config {
448450
flag.BoolVar(&cfg.EnableRouteLIFOMetrics, "enable-route-lifo-metrics", false, "enable metrics for the individual route LIFO queues")
449451
flag.Var(cfg.MetricsFlavour, "metrics-flavour", "Metrics flavour is used to change the exposed metrics format. Supported metric formats: 'codahale', 'prometheus' and 'otel', you can select multiple or all of them by using one option with ',' separated values")
450452
flag.BoolVar(&cfg.DisableMetricsCompression, "disable-metrics-compression", false, "disable metrics compression on /metrics handler endpoint.")
453+
flag.BoolVar(&cfg.EnablePrometheusNativeHistograms, "enable-prometheus-native-histograms", false, "enables prometheus native histograms in addition to the classic bucketed histograms")
454+
flag.Float64Var(&cfg.PrometheusNativeHistogramBucketFactor, "prometheus-native-histogram-bucket-factor", metrics.DefaultNativeHistogramFactor, "resolution of prometheus native histograms, must be greater than 1, defaults to 1.1 when native histograms are enabled")
451455
flag.Var(cfg.FilterPlugins, "filter-plugin", "set a custom filter plugins to load, a comma separated list of name and arguments")
452456
flag.Var(cfg.PredicatePlugins, "predicate-plugin", "set a custom predicate plugins to load, a comma separated list of name and arguments")
453457
flag.Var(cfg.DataclientPlugins, "dataclient-plugin", "set a custom dataclient plugins to load, a comma separated list of name and arguments")
@@ -921,42 +925,44 @@ func (c *Config) ToOptions() skipper.Options {
921925

922926
options := skipper.Options{
923927
// generic:
924-
Address: c.Address,
925-
InsecureAddress: c.InsecureAddress,
926-
StatusChecks: c.StatusChecks.values,
927-
EnableTCPQueue: c.EnableTCPQueue,
928-
ExpectedBytesPerRequest: c.ExpectedBytesPerRequest,
929-
MaxTCPListenerConcurrency: c.MaxTCPListenerConcurrency,
930-
MaxTCPListenerQueue: c.MaxTCPListenerQueue,
931-
EnableCopyStreamPoolExperimental: c.EnableCopyStreamPoolExperimental,
932-
IgnoreTrailingSlash: c.IgnoreTrailingSlash,
933-
DevMode: c.DevMode,
934-
SupportListener: c.SupportListener,
935-
DebugListener: c.DebugListener,
936-
CertPathTLS: c.CertPathTLS,
937-
KeyPathTLS: c.KeyPathTLS,
938-
TLSClientAuth: c.TLSClientAuth,
939-
TLSMinVersion: c.getMinTLSVersion(),
940-
CipherSuites: c.filterCipherSuites(),
941-
MaxLoopbacks: c.MaxLoopbacks,
942-
DefaultHTTPStatus: c.DefaultHTTPStatus,
943-
ReverseSourcePredicate: c.ReverseSourcePredicate,
944-
MaxAuditBody: c.MaxAuditBody,
945-
MaxMatcherBufferSize: c.MaxMatcherBufferSize,
946-
EnableBreakers: c.EnableBreakers,
947-
BreakerSettings: c.Breakers,
948-
EnableRatelimiters: c.EnableRatelimiters,
949-
RatelimitSettings: c.Ratelimits,
950-
EnableRouteFIFOMetrics: c.EnableRouteFIFOMetrics,
951-
EnableRouteLIFOMetrics: c.EnableRouteLIFOMetrics,
952-
MetricsFlavours: c.MetricsFlavour.values,
953-
DisableMetricsCompression: c.DisableMetricsCompression,
954-
FilterPlugins: c.FilterPlugins.values,
955-
PredicatePlugins: c.PredicatePlugins.values,
956-
DataClientPlugins: c.DataclientPlugins.values,
957-
Plugins: c.MultiPlugins.values,
958-
PluginDirs: []string{skipper.DefaultPluginDir},
959-
CompressEncodings: c.CompressEncodings.values,
928+
Address: c.Address,
929+
InsecureAddress: c.InsecureAddress,
930+
StatusChecks: c.StatusChecks.values,
931+
EnableTCPQueue: c.EnableTCPQueue,
932+
ExpectedBytesPerRequest: c.ExpectedBytesPerRequest,
933+
MaxTCPListenerConcurrency: c.MaxTCPListenerConcurrency,
934+
MaxTCPListenerQueue: c.MaxTCPListenerQueue,
935+
EnableCopyStreamPoolExperimental: c.EnableCopyStreamPoolExperimental,
936+
IgnoreTrailingSlash: c.IgnoreTrailingSlash,
937+
DevMode: c.DevMode,
938+
SupportListener: c.SupportListener,
939+
DebugListener: c.DebugListener,
940+
CertPathTLS: c.CertPathTLS,
941+
KeyPathTLS: c.KeyPathTLS,
942+
TLSClientAuth: c.TLSClientAuth,
943+
TLSMinVersion: c.getMinTLSVersion(),
944+
CipherSuites: c.filterCipherSuites(),
945+
MaxLoopbacks: c.MaxLoopbacks,
946+
DefaultHTTPStatus: c.DefaultHTTPStatus,
947+
ReverseSourcePredicate: c.ReverseSourcePredicate,
948+
MaxAuditBody: c.MaxAuditBody,
949+
MaxMatcherBufferSize: c.MaxMatcherBufferSize,
950+
EnableBreakers: c.EnableBreakers,
951+
BreakerSettings: c.Breakers,
952+
EnableRatelimiters: c.EnableRatelimiters,
953+
RatelimitSettings: c.Ratelimits,
954+
EnableRouteFIFOMetrics: c.EnableRouteFIFOMetrics,
955+
EnableRouteLIFOMetrics: c.EnableRouteLIFOMetrics,
956+
MetricsFlavours: c.MetricsFlavour.values,
957+
DisableMetricsCompression: c.DisableMetricsCompression,
958+
EnablePrometheusNativeHistograms: c.EnablePrometheusNativeHistograms,
959+
PrometheusNativeHistogramBucketFactor: c.PrometheusNativeHistogramBucketFactor,
960+
FilterPlugins: c.FilterPlugins.values,
961+
PredicatePlugins: c.PredicatePlugins.values,
962+
DataClientPlugins: c.DataclientPlugins.values,
963+
Plugins: c.MultiPlugins.values,
964+
PluginDirs: []string{skipper.DefaultPluginDir},
965+
CompressEncodings: c.CompressEncodings.values,
960966

961967
// logging, metrics, profiling, tracing:
962968
EnablePrometheusMetrics: c.EnablePrometheusMetrics,

config/config_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ func defaultConfig(with func(*Config)) *Config {
8282
MaxAuditBody: 1024,
8383
MaxMatcherBufferSize: 2097152,
8484
MetricsFlavour: commaListFlag("codahale", "prometheus", "otel"),
85+
PrometheusNativeHistogramBucketFactor: metrics.DefaultNativeHistogramFactor,
8586
FilterPlugins: newPluginFlag(),
8687
PredicatePlugins: newPluginFlag(),
8788
DataclientPlugins: newPluginFlag(),

docs/operation/operation.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,21 @@ To monitor skipper we recommend the following queries:
302302

303303
You may add static metrics labels like `version` using Prometheus [relabeling feature](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config).
304304

305+
### Prometheus native histograms
306+
307+
Skipper can additionally expose [Prometheus native
308+
histograms](https://prometheus.io/docs/specs/native_histograms/) for
309+
all histogram metrics:
310+
311+
-enable-prometheus-native-histograms
312+
313+
Classic bucketed histograms are kept, so this is backwards compatible
314+
with existing dashboards and alerts. The resolution of native
315+
histograms can be tuned with `-prometheus-native-histogram-bucket-factor`, which
316+
must be greater than 1 and defaults to 1.1. Note that native
317+
histograms are only transmitted via the protobuf exposition format,
318+
so the scraping Prometheus needs `--enable-feature=native-histograms`.
319+
305320
### OpenTelemetry (OTel)
306321

307322
Skipper can push metrics to any [OpenTelemetry](https://opentelemetry.io/) compatible backend

metrics/metrics.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,14 @@ type Options struct {
209209

210210
// DisableCompression defaults to enable compression on the metrics endpoint, can be disabled if set to true
211211
DisableCompression bool
212+
213+
// EnablePrometheusNativeHistograms enables Prometheus native (sparse) histograms
214+
// in addition to classic bucketed histograms.
215+
EnablePrometheusNativeHistograms bool
216+
217+
// PrometheusNativeHistogramBucketFactor controls the resolution of native
218+
// histograms. Must be > 1. Defaults to 1.1 when native histograms are enabled.
219+
PrometheusNativeHistogramBucketFactor float64
212220
}
213221

214222
var (

0 commit comments

Comments
 (0)