Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Inspired by [github.com/zsais/go-gin-prometheus](https://github.com/zsais/go-gin
- [Ignore](#ignore)
- [Token](#token)
- [Bucket size](#bucket-size)
- [Native histogram](#native-histogram)
- [Troubleshooting](#troubleshooting)
- [The instrumentation doesn't seem to work](#the-instrumentation-doesnt-seem-to-work)

Expand Down Expand Up @@ -324,6 +325,28 @@ p := ginprom.New(
r.Use(p.Instrument())
```

### Native histogram

Configure ginprom to use native histogram instead of classical histograms.
Refers to: https://prometheus.io/docs/specs/native_histograms/

Default values:
- BucketFactor : 1.1
- MaxBucketNumber: 100
- MinResetDuration : 1 Hour

```go
r := gin.New()
p := ginprom.New(
ginprom.Engine(r),
ginprom.NativeHistogram(true),
ginprom.NativeHistogramBucketFactor(1.1),
ginprom.NativeHistogramMaxBucketNumber(100),
ginprom.NativeHistogramMinResetDuration(1 * time.Hour),
)
r.Use(p.Instrument())
```

## Troubleshooting

### The instrumentation doesn't seem to work
Expand Down
26 changes: 26 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ginprom

import (
"time"

"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
Expand Down Expand Up @@ -158,3 +160,27 @@ func CustomCounterLabels(labels []string, f func(c *gin.Context) map[string]stri
p.customCounterLabels = labels
}
}

func NativeHistogram(nh bool) PrometheusOption {
return func(p *Prometheus) {
p.nativeHistogram = nh
}
}

func NativeHistogramBucketFactor(nhbf float64) PrometheusOption {
return func(p *Prometheus) {
p.NativeHistogramBucketFactor = nhbf
}
}

func NativeHistogramMaxBucketNumber(nhmbn uint32) PrometheusOption {
return func(p *Prometheus) {
p.NativeHistogramMaxBucketNumber = nhmbn
}
}

func NativeHistogramMinResetDuration(nhmrd time.Duration) PrometheusOption {
return func(p *Prometheus) {
p.NativeHistogramMinResetDuration = nhmrd
}
}
69 changes: 56 additions & 13 deletions prom.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type Prometheus struct {
customCounterLabelsProvider func(c *gin.Context) map[string]string
customCounterLabels []string
customHistograms pmapHistogram
nativeHistogram bool

MetricsPath string
Namespace string
Expand All @@ -80,6 +81,10 @@ type Prometheus struct {
RequestPathFunc func(c *gin.Context) string
HandlerOpts promhttp.HandlerOpts

NativeHistogramBucketFactor float64
NativeHistogramMaxBucketNumber uint32
NativeHistogramMinResetDuration time.Duration

RequestCounterMetricName string
RequestDurationMetricName string
RequestSizeMetricName string
Expand Down Expand Up @@ -224,12 +229,29 @@ func (p *Prometheus) AddCustomHistogramValue(name string, labelValues []string,
func (p *Prometheus) AddCustomHistogram(name, help string, labels []string) {
p.customHistograms.Lock()
defer p.customHistograms.Unlock()
g := prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: p.Namespace,
Subsystem: p.Subsystem,
Name: name,
Help: help,
}, labels)

var reqDurOpts prometheus.HistogramOpts
if p.nativeHistogram {
reqDurOpts = prometheus.HistogramOpts{
Namespace: p.Namespace,
Subsystem: p.Subsystem,
NativeHistogramBucketFactor: p.NativeHistogramBucketFactor,
NativeHistogramMaxBucketNumber: p.NativeHistogramMaxBucketNumber,
NativeHistogramMinResetDuration: p.NativeHistogramMinResetDuration,
Name: name,
Help: help,
}

} else {
reqDurOpts = prometheus.HistogramOpts{
Namespace: p.Namespace,
Subsystem: p.Subsystem,
Name: name,
Help: help,
}
}

g := prometheus.NewHistogramVec(reqDurOpts, labels)
p.customHistograms.values[name] = *g
p.mustRegister(g)
}
Expand All @@ -254,6 +276,11 @@ func New(options ...PrometheusOption) *Prometheus {
RequestDurationMetricName: defaultReqDurMetricName,
RequestSizeMetricName: defaultReqSzMetricName,
ResponseSizeMetricName: defaultResSzMetricName,
nativeHistogram: false,
// Grafana Mimir recommended parameters: https://grafana.com/docs/mimir/latest/send/native-histograms/
NativeHistogramBucketFactor: 1.1,
NativeHistogramMaxBucketNumber: 100,
NativeHistogramMinResetDuration: 1 * time.Hour,
}
p.customGauges.values = make(map[string]prometheus.GaugeVec)
p.customCounters.values = make(map[string]prometheus.CounterVec)
Expand Down Expand Up @@ -292,13 +319,29 @@ func (p *Prometheus) register() {
)
p.mustRegister(p.reqCnt)

p.reqDur = prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: p.Namespace,
Subsystem: p.Subsystem,
Buckets: p.BucketsSize,
Name: p.RequestDurationMetricName,
Help: "The HTTP request latency bucket.",
}, []string{"method", "path", "host"})
var reqDurOpts prometheus.HistogramOpts
if p.nativeHistogram {
reqDurOpts = prometheus.HistogramOpts{
Namespace: p.Namespace,
Subsystem: p.Subsystem,
NativeHistogramBucketFactor: p.NativeHistogramBucketFactor,
NativeHistogramMaxBucketNumber: p.NativeHistogramMaxBucketNumber,
NativeHistogramMinResetDuration: p.NativeHistogramMinResetDuration,
Name: p.RequestDurationMetricName,
Help: "The HTTP request latency bucket.",
}

} else {
reqDurOpts = prometheus.HistogramOpts{
Namespace: p.Namespace,
Subsystem: p.Subsystem,
Buckets: p.BucketsSize,
Name: p.RequestDurationMetricName,
Help: "The HTTP request latency bucket.",
}
}

p.reqDur = prometheus.NewHistogramVec(reqDurOpts, []string{"method", "path", "host"})
p.mustRegister(p.reqDur)

p.reqSz = prometheus.NewSummary(
Expand Down
33 changes: 33 additions & 0 deletions prom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
io_prometheus_client "github.com/prometheus/client_model/go"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -495,6 +496,38 @@ func TestCustomHistogram(t *testing.T) {
})
}

func TestCustomNativeHistogram(t *testing.T) {
r := gin.New()
registry := prometheus.NewRegistry()
p := New(Engine(r), Registry(registry), NativeHistogram(true))
p.AddCustomHistogram("custom_histogram", "test histogram", []string{"url", "method"})
r.Use(p.Instrument())
defer unregister(p)

err := p.AddCustomHistogramValue("custom_histogram", []string{"http://example.com/status", "GET"}, 0.45)
assert.Nil(t, err)

mfs, err := registry.Gather()
assert.Nil(t, err)

found := false

for _, mf := range mfs {
if mf.GetType() == io_prometheus_client.MetricType_HISTOGRAM {
for _, m := range mf.Metric {
if mf.GetName() == "gin_gonic_custom_histogram" {
found = true
assert.Equal(t, int32(3), m.GetHistogram().GetSchema())
assert.Equal(t, uint64(0x1), m.GetHistogram().GetSampleCount())
assert.Equal(t, 0.45, m.GetHistogram().GetSampleSum())
}
}
}
}

assert.True(t, found)
}

func TestIgnore(t *testing.T) {
r := gin.New()
ipath := "/ping"
Expand Down