Skip to content

Commit

Permalink
Various improvements (#42)
Browse files Browse the repository at this point in the history
* doc: add README sections for HandlerNameFunc and RequestPathFunc options

* refactor: extract options in their own file

* refactor: type options for better readability

* feat: push coverage to 100%
  • Loading branch information
mlevieux authored Apr 25, 2023
1 parent 9f1d545 commit 18484e5
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 137 deletions.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,48 @@ p := ginprom.New(
r.Use(p.Instrument())
```

### HandlerNameFunc

Change the way the `handler` label is computed. By default, the `(*gin.Context).HandlerName`
function is used.
This option is useful when wanting to group different functions under
the same `handler` label or when using `gin` with decorated handlers.

```go
r := gin.Default()
p := ginprom.New(
HandlerNameFunc(func (c *gin.Context) string {
return "my handler"
}),
)
r.Use(p.Instrument())
```

### RequestPathFunc

Change how the `path` label is computed. By default, the `(*gin.Context).FullPath` function
is used.
This option is useful when wanting to group different requests under the same `path`
label or when wanting to process unknown routes (the default `(*gin.Context).FullPath` returns
an empty string for unregistered routes). Note that requests for which `f` returns the empty
string are ignored.

To specifically ignore certain paths, see the [Ignore](#ignore) option.

```go
r := gin.Default()
p := ginprom.New(
// record a metric for unregistered routes under the path label "<unknown>"
RequestPathFunc(func (c *gin.Context) string {
if fullpath := c.FullPath(); fullpath != "" {
return fullpath
}
return "<unknown>"
}),
)
r.Use(p.Instrument())
```

### Ignore

Ignore allows to completely ignore some routes. Even though you can apply the
Expand Down
144 changes: 144 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package ginprom

import (
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus"
)

type PrometheusOption func(*Prometheus)

// Path is an option allowing to set the metrics path when initializing with New.
func Path(path string) PrometheusOption {
return func(p *Prometheus) {
p.MetricsPath = path
}
}

// Ignore is used to disable instrumentation on some routes.
func Ignore(paths ...string) PrometheusOption {
return func(p *Prometheus) {
p.Ignored.Lock()
defer p.Ignored.Unlock()
for _, path := range paths {
p.Ignored.values[path] = true
}
}
}

// BucketSize is used to define the default bucket size when initializing with
// New.
func BucketSize(b []float64) PrometheusOption {
return func(p *Prometheus) {
p.BucketsSize = b
}
}

// Subsystem is an option allowing to set the subsystem when initializing
// with New.
func Subsystem(sub string) PrometheusOption {
return func(p *Prometheus) {
p.Subsystem = sub
}
}

// Namespace is an option allowing to set the namespace when initializing
// with New.
func Namespace(ns string) PrometheusOption {
return func(p *Prometheus) {
p.Namespace = ns
}
}

// Token is an option allowing to set the bearer token in prometheus
// with New.
// Example: ginprom.New(ginprom.Token("your_custom_token"))
func Token(token string) PrometheusOption {
return func(p *Prometheus) {
p.Token = token
}
}

// RequestCounterMetricName is an option allowing to set the request counter metric name.
func RequestCounterMetricName(reqCntMetricName string) PrometheusOption {
return func(p *Prometheus) {
p.RequestCounterMetricName = reqCntMetricName
}
}

// RequestDurationMetricName is an option allowing to set the request duration metric name.
func RequestDurationMetricName(reqDurMetricName string) PrometheusOption {
return func(p *Prometheus) {
p.RequestDurationMetricName = reqDurMetricName
}
}

// RequestSizeMetricName is an option allowing to set the request size metric name.
func RequestSizeMetricName(reqSzMetricName string) PrometheusOption {
return func(p *Prometheus) {
p.RequestSizeMetricName = reqSzMetricName
}
}

// ResponseSizeMetricName is an option allowing to set the response size metric name.
func ResponseSizeMetricName(resDurMetricName string) PrometheusOption {
return func(p *Prometheus) {
p.ResponseSizeMetricName = resDurMetricName
}
}

// Engine is an option allowing to set the gin engine when intializing with New.
// Example:
// r := gin.Default()
// p := ginprom.New(Engine(r))
func Engine(e *gin.Engine) PrometheusOption {
return func(p *Prometheus) {
p.Engine = e
}
}

// Registry is an option allowing to set a *prometheus.Registry with New.
// Use this option if you want to use a custom Registry instead of a global one that prometheus
// client uses by default
// Example:
// r := gin.Default()
// p := ginprom.New(Registry(r))
func Registry(r *prometheus.Registry) PrometheusOption {
return func(p *Prometheus) {
p.Registry = r
}
}

// HandlerNameFunc is an option allowing to set the HandlerNameFunc with New.
// Use this option if you want to override the default behavior (i.e. using
// (*gin.Context).HandlerName). This is useful when wanting to group different
// functions under the same "handler" label or when using gin with decorated handlers
// Example:
// r := gin.Default()
// p := ginprom.New(HandlerNameFunc(func (c *gin.Context) string { return "my handler" }))
func HandlerNameFunc(f func(c *gin.Context) string) PrometheusOption {
return func(p *Prometheus) {
p.HandlerNameFunc = f
}
}

// RequestPathFunc is an option allowing to set the RequestPathFunc with New.
// Use this option if you want to override the default behavior (i.e. using
// (*gin.Context).FullPath). This is useful when wanting to group different requests
// under the same "path" label or when wanting to process unknown routes (the default
// (*gin.Context).FullPath return an empty string for unregistered routes). Note that
// requests for which f returns the empty string are ignored.
// To specifically ignore certain paths, see the Ignore option.
// Example:
//
// r := gin.Default()
// p := ginprom.New(RequestPathFunc(func (c *gin.Context) string {
// if fullpath := c.FullPath(); fullpath != "" {
// return fullpath
// }
// return "<unknown>"
// }))
func RequestPathFunc(f func(c *gin.Context) string) PrometheusOption {
return func(p *Prometheus) {
p.RequestPathFunc = f
}
}
138 changes: 1 addition & 137 deletions prom.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,147 +203,11 @@ func (p *Prometheus) mustRegister(c ...prometheus.Collector) {
registerer.MustRegister(c...)
}

// Path is an option allowing to set the metrics path when initializing with New.
func Path(path string) func(*Prometheus) {
return func(p *Prometheus) {
p.MetricsPath = path
}
}

// Ignore is used to disable instrumentation on some routes.
func Ignore(paths ...string) func(*Prometheus) {
return func(p *Prometheus) {
p.Ignored.Lock()
defer p.Ignored.Unlock()
for _, path := range paths {
p.Ignored.values[path] = true
}
}
}

// BucketSize is used to define the default bucket size when initializing with
// New.
func BucketSize(b []float64) func(*Prometheus) {
return func(p *Prometheus) {
p.BucketsSize = b
}
}

// Subsystem is an option allowing to set the subsystem when initializing
// with New.
func Subsystem(sub string) func(*Prometheus) {
return func(p *Prometheus) {
p.Subsystem = sub
}
}

// Namespace is an option allowing to set the namespace when initializing
// with New.
func Namespace(ns string) func(*Prometheus) {
return func(p *Prometheus) {
p.Namespace = ns
}
}

// Token is an option allowing to set the bearer token in prometheus
// with New.
// Example: ginprom.New(ginprom.Token("your_custom_token"))
func Token(token string) func(*Prometheus) {
return func(p *Prometheus) {
p.Token = token
}
}

// RequestCounterMetricName is an option allowing to set the request counter metric name.
func RequestCounterMetricName(reqCntMetricName string) func(*Prometheus) {
return func(p *Prometheus) {
p.RequestCounterMetricName = reqCntMetricName
}
}

// RequestDurationMetricName is an option allowing to set the request duration metric name.
func RequestDurationMetricName(reqDurMetricName string) func(*Prometheus) {
return func(p *Prometheus) {
p.RequestDurationMetricName = reqDurMetricName
}
}

// RequestSizeMetricName is an option allowing to set the request size metric name.
func RequestSizeMetricName(reqSzMetricName string) func(*Prometheus) {
return func(p *Prometheus) {
p.RequestSizeMetricName = reqSzMetricName
}
}

// ResponseSizeMetricName is an option allowing to set the response size metric name.
func ResponseSizeMetricName(resDurMetricName string) func(*Prometheus) {
return func(p *Prometheus) {
p.ResponseSizeMetricName = resDurMetricName
}
}

// Engine is an option allowing to set the gin engine when intializing with New.
// Example:
// r := gin.Default()
// p := ginprom.New(Engine(r))
func Engine(e *gin.Engine) func(*Prometheus) {
return func(p *Prometheus) {
p.Engine = e
}
}

// Registry is an option allowing to set a *prometheus.Registry with New.
// Use this option if you want to use a custom Registry instead of a global one that prometheus
// client uses by default
// Example:
// r := gin.Default()
// p := ginprom.New(Registry(r))
func Registry(r *prometheus.Registry) func(*Prometheus) {
return func(p *Prometheus) {
p.Registry = r
}
}

// HandlerNameFunc is an option allowing to set the HandlerNameFunc with New.
// Use this option if you want to override the default behavior (i.e. using
// (*gin.Context).HandlerName). This is useful when wanting to group different
// functions under the same "handler" label or when using gin with decorated handlers
// Example:
// r := gin.Default()
// p := ginprom.New(HandlerNameFunc(func (c *gin.Context) string { return "my handler" }))
func HandlerNameFunc(f func(c *gin.Context) string) func(*Prometheus) {
return func(p *Prometheus) {
p.HandlerNameFunc = f
}
}

// RequestPathFunc is an option allowing to set the RequestPathFunc with New.
// Use this option if you want to override the default behavior (i.e. using
// (*gin.Context).FullPath). This is useful when wanting to group different requests
// under the same "path" label or when wanting to process unknown routes (the default
// (*gin.Context).FullPath return an empty string for unregistered routes). Note that
// requests for which f returns the empty string are ignored.
// To specifically ignore certain paths, see the Ignore option.
// Example:
//
// r := gin.Default()
// p := ginprom.New(RequestPathFunc(func (c *gin.Context) string {
// if fullpath := c.FullPath(); fullpath != "" {
// return fullpath
// }
// return "<unknown>"
// }))
func RequestPathFunc(f func(c *gin.Context) string) func(*Prometheus) {
return func(p *Prometheus) {
p.RequestPathFunc = f
}
}

// New will initialize a new Prometheus instance with the given options.
// If no options are passed, sane defaults are used.
// If a router is passed using the Engine() option, this instance will
// automatically bind to it.
func New(options ...func(*Prometheus)) *Prometheus {
func New(options ...PrometheusOption) *Prometheus {
p := &Prometheus{
MetricsPath: defaultPath,
Namespace: defaultNs,
Expand Down
17 changes: 17 additions & 0 deletions prom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,23 @@ func TestMetricsBearerToken(t *testing.T) {
unregister(p)
}

func TestCustomCounterErr(t *testing.T) {
p := New()
assert.Equal(t, p.IncrementCounterValue("not_found", []string{"some", "labels"}), ErrCustomCounter)
assert.Equal(t, p.AddCounterValue("not_found", []string{"some", "labels"}, 1.), ErrCustomCounter)
unregister(p)
}

func TestCustomGaugeErr(t *testing.T) {
p := New()
assert.Equal(t, p.IncrementGaugeValue("not_found", []string{"some", "labels"}), ErrCustomGauge)
assert.Equal(t, p.DecrementGaugeValue("not_found", []string{"some", "labels"}), ErrCustomGauge)
assert.Equal(t, p.AddGaugeValue("not_found", []string{"some", "labels"}, 1.), ErrCustomGauge)
assert.Equal(t, p.SubGaugeValue("not_found", []string{"some", "labels"}, 1.), ErrCustomGauge)
assert.Equal(t, p.SetGaugeValue("not_found", []string{"some", "labels"}, 1.), ErrCustomGauge)
unregister(p)
}

func TestInstrumentCustomCounter(t *testing.T) {
var helpText = "help text"
var labels = []string{"label1"}
Expand Down

0 comments on commit 18484e5

Please sign in to comment.