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 basic telemetry #1299

Merged
merged 12 commits into from
Oct 24, 2022
5 changes: 4 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ linters-settings:
revive:
ignore-generated-header: true
severity: warning
enableAllRules: true
AlekSi marked this conversation as resolved.
Show resolved Hide resolved
# TODO enable-all-rules: true
rules:
- name: exported
# TODO arguments: [checkPrivateReceivers]
staticcheck:
checks:
- all
Expand Down
2 changes: 1 addition & 1 deletion build/nfpm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ arch: ${GOARCH}
platform: linux
version: ${VERSION}
section: database
maintainer: FerretDB packages <packages@ferretdb.com>
maintainer: FerretDB packages <packages@ferretdb.io>
description: >
FerretDB (previously MangoDB) was founded to become the de-facto open-source substitute to MongoDB.
FerretDB is an open-source proxy, converting the MongoDB 6.0+ wire protocol queries to SQL -
Expand Down
55 changes: 54 additions & 1 deletion cmd/ferretdb/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ import (
"path/filepath"
"sort"
"strings"
"sync"
"time"

"github.com/AlekSi/pointer"
"github.com/alecthomas/kong"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/expfmt"
Expand All @@ -38,6 +41,7 @@ import (
"github.com/FerretDB/FerretDB/internal/util/debug"
"github.com/FerretDB/FerretDB/internal/util/logging"
"github.com/FerretDB/FerretDB/internal/util/state"
"github.com/FerretDB/FerretDB/internal/util/telemetry"
"github.com/FerretDB/FerretDB/internal/util/version"
)

Expand All @@ -58,6 +62,9 @@ var cli struct {

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

// TODO switch to string for 3 states
Telemetry bool `default:"false" help:"Enable basic telemetry. See https://beacon.ferretdb.io."`

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

PostgreSQLURL string `name:"postgresql-url" default:"${default_postgresql_url}" help:"PostgreSQL URL for 'pg' handler."`
Expand All @@ -69,6 +76,12 @@ var cli struct {

Test struct {
RecordsDir string `default:"" help:"Testing flag: directory for record files."`
Telemetry struct {
URL string `default:"https://beacon.ferretdb.io/" help:"Testing flag: telemetry URL."`
rumyantseva marked this conversation as resolved.
Show resolved Hide resolved
UndecidedDelay time.Duration `default:"1h" help:"Testing flag: delay for undecided state."`
ReportInterval time.Duration `default:"24h" help:"Testing flag: report interval."`
ReportTimeout time.Duration `default:"5s" help:"Testing flag: report timeout."`
} `embed:"" prefix:"telemetry-"`
} `embed:"" prefix:"test-"`
}

Expand Down Expand Up @@ -185,6 +198,19 @@ func setupLogger(stateProvider *state.Provider) *zap.Logger {
return l
}

// runTelemetryReporter runs telemetry reporter until ctx is canceled.
func runTelemetryReporter(ctx context.Context, enabled bool, opts *telemetry.NewReporterOpts) {
// TODO probably move out of this function
opts.P.Update(func(s *state.State) { s.Telemetry = pointer.ToBool(enabled) })

r, err := telemetry.NewReporter(opts)
if err != nil {
opts.L.Fatal("Failed to create telemetry reporter.", zap.Error(err))
}

r.Run(ctx)
}

// run sets up environment based on provided flags and runs FerretDB.
func run() {
if cli.Version {
Expand Down Expand Up @@ -212,7 +238,32 @@ func run() {
stop()
}()

go debug.RunHandler(ctx, cli.DebugAddr, metricsRegisterer, logger.Named("debug"))
var wg sync.WaitGroup

wg.Add(1)

go func() {
defer wg.Done()
debug.RunHandler(ctx, cli.DebugAddr, metricsRegisterer, logger.Named("debug"))
}()

wg.Add(1)

go func() {
defer wg.Done()
runTelemetryReporter(
ctx,
cli.Telemetry,
&telemetry.NewReporterOpts{
URL: cli.Test.Telemetry.URL,
P: stateProvider,
L: logger.Named("telemetry"),
UndecidedDelay: cli.Test.Telemetry.UndecidedDelay,
ReportInterval: cli.Test.Telemetry.ReportInterval,
ReportTimeout: cli.Test.Telemetry.ReportTimeout,
},
)
}()

cmdsList := maps.Keys(common.Commands)
sort.Strings(cmdsList)
Expand Down Expand Up @@ -257,6 +308,8 @@ func run() {
logger.Error("Listener stopped", zap.Error(err))
}

wg.Wait()

mfs, err := prometheus.DefaultGatherer.Gather()
if err != nil {
panic(err)
Expand Down
2 changes: 1 addition & 1 deletion internal/clientconn/connmetrics/listener_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type ListenerMetrics struct {
ConnMetrics *ConnMetrics
}

// newListenerMetrics creates new listener metrics.
// NewListenerMetrics creates new listener metrics.
//
// The cmds is the list of all expected commands that could be measured.
// After providing them, they will be set with the zero values.
Expand Down
13 changes: 12 additions & 1 deletion internal/util/state/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,19 @@ func (mc *metricsCollector) Collect(ch chan<- prometheus.Metric) {
"debug": strconv.FormatBool(v.Debug),
}

s := mc.p.Get()

switch {
case s.Telemetry == nil:
constLabels["telemetry"] = "undecided"
case *s.Telemetry:
constLabels["telemetry"] = "enabled"
default:
constLabels["telemetry"] = "disabled"
}

if mc.addUUIDToMetric {
constLabels["uuid"] = mc.p.Get().UUID
constLabels["uuid"] = s.UUID
}

ch <- prometheus.MustNewConstMetric(
Expand Down
4 changes: 2 additions & 2 deletions internal/util/state/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func TestMetrics(t *testing.T) {
`
# HELP ferretdb_up FerretDB instance state.
# TYPE ferretdb_up gauge
ferretdb_up{branch=%q,commit=%q,debug="%t",dirty="%t",uuid=%q,version=%q} 1
ferretdb_up{branch=%q,commit=%q,debug="%t",dirty="%t",telemetry="undecided",uuid=%q,version=%q} 1
`,
v.Branch, v.Commit, v.Debug, v.Dirty, uuid, v.Version,
)
Expand All @@ -69,7 +69,7 @@ func TestMetrics(t *testing.T) {
`
# HELP ferretdb_up FerretDB instance state.
# TYPE ferretdb_up gauge
ferretdb_up{branch=%q,commit=%q,debug="%t",dirty="%t",version=%q} 1
ferretdb_up{branch=%q,commit=%q,debug="%t",dirty="%t",telemetry="undecided",version=%q} 1
`,
v.Branch, v.Commit, v.Debug, v.Dirty, v.Version,
)
Expand Down
18 changes: 14 additions & 4 deletions internal/util/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,20 @@ package state
import (
"time"

"github.com/AlekSi/pointer"
"github.com/google/uuid"

"github.com/FerretDB/FerretDB/internal/util/must"
)

// State represents FerretDB process state.
type State struct {
UUID string `json:"uuid"`
UUID string `json:"uuid"`
Telemetry *bool `json:"telemetry,omitempty"` // nil for undecided

// never persisted
Start time.Time `json:"-"`
Start time.Time `json:"-"`
LatestVersion string `json:"-"`
}

// fill replaces all unset or invalid values with default.
Expand All @@ -44,8 +47,15 @@ func (s *State) fill() {

// deepCopy returns a deep copy of the state.
func (s *State) deepCopy() *State {
var telemetry *bool
if s.Telemetry != nil {
telemetry = pointer.ToBool(*s.Telemetry)
}

return &State{
UUID: s.UUID,
Start: s.Start,
UUID: s.UUID,
Telemetry: telemetry,
Start: s.Start,
LatestVersion: s.LatestVersion,
}
}