Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 41 additions & 27 deletions collector/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"sync"
"time"

"github.com/alecthomas/kingpin/v2"
"github.com/go-sql-driver/mysql"
"github.com/prometheus/client_golang/prometheus"
)
Expand All @@ -40,22 +39,6 @@ const (
timeoutParam = `lock_wait_timeout=%d`
)

// Tunable flags.
var (
exporterLockTimeout = kingpin.Flag(
"exporter.lock_wait_timeout",
"Set a lock_wait_timeout (in seconds) on the connection to avoid long metadata locking.",
).Default("2").Int()
enableExporterLockTimeout = kingpin.Flag(
"exporter.enable_lock_wait_timeout",
"Enable the lock_wait_timeout MySQL connection parameter.",
).Default("true").Bool()
slowLogFilter = kingpin.Flag(
"exporter.log_slow_filter",
"Add a log_slow_filter to avoid slow query logging of scrapes. NOTE: Not supported by Oracle MySQL.",
).Default("false").Bool()
)

// metric definition
var (
mysqlUp = prometheus.NewDesc(
Expand Down Expand Up @@ -87,19 +70,53 @@ type Exporter struct {
dsn string
scrapers []Scraper
instance *instance

enableLockWaitTimeout bool
lockWaitTimeout int
slowLogFilter bool
}

type ExporterOpt func(*Exporter)

func EnableLockWaitTimeout(b bool) ExporterOpt {
return func(e *Exporter) {
e.enableLockWaitTimeout = b
}
}

func SetLockWaitTimeout(timeout int) ExporterOpt {
return func(e *Exporter) {
e.lockWaitTimeout = timeout
}
}

func SetSlowLogFilter(b bool) ExporterOpt {
return func(e *Exporter) {
e.slowLogFilter = b
}
}

// New returns a new MySQL exporter for the provided DSN.
func New(ctx context.Context, dsn string, scrapers []Scraper, logger *slog.Logger) *Exporter {
func New(ctx context.Context, dsn string, scrapers []Scraper, logger *slog.Logger, opts ...ExporterOpt) *Exporter {
e := &Exporter{
ctx: ctx,
logger: logger,
scrapers: scrapers,
}

for _, opt := range opts {
opt(e)
}

// Setup extra params for the DSN
dsnParams := []string{}

// Only set lock_wait_timeout if it is enabled
if *enableExporterLockTimeout {
dsnParams = append(dsnParams, fmt.Sprintf(timeoutParam, *exporterLockTimeout))
if e.enableLockWaitTimeout {
dsnParams = append(dsnParams, fmt.Sprintf(timeoutParam, e.lockWaitTimeout))
}

if *slowLogFilter {
if e.slowLogFilter {
dsnParams = append(dsnParams, sessionSettingsParam)
}

Expand All @@ -110,12 +127,9 @@ func New(ctx context.Context, dsn string, scrapers []Scraper, logger *slog.Logge
}
dsn += strings.Join(dsnParams, "&")

return &Exporter{
ctx: ctx,
logger: logger,
dsn: dsn,
scrapers: scrapers,
}
e.dsn = dsn

return e
}

// Describe implements prometheus.Collector.
Expand Down
77 changes: 77 additions & 0 deletions collector/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,80 @@ func TestExporter(t *testing.T) {
}
})
}

func TestExporterWithOpts(t *testing.T) {
convey.Convey("DSN changes with options", t, func() {
convey.Convey("without any option", func() {
exporter := New(
context.Background(),
dsn,
[]Scraper{},
promslog.NewNopLogger(),
)
convey.So(exporter.dsn, convey.ShouldEqual, "root@/mysql?")
})

convey.Convey("SetSlowLogFilter enabled", func() {
exporter := New(
context.Background(),
dsn,
[]Scraper{},
promslog.NewNopLogger(),
SetSlowLogFilter(true),
)
convey.So(exporter.dsn, convey.ShouldEqual, "root@/mysql?log_slow_filter=%27tmp_table_on_disk,filesort_on_disk%27")
})

convey.Convey("EnableLockWaitTimeout enabled and SetLockWaitTimeout", func() {
exporter := New(
context.Background(),
dsn,
[]Scraper{},
promslog.NewNopLogger(),
EnableLockWaitTimeout(true),
SetLockWaitTimeout(30),
)
convey.So(exporter.dsn, convey.ShouldEqual, "root@/mysql?lock_wait_timeout=30")
})

convey.Convey("EnableLockWaitTimeout disabled", func() {
exporter := New(
context.Background(),
dsn,
[]Scraper{},
promslog.NewNopLogger(),
EnableLockWaitTimeout(false),
SetLockWaitTimeout(30),
SetSlowLogFilter(true),
)
convey.So(exporter.dsn, convey.ShouldEqual, "root@/mysql?log_slow_filter=%27tmp_table_on_disk,filesort_on_disk%27")
})

convey.Convey("All options enabled", func() {
exporter := New(
context.Background(),
dsn,
[]Scraper{},
promslog.NewNopLogger(),
EnableLockWaitTimeout(true),
SetLockWaitTimeout(30),
SetSlowLogFilter(true),
)
convey.So(exporter.dsn, convey.ShouldEqual, "root@/mysql?lock_wait_timeout=30&log_slow_filter=%27tmp_table_on_disk,filesort_on_disk%27")
})

convey.Convey("All options with existing query parameter", func() {
dsnWithParams := "root@/mysql?parseTime=true"
exporter := New(
context.Background(),
dsnWithParams,
[]Scraper{},
promslog.NewNopLogger(),
EnableLockWaitTimeout(true),
SetLockWaitTimeout(30),
SetSlowLogFilter(true),
)
convey.So(exporter.dsn, convey.ShouldEqual, "root@/mysql?parseTime=true&lock_wait_timeout=30&log_slow_filter=%27tmp_table_on_disk,filesort_on_disk%27")
})
})
}
18 changes: 17 additions & 1 deletion mysqld_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ var (
"tls.insecure-skip-verify",
"Ignore certificate and server verification when using a tls connection.",
).Bool()
exporterLockTimeout = kingpin.Flag(
"exporter.lock_wait_timeout",
"Set a lock_wait_timeout (in seconds) on the connection to avoid long metadata locking.",
).Default("2").Int()
enableExporterLockTimeout = kingpin.Flag(
"exporter.enable_lock_wait_timeout",
"Enable the lock_wait_timeout MySQL connection parameter.",
).Default("true").Bool()
slowLogFilter = kingpin.Flag(
"exporter.log_slow_filter",
"Add a log_slow_filter to avoid slow query logging of scrapes. NOTE: Not supported by Oracle MySQL.",
).Default("false").Bool()
toolkitFlags = webflag.AddFlags(kingpin.CommandLine, ":9104")
c = config.MySqlConfigHandler{
Config: &config.Config{},
Expand Down Expand Up @@ -200,7 +212,11 @@ func newHandler(scrapers []collector.Scraper, logger *slog.Logger) http.HandlerF

registry := prometheus.NewRegistry()

registry.MustRegister(collector.New(ctx, dsn, filteredScrapers, logger))
registry.MustRegister(collector.New(ctx, dsn, filteredScrapers, logger,
collector.EnableLockWaitTimeout(*enableExporterLockTimeout),
collector.SetLockWaitTimeout(*exporterLockTimeout),
collector.SetSlowLogFilter(*slowLogFilter),
))

gatherers := prometheus.Gatherers{
prometheus.DefaultGatherer,
Expand Down
6 changes: 5 additions & 1 deletion probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ func handleProbe(scrapers []collector.Scraper, logger *slog.Logger) http.Handler
filteredScrapers := filterScrapers(scrapers, collectParams)

registry := prometheus.NewRegistry()
registry.MustRegister(collector.New(ctx, dsn, filteredScrapers, logger))
registry.MustRegister(collector.New(ctx, dsn, filteredScrapers, logger,
collector.EnableLockWaitTimeout(*enableExporterLockTimeout),
collector.SetLockWaitTimeout(*exporterLockTimeout),
collector.SetSlowLogFilter(*slowLogFilter),
))

h := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
h.ServeHTTP(w, r)
Expand Down