Fx module for prometheus.
go get github.com/ankorstore/yokai/fxmetrics
This module is intended to be used alongside:
To load the module in your Fx application:
package main
import (
"github.com/ankorstore/yokai/fxconfig"
"github.com/ankorstore/yokai/fxlog"
"github.com/ankorstore/yokai/fxmetrics"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/fx"
)
func main() {
fx.New(
fxconfig.FxConfigModule, // load the module dependencies
fxlog.FxLogModule,
fxmetrics.FxMetricsModule, // load the module
fx.Invoke(func(registry *prometheus.Registry) { // invoke the metrics registry
// ...
}),
).Run()
}
Configuration reference:
# ./configs/config.yaml
app:
name: app
env: dev
version: 0.1.0
debug: true
modules:
metrics:
collect:
build: true # to collect build infos metrics (disabled by default)
go: true # to collect go metrics (disabled by default)
process: true # to collect process metrics (disabled by default)
This module provides the possibility to register your metrics collectors in a common *prometheus.Registry
via AsMetricsCollector()
:
package main
import (
"github.com/ankorstore/yokai/fxconfig"
"github.com/ankorstore/yokai/fxlog"
"github.com/ankorstore/yokai/fxmetrics"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/fx"
)
var SomeCounter = prometheus.NewCounter(prometheus.CounterOpts{
Name: "some_total",
Help: "some help",
})
func main() {
fx.New(
fxconfig.FxConfigModule, // load the module dependencies
fxlog.FxLogModule,
fxmetrics.FxMetricsModule, // load the module
fx.Options(
fxmetrics.AsMetricsCollector(SomeCounter), // register the counter
),
fx.Invoke(func() {
SomeCounter.Inc() // manipulate the counter
}),
).Run()
}
Important: even if convenient, it's recommended to NOT use the promauto way of registering metrics,
but to use instead fxmetrics.AsMetricsCollector()
, as promauto
uses a global registry that leads to data race
conditions in testing.
Also, if you want to register several collectors at once, you can use fxmetrics.AsMetricsCollectors()
By default, the *prometheus.Registry
is created by the DefaultMetricsRegistryFactory.
If needed, you can provide your own factory and override the module:
package main
import (
"github.com/ankorstore/yokai/fxconfig"
"github.com/ankorstore/yokai/fxlog"
"github.com/ankorstore/yokai/fxmetrics"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/fx"
)
type CustomMetricsRegistryFactory struct{}
func NewCustomMetricsRegistryFactory() fxmetrics.MetricsRegistryFactory {
return &CustomMetricsRegistryFactory{}
}
func (f *CustomMetricsRegistryFactory) Create() (*prometheus.Registry, error) {
return prometheus.NewPedanticRegistry(), nil
}
func main() {
fx.New(
fxconfig.FxConfigModule, // load the module dependencies
fxlog.FxLogModule,
fxmetrics.FxMetricsModule, // load the module
fx.Decorate(NewCustomMetricsRegistryFactory), // override the module with a custom factory
fx.Invoke(func(registry *prometheus.Registry) { // invoke the custom registry
// ...
}),
).Run()
}
This module provides the possibility to easily test your metrics with the prometheus package testutil helpers.
package main_test
import (
"strings"
"testing"
"github.com/ankorstore/yokai/fxconfig"
"github.com/ankorstore/yokai/fxlog"
"github.com/ankorstore/yokai/fxmetrics"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
"go.uber.org/fx"
"go.uber.org/fx/fxtest"
)
var SomeCounter = prometheus.NewCounter(prometheus.CounterOpts{
Name: "some_total",
Help: "some help",
})
func TestSomeCounter(t *testing.T) {
t.Setenv("APP_CONFIG_PATH", "testdata/config")
var registry *prometheus.Registry
fxtest.New(
t,
fx.NopLogger,
fxconfig.FxConfigModule,
fxlog.FxLogModule,
fxmetrics.FxMetricsModule,
fx.Options(
fxmetrics.AsMetricsCollector(SomeCounter),
),
fx.Invoke(func() {
SomeCounter.Add(9)
}),
fx.Populate(®istry),
).RequireStart().RequireStop()
// metric assertions
expectedHelp := `
# HELP some_total some help
# TYPE some_total counter
`
expectedMetric := `
some_total 9
`
err := testutil.GatherAndCompare(
registry,
strings.NewReader(expectedHelp+expectedMetric),
"some_total",
)
assert.NoError(t, err)
}