Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UUID to Prometheus metrics if requested #1240

Merged
merged 16 commits into from
Oct 12, 2022
15 changes: 12 additions & 3 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ linters-settings:
- github.com/FerretDB/FerretDB/internal/bson
- github.com/FerretDB/FerretDB/internal/handlers/pg/pgdb
- github.com/FerretDB/FerretDB/internal/handlers/pg/pjson
- github.com/FerretDB/FerretDB/internal/handlers/tigris/tigrisdb
- github.com/FerretDB/FerretDB/internal/tjson
- github.com/FerretDB/FerretDB/internal/types/fjson
- github.com/tigrisdata/tigris-client-go/api/client/v1/api
Expand Down Expand Up @@ -139,7 +140,7 @@ issues:
path: internal/wire
text: fjson

# only `pg` handler can import `pgdb` package, not other handler can do that
# only `pg` handler can import `pgdb` package, no other handler can do that
- linters: [depguard]
path: internal/handlers/pg
text: pgdb
Expand All @@ -156,7 +157,7 @@ issues:
path: internal/handlers/registry
text: pgdb

# only `pg` handler can import `pjson` package, not other handler can do that
# only `pg` handler can import `pjson` package, no other handler can do that
- linters: [depguard]
path: internal/handlers/pg
text: pjson
Expand All @@ -167,7 +168,15 @@ issues:
path: internal/util/testutil
text: pjson

# only `tigris` handler can import `tjson` package, not other handler can do that
# only `tigris` handler can import `tigrisdb` package, no other handler can do that
- linters: [depguard]
path: internal/handlers/tigris
text: tigrisdb
- linters: [depguard]
path: internal/handlers/registry
text: tigrisdb

# only `tigris` handler can import `tjson` package, no other handler can do that
- linters: [depguard]
path: internal/handlers/tigris
text: tjson
Expand Down
3 changes: 2 additions & 1 deletion cmd/envtool/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

"github.com/alecthomas/kong"
"github.com/jackc/pgx/v4"
"github.com/prometheus/client_golang/prometheus"
"github.com/tigrisdata/tigris-client-go/config"
"github.com/tigrisdata/tigris-client-go/driver"
"go.uber.org/zap"
Expand Down Expand Up @@ -259,7 +260,7 @@ func setupTigris(ctx context.Context, logger *zap.SugaredLogger) error {

// run runs all setup commands.
func run(ctx context.Context, logger *zap.SugaredLogger) error {
go debug.RunHandler(ctx, "127.0.0.1:8089", logger.Named("debug").Desugar())
go debug.RunHandler(ctx, "127.0.0.1:8089", prometheus.DefaultRegisterer, logger.Named("debug").Desugar())

if err := setupPostgres(ctx, logger); err != nil {
return err
Expand Down
94 changes: 68 additions & 26 deletions cmd/ferretdb/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,17 @@ import (
var cli struct {
ListenAddr string `default:"127.0.0.1:27017" help:"Listen address."`
ProxyAddr string `default:"127.0.0.1:37017" help:"Proxy address."`
DebugAddr string `default:"127.0.0.1:8088" help:"Debug address."`
DebugAddr string `default:"127.0.0.1:8088" help:"${help_debug_addr}"`
StateDir string `default:"." help:"Process state directory."`
Mode string `default:"${default_mode}" help:"${help_mode}" enum:"${enum_mode}"`

Log struct {
Level string `default:"${default_log_level}" help:"${help_log_level}"`
UUID bool `default:"false" help:"Add instance UUID to log messages."`
UUID bool `default:"false" help:"Add instance UUID to all log messages."`
} `embed:"" prefix:"log-"`

MetricsUUID bool `default:"false" help:"Add instance UUID to all metrics."`

Handler string `default:"pg" help:"${help_handler}"`

PostgresURL string `default:"postgres://postgres@127.0.0.1:5432/ferretdb" help:"PostgreSQL URL for 'pg' handler."`
Expand Down Expand Up @@ -80,6 +82,7 @@ var (
"default_log_level": zap.DebugLevel.String(),
"default_mode": clientconn.AllModes[0],

"help_debug_addr": "Debug address for /debug/metrics, /debug/pprof, and similar HTTP handlers.",
"help_log_level": fmt.Sprintf(
"Log level: '%s'. Debug level also enables development mode.",
strings.Join(logLevels, "', '"),
Expand Down Expand Up @@ -107,24 +110,14 @@ func main() {
run()
}

// run sets up environment based on provided flags and runs FerretDB.
func run() {
info := version.Get()

if cli.Version {
fmt.Fprintln(os.Stdout, "version:", info.Version)
fmt.Fprintln(os.Stdout, "commit:", info.Commit)
fmt.Fprintln(os.Stdout, "branch:", info.Branch)
fmt.Fprintln(os.Stdout, "dirty:", info.Dirty)
return
}

stateFile, err := filepath.Abs(filepath.Join(cli.StateDir, "state.json"))
// setupState setups state provider.
func setupState() (*state.Provider, string) {
f, err := filepath.Abs(filepath.Join(cli.StateDir, "state.json"))
if err != nil {
log.Fatalf("Failed to get path for state file: %s.", err)
}

p, err := state.NewProvider(stateFile)
p, err := state.NewProvider(f)
if err != nil {
log.Fatalf("Failed to create state provider: %s.", err)
}
Expand All @@ -134,12 +127,34 @@ func run() {
log.Fatalf("Failed to get state: %s.", err)
}

level, err := zapcore.ParseLevel(cli.Log.Level)
if err != nil {
log.Fatal(err)
return p, s.UUID
}

// setupMetrics setups Prometheus metrics registerer with some metrics.
func setupMetrics(stateProvider *state.Provider, uuid string) prometheus.Registerer {
r := prometheus.WrapRegistererWith(
prometheus.Labels{"uuid": uuid},
prometheus.DefaultRegisterer,
)
m := stateProvider.MetricsCollector(false)

// Unless requested, don't add UUID to all metrics, but add it to one.
// See https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
if !cli.MetricsUUID {
r = prometheus.DefaultRegisterer
m = stateProvider.MetricsCollector(true)
}

logUUID := s.UUID
r.MustRegister(m)

return r
}

// setupLogger setups zap logger.
func setupLogger(uuid string) *zap.Logger {
info := version.Get()

logUUID := uuid
startupFields := []zap.Field{
zap.String("version", info.Version),
zap.String("commit", info.Commit),
Expand All @@ -149,16 +164,43 @@ func run() {
zap.Reflect("buildEnvironment", info.BuildEnvironment.Map()),
}

// don't add UUID to all messages, but log it once at startup
// Similarly to Prometheus, unless requested, don't add UUID to all messages, but log it once at startup.
if !cli.Log.UUID {
logUUID = ""
startupFields = append(startupFields, zap.String("uuid", s.UUID))
startupFields = append(startupFields, zap.String("uuid", uuid))
}

level, err := zapcore.ParseLevel(cli.Log.Level)
if err != nil {
log.Fatal(err)
}

logging.Setup(level, logUUID)
logger := zap.L()
l := zap.L()

l.Info("Starting FerretDB "+info.Version+"...", startupFields...)

return l
}

// run sets up environment based on provided flags and runs FerretDB.
func run() {
if cli.Version {
info := version.Get()

fmt.Fprintln(os.Stdout, "version:", info.Version)
fmt.Fprintln(os.Stdout, "commit:", info.Commit)
fmt.Fprintln(os.Stdout, "branch:", info.Branch)
fmt.Fprintln(os.Stdout, "dirty:", info.Dirty)

return
}

stateProvider, uuid := setupState()

metricsRegisterer := setupMetrics(stateProvider, uuid)

logger.Info("Starting FerretDB "+info.Version+"...", startupFields...)
logger := setupLogger(uuid)

ctx, stop := notifyAppTermination(context.Background())
go func() {
Expand All @@ -167,7 +209,7 @@ func run() {
stop()
}()

go debug.RunHandler(ctx, cli.DebugAddr, logger.Named("debug"))
go debug.RunHandler(ctx, cli.DebugAddr, metricsRegisterer, logger.Named("debug"))

h, err := registry.NewHandler(cli.Handler, &registry.NewHandlerOpts{
Ctx: ctx,
Expand Down Expand Up @@ -195,7 +237,7 @@ func run() {
TestRecordsDir: cli.Test.RecordsDir,
})

prometheus.DefaultRegisterer.MustRegister(l)
metricsRegisterer.MustRegister(l)

err = l.Run(ctx)
if err == nil || err == context.Canceled {
Expand Down
8 changes: 4 additions & 4 deletions integration/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,23 @@ tasks:
cmds:
- go generate -x ./...
sources:
- "**/*.go"
- "../**/*.go"

lint:
cmds:
- go vet -vettool=../bin/checkswitch{{exeExt}} ./...
- ../bin/golangci-lint{{exeExt}} run --config=../.golangci-new.yml
sources:
- "**/*.go"
- "../**/*.go"

lint-golangci-lint:
cmds:
- ../bin/golangci-lint{{exeExt}} run --config=.golangci.yml
sources:
- "**/*.go"
- "../**/*.go"

lint-go-consistent:
cmds:
- ../bin/go-consistent{{exeExt}} -v -pedantic ./...
sources:
- "**/*.go"
- "../**/*.go"
2 changes: 1 addition & 1 deletion integration/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ replace github.com/FerretDB/FerretDB => ../
require (
github.com/AlekSi/pointer v1.2.0
github.com/FerretDB/FerretDB v0.0.0-00010101000000-000000000000
github.com/prometheus/client_golang v1.13.0
github.com/stretchr/testify v1.8.0
go.mongodb.org/mongo-driver v1.10.3
go.uber.org/zap v1.23.0
Expand Down Expand Up @@ -50,7 +51,6 @@ require (
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.13.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
Expand Down
3 changes: 2 additions & 1 deletion integration/setup/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"testing"
"time"

"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
Expand Down Expand Up @@ -188,7 +189,7 @@ func startup() {
startupOnce.Do(func() {
logging.Setup(zap.DebugLevel, "")

go debug.RunHandler(context.Background(), "127.0.0.1:0", zap.L().Named("debug"))
go debug.RunHandler(context.Background(), "127.0.0.1:0", prometheus.DefaultRegisterer, zap.L().Named("debug"))

if p := *targetPortF; p == 0 {
zap.S().Infof("Target system: in-process FerretDB with %q handler.", *handlerF)
Expand Down
25 changes: 15 additions & 10 deletions internal/clientconn/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,16 +434,6 @@ func (c *conn) handleOpMsg(ctx context.Context, msg *wire.OpMsg, cmd string) (*w
return nil, common.NewErrorMsg(common.ErrCommandNotFound, errMsg)
}

// Describe implements prometheus.Collector.
func (c *conn) Describe(ch chan<- *prometheus.Desc) {
c.m.Describe(ch)
}

// Collect implements prometheus.Collector.
func (c *conn) Collect(ch chan<- prometheus.Metric) {
c.m.Collect(ch)
}

// logResponse logs response's header and body and returns the log level that was used.
//
// The param `who` will be used in logs and should represent the type of the response,
Expand Down Expand Up @@ -475,3 +465,18 @@ func (c *conn) logResponse(who string, resHeader *wire.MsgHeader, resBody wire.M

return level
}

// Describe implements prometheus.Collector.
func (c *conn) Describe(ch chan<- *prometheus.Desc) {
c.m.Describe(ch)
}

// Collect implements prometheus.Collector.
func (c *conn) Collect(ch chan<- prometheus.Metric) {
c.m.Collect(ch)
}

// check interfaces
var (
_ prometheus.Collector = (*conn)(nil)
)
5 changes: 5 additions & 0 deletions internal/clientconn/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,8 @@ func (l *Listener) Describe(ch chan<- *prometheus.Desc) {
func (l *Listener) Collect(ch chan<- prometheus.Metric) {
l.metrics.Collect(ch)
}

// check interfaces
var (
_ prometheus.Collector = (*Listener)(nil)
)
6 changes: 3 additions & 3 deletions internal/util/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ import (
)

// RunHandler runs debug handler.
func RunHandler(ctx context.Context, addr string, l *zap.Logger) {
func RunHandler(ctx context.Context, addr string, r prometheus.Registerer, l *zap.Logger) {
stdL, err := zap.NewStdLogAt(l, zap.WarnLevel)
if err != nil {
panic(err)
}

http.Handle("/debug/metrics", promhttp.InstrumentMetricHandler(
prometheus.DefaultRegisterer, promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{
r, promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{
ErrorLog: stdL,
ErrorHandling: promhttp.ContinueOnError,
Registry: prometheus.DefaultRegisterer,
Registry: r,
EnableOpenMetrics: true,
}),
))
Expand Down
Loading