ProductPromotion
Logo

Go.Lang

made by https://0x3d.site

GitHub - cabify/gotoprom: Type-safe Prometheus metrics builder library for golang [managed by soy-programador]
Type-safe Prometheus metrics builder library for golang [managed by soy-programador] - cabify/gotoprom
Visit Site

GitHub - cabify/gotoprom: Type-safe Prometheus metrics builder library for golang [managed by soy-programador]

GitHub - cabify/gotoprom: Type-safe Prometheus metrics builder library for golang [managed by soy-programador]

gotoprom

A Prometheus metrics builder

Build Status Coverage Status GoDoc Mentioned in Awesome Go

gotoprom offers an easy to use declarative API with type-safe labels for building and using Prometheus metrics. It doesn't replace the official Prometheus client but adds a wrapper on top of it.

gotoprom is built for developers who like type safety, navigating the code using IDEs and using a “find usages” functionality, making refactoring and debugging easier at the cost of performance and writing slightly more verbose code.

Motivation

Main motivation for this library was to have type-safety on the Prometheus labels, which are just a map[string]string in the original library, and their values can be reported even without mentioning the label name, just relying on the order they were declared in.

For example, it replaces:

httpReqs := prometheus.NewCounterVec(
    prometheus.CounterOpts{
        Name: "http_requests_total",
        Help: "How many HTTP requests processed, partitioned by status code and HTTP method.",
    },
    []string{"code", "method"},
)
prometheus.MustRegister(httpReqs)

// ...

httpReqs.WithLabelValues("404", "POST").Add(42)

With:

var metrics = struct{
	Reqs func(labels) prometheus.Counter `name:"requests_total" help:"How many HTTP requests processed, partitioned by status code and HTTP method."`
}

type labels struct {
	Code   int    `label:"code"`
	Method string `label:"method"`
}

gotoprom.MustInit(&metrics, "http")

// ...

metrics.Reqs(labels{Code: 404, Method: "POST"}).Inc()

This way it's impossible to mess the call by exchanging the order of "POST" & "404" params.

Usage

Define your metrics:

var metrics struct {
	SomeCounter                      func() prometheus.Counter   `name:"some_counter" help:"some counter"`
	SomeHistogram                    func() prometheus.Histogram `name:"some_histogram" help:"Some histogram with default prometheus buckets" buckets:""`
	SomeHistogramWithSpecificBuckets func() prometheus.Histogram `name:"some_histogram_with_buckets" help:"Some histogram with custom buckets" buckets:".01,.05,.1"`
	SomeGauge                        func() prometheus.Gauge     `name:"some_gauge" help:"Some gauge"`
	SomeSummaryWithSpecificMaxAge    func() prometheus.Summary   `name:"some_summary_with_specific_max_age" help:"Some summary with custom max age" max_age:"20m" objectives:"0.50,0.95,0.99"`

	Requests struct {
		Total func(requestLabels) prometheus.Count `name:"total" help:"Total amount of requests served"`
	} `namespace:"requests"`
}

type requestLabels struct {
	Service    string `label:"service"`
	StatusCode int    `label:"status"`
	Success    bool   `label:"success"`
}

Initialize them:

func init() {
	gotoprom.MustInit(&metrics, "namespace")
}

Measure stuff:

metrics.SomeGauge().Set(100)
metrics.Requests.Total(requestLabels{Service: "google", StatusCode: 404, Success: false}).Inc()

Custom metric types

By default, only some basic metric types are registered when gotoprom is intialized:

  • prometheus.Counter
  • prometheus.Histogram
  • prometheus.Gauge
  • prometheus.Summary

You can extend this by adding more types, for instance, if you want to observe time and want to avoid repetitive code you can create a prometheusx.TimeHistogram:

package prometheusx

import (
	"reflect"
	"time"

	"github.com/cabify/gotoprom"
	"github.com/cabify/gotoprom/prometheusvanilla"
	"github.com/prometheus/client_golang/prometheus"
)

var (
	// TimeHistogramType is the reflect.Type of the TimeHistogram interface
	TimeHistogramType = reflect.TypeOf((*TimeHistogram)(nil)).Elem()
)

func init() {
	gotoprom.MustAddBuilder(TimeHistogramType, RegisterTimeHistogram)
}

// RegisterTimeHistogram registers a TimeHistogram after registering the underlying prometheus.Histogram in the prometheus.Registerer provided
// The function it returns returns a TimeHistogram type as an interface{}
func RegisterTimeHistogram(name, help, namespace string, labelNames []string, tag reflect.StructTag) (func(prometheus.Labels) interface{}, prometheus.Collector, error) {
	f, collector, err := prometheusvanilla.BuildHistogram(name, help, namespace, labelNames, tag)
	if err != nil {
		return nil, nil, err
	}

	return func(labels prometheus.Labels) interface{} {
		return timeHistogramAdapter{Histogram: f(labels).(prometheus.Histogram)}
	}, collector, nil
}

// TimeHistogram offers the basic prometheus.Histogram functionality
// with additional time-observing functions
type TimeHistogram interface {
	prometheus.Histogram
	// Duration observes the duration in seconds
	Duration(duration time.Duration)
	// Since observes the duration in seconds since the time point provided
	Since(time.Time)
}

type timeHistogramAdapter struct {
	prometheus.Histogram
}

// Duration observes the duration in seconds
func (to timeHistogramAdapter) Duration(duration time.Duration) {
	to.Observe(duration.Seconds())
}

// Since observes the duration in seconds since the time point provided
func (to timeHistogramAdapter) Since(duration time.Time) {
	to.Duration(time.Since(duration))
}

So you can later define it as:

var metrics struct {
	DurationSeconds func() prometheusx.TimeHistogram `name:"duration_seconds" help:"Duration in seconds" buckets:".001,.005,.01,.025,.05,.1"`
}

func init() {
	gotoprom.MustInit(&metrics, "requests")
}

And use it as:

// ...
defer metrics.DurationSeconds().Since(t0)
// ...

Replacing metric builders

If you don't like the default metric builders, you can replace the DefaultInitializer with your own one.

Performance

Obviously, there's a performance cost to perform the type-safety mapping magic to the original Prometheus client's API.

In general terms, it takes 3x to increment a counter than with vanilla Prometheus, which is around 600ns (we're talking about a portion of a microsecond, less than a thousandth of a millisecond)

$ go test -bench . -benchtime 3s
goos: darwin
goarch: amd64
pkg: github.com/cabify/gotoprom
BenchmarkVanilla-4    	10000000	       387 ns/op
BenchmarkGotoprom-4   	 5000000	      1049 ns/op
PASS
ok  	github.com/cabify/gotoprom	10.611s

In terms of memory, there's a also a 33% increase in terms of space, and 3x increase in allocations:

$ go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/cabify/gotoprom
BenchmarkVanilla-4    	 5000000	       381 ns/op	     336 B/op	       2 allocs/op
BenchmarkGotoprom-4   	 1000000	      1030 ns/op	     432 B/op	       6 allocs/op
PASS
ok  	github.com/cabify/gotoprom	3.369s

This costs are probably assumable in most of the applications, especially when measuring network accesses, etc. which are magnitudes higher.

Articles
to learn more about the golang concepts.

Resources
which are currently available to browse on.

mail [email protected] to add your project or resources here 🔥.

FAQ's
to know more about the topic.

mail [email protected] to add your project or resources here 🔥.

Queries
or most google FAQ's about GoLang.

mail [email protected] to add more queries here 🔍.

More Sites
to check out once you're finished browsing here.

0x3d
https://www.0x3d.site/
0x3d is designed for aggregating information.
NodeJS
https://nodejs.0x3d.site/
NodeJS Online Directory
Cross Platform
https://cross-platform.0x3d.site/
Cross Platform Online Directory
Open Source
https://open-source.0x3d.site/
Open Source Online Directory
Analytics
https://analytics.0x3d.site/
Analytics Online Directory
JavaScript
https://javascript.0x3d.site/
JavaScript Online Directory
GoLang
https://golang.0x3d.site/
GoLang Online Directory
Python
https://python.0x3d.site/
Python Online Directory
Swift
https://swift.0x3d.site/
Swift Online Directory
Rust
https://rust.0x3d.site/
Rust Online Directory
Scala
https://scala.0x3d.site/
Scala Online Directory
Ruby
https://ruby.0x3d.site/
Ruby Online Directory
Clojure
https://clojure.0x3d.site/
Clojure Online Directory
Elixir
https://elixir.0x3d.site/
Elixir Online Directory
Elm
https://elm.0x3d.site/
Elm Online Directory
Lua
https://lua.0x3d.site/
Lua Online Directory
C Programming
https://c-programming.0x3d.site/
C Programming Online Directory
C++ Programming
https://cpp-programming.0x3d.site/
C++ Programming Online Directory
R Programming
https://r-programming.0x3d.site/
R Programming Online Directory
Perl
https://perl.0x3d.site/
Perl Online Directory
Java
https://java.0x3d.site/
Java Online Directory
Kotlin
https://kotlin.0x3d.site/
Kotlin Online Directory
PHP
https://php.0x3d.site/
PHP Online Directory
React JS
https://react.0x3d.site/
React JS Online Directory
Angular
https://angular.0x3d.site/
Angular JS Online Directory