vimebu provides a type-safe builder to create VictoriaMetrics compatible metrics.
It aims to be as CPU & memory efficient as possible using strategies such as object pooling, buffer reuse etc.
go get -u github.com/wazazaby/vimebu/v2
import (
"github.com/VictoriaMetrics/metrics"
"github.com/wazazaby/vimebu/v2"
)
// Only using the builder.
var requestsTotalCounter = metrics.NewCounter(
vimebu.
Metric("request_total").
LabelString("path", "/foo/bar").
String(), // request_total{path="/foo/bar"}
)
// Registering the metric using the provided helpers.
var updateTotalCounterV3 = vimebu.
Metric("update_total").
LabelInt("version", 3).
NewCounter() // update_total{version="3"}vimebu is even more useful when you want to build metrics with variable label values.
import (
"net"
"github.com/wazazaby/vimebu/v2"
)
func getCassandraQueryCounter(name string, host net.IP, err error) *metrics.Counter {
return vimebu.Metric("cassandra_query_total").
LabelString("name", name).
LabelStringer("host", host).
LabelErrorQuote("error", err). // The label "error" won't be added if err is nil.
GetOrCreateCounter() // cassandra_query_total{name="beep",host="1.2.3.4",error="i/o timeout"}
}You can also have metrics with labels that are added under certain conditions.
import (
"github.com/VictoriaMetrics/metrics"
"github.com/wazazaby/vimebu/v2"
)
func getHTTPRequestCounter(host string) *metrics.Counter {
builder := vimebu.Metric("api_http_requests_total")
if host != "" {
builder.LabelString("host", host)
}
return builder.GetOrCreateCounter() // api_http_requests_total or api_http_requests_total{host="api.app.com"}
}vimebu also exposes a way to escape quotes on label values you don't control using the following methods :
Builder.LabelStringQuoteBuilder.LabelStringerQuoteBuilder.LabelErrorQuote
import (
"github.com/VictoriaMetrics/metrics"
"github.com/wazazaby/vimebu/v2"
)
func getHTTPRequestCounter(path string) *metrics.Counter {
return vimebu.Metric("api_http_requests_total").
LabelQuote("path", path).
GetOrCreateCounter() // api_http_requests_total{path="some/bro\"ken/path"}
}You can use these methods to append specific value types to the builder :
Builder.LabelBoolfor booleansBuilder.LabelIntand variations for signed integersBuilder.LabelUintand variations for unsigned integersBuilder.LabelFloatand variations for floatsBuilder.LabelStringerfor values implementing thefmt.StringerinterfaceBuilder.LabelErrorfor values implementing theerrorinterface
Here are some simple benchmarks comparing building a metric using the fmt package vs vimebu.
Each metric is built with 4 labels (string, int, error and bool).
As you can see, in the sequential benchmarks, vimebu is about twice as fast. For the parralel benchmarks, vimebu is about ~30% faster.
In each case, it allocates half as much per operation. Yay!
❯ go test -bench="BenchmarkCompare" -benchmem -run=NONE
goos: darwin
goarch: arm64
pkg: github.com/wazazaby/vimebu/v2
cpu: Apple M1 Max
BenchmarkCompareSequentialFmt-10 717055 1660 ns/op 1024 B/op 16 allocs/op
BenchmarkCompareParralelFmt-10 2279166 528.0 ns/op 1024 B/op 16 allocs/op
BenchmarkCompareSequentialVimebu-10 1557618 765.0 ns/op 896 B/op 8 allocs/op
BenchmarkCompareParralelVimebu-10 3098870 398.5 ns/op 896 B/op 8 allocs/op
PASS
ok github.com/wazazaby/vimebu/v2 7.445s
Builders can be acquired and released using a BuilderPool, which is a wrapper around a sync.Pool instance.
A default BuilderPool instance is created and exposed by the package, it is accessible like this :
import (
"github.com/VictoriaMetrics/metrics"
"github.com/wazazaby/vimebu/v2"
)
func getHTTPRequestCounter(path string) *metrics.Counter {
builder := vimebu.AcquireBuilder()
defer vimebu.ReleaseBuilder(builder)
builder.LabelQuote("path", path)
return builder.GetOrCreateCounter() // api_http_requests_total{path="some/bro\"ken/path"}
}Using a pool allows for reusing objects, thus relieving pressure on the garbage collector.
Understanding that this syntax can be quite verbose, vimebu also provides a simpler API that manages the lifecycle
of these objects internally by using the vimebu.Metric package level function.
Here, vimebu will automatically acquire a Builder, to finally reset and release it when the Builder.String method is called.
- A Builder instance is not safe to use from concurrently running goroutines
- A Builder instance must not be copied (it embeds a noOp
sync.Lockerimplementation, to raise warnings withgo vetwhen copied)