Skip to content

Commit

Permalink
PMM-1920: Standard Metrics (#15)
Browse files Browse the repository at this point in the history
* PMM-1920: Standard Metrics
* try to fix port allocation
* not sure why tests sometimes fail, let's try increase timeouts
* fix path
* forgot about ssl:/
* fix passing port
* PMM-1920 Expose standard metrics as HR only.
  • Loading branch information
arvenil authored and AlekSi committed Jan 17, 2018
1 parent d051e40 commit 592653d
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 43 deletions.
36 changes: 18 additions & 18 deletions mysqld_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -793,25 +793,25 @@ func main() {
// https
mux := http.NewServeMux()

reg := prometheus.NewRegistry()
reg.MustRegister(NewExporter(dsn))
handler := promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
registryHr := prometheus.NewRegistry()
registryHr.MustRegister(NewExporter(dsn))
handler := promhttp.HandlerFor(prometheus.Gatherers{prometheus.DefaultGatherer, registryHr}, promhttp.HandlerOpts{})
if cfg.User != "" && cfg.Password != "" {
handler = &basicAuthHandler{handler: handler.ServeHTTP, user: cfg.User, password: cfg.Password}
}
mux.Handle(*metricPath+"-hr", handler)

reg = prometheus.NewRegistry()
reg.MustRegister(NewExporterMr(dsn))
handler = promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
registryMr := prometheus.NewRegistry()
registryMr.MustRegister(NewExporterMr(dsn))
handler = promhttp.HandlerFor(registryMr, promhttp.HandlerOpts{})
if cfg.User != "" && cfg.Password != "" {
handler = &basicAuthHandler{handler: handler.ServeHTTP, user: cfg.User, password: cfg.Password}
}
mux.Handle(*metricPath+"-mr", handler)

reg = prometheus.NewRegistry()
reg.MustRegister(NewExporterLr(dsn))
handler = promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
registryLr := prometheus.NewRegistry()
registryLr.MustRegister(NewExporterLr(dsn))
handler = promhttp.HandlerFor(registryLr, promhttp.HandlerOpts{})
if cfg.User != "" && cfg.Password != "" {
handler = &basicAuthHandler{handler: handler.ServeHTTP, user: cfg.User, password: cfg.Password}
}
Expand Down Expand Up @@ -841,25 +841,25 @@ func main() {
log.Fatal(srv.ListenAndServeTLS(*sslCertFile, *sslKeyFile))
} else {
// http
reg := prometheus.NewRegistry()
reg.MustRegister(NewExporter(dsn))
handler := promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
registryHr := prometheus.NewRegistry()
registryHr.MustRegister(NewExporter(dsn))
handler := promhttp.HandlerFor(prometheus.Gatherers{prometheus.DefaultGatherer, registryHr}, promhttp.HandlerOpts{})
if cfg.User != "" && cfg.Password != "" {
handler = &basicAuthHandler{handler: handler.ServeHTTP, user: cfg.User, password: cfg.Password}
}
http.Handle(*metricPath+"-hr", handler)

reg = prometheus.NewRegistry()
reg.MustRegister(NewExporterMr(dsn))
handler = promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
registryMr := prometheus.NewRegistry()
registryMr.MustRegister(NewExporterMr(dsn))
handler = promhttp.HandlerFor(registryMr, promhttp.HandlerOpts{})
if cfg.User != "" && cfg.Password != "" {
handler = &basicAuthHandler{handler: handler.ServeHTTP, user: cfg.User, password: cfg.Password}
}
http.Handle(*metricPath+"-mr", handler)

reg = prometheus.NewRegistry()
reg.MustRegister(NewExporterLr(dsn))
handler = promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
registryLr := prometheus.NewRegistry()
registryLr.MustRegister(NewExporterLr(dsn))
handler = promhttp.HandlerFor(registryLr, promhttp.HandlerOpts{})
if cfg.User != "" && cfg.Password != "" {
handler = &basicAuthHandler{handler: handler.ServeHTTP, user: cfg.User, password: cfg.Password}
}
Expand Down
116 changes: 91 additions & 25 deletions mysqld_exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ func TestGetMySQLVersion(t *testing.T) {
}

type binData struct {
bin string
bin string
port int
}

func TestBin(t *testing.T) {
Expand Down Expand Up @@ -189,17 +190,22 @@ func TestBin(t *testing.T) {
t.Fatalf("Failed to build: %s", err)
}

data := binData{
bin: bin,
}
tests := []func(*testing.T, binData){
testLandingPage,
testVersion,
testDefaultGatherer,
}

portStart := 56000
t.Run(binName, func(t *testing.T) {
for _, f := range tests {
f := f // capture range variable
fName := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
portStart++
data := binData{
bin: bin,
port: portStart,
}
t.Run(fName, func(t *testing.T) {
t.Parallel()
f(t, data)
Expand All @@ -216,6 +222,7 @@ func testVersion(t *testing.T, data binData) {
ctx,
data.bin,
"--version",
"--web.listen-address", fmt.Sprintf(":%d", data.port),
)

b := &bytes.Buffer{}
Expand Down Expand Up @@ -271,6 +278,7 @@ func testLandingPage(t *testing.T, data binData) {
cmd := exec.CommandContext(
ctx,
data.bin,
"--web.listen-address", fmt.Sprintf(":%d", data.port),
)
cmd.Env = append(os.Environ(), "DATA_SOURCE_NAME=127.0.0.1:3306")

Expand All @@ -281,13 +289,75 @@ func testLandingPage(t *testing.T, data binData) {
defer cmd.Process.Kill()

// Get the main page, but we need to wait a bit for http server
var resp *http.Response
var err error
for i := 0; i <= 10; i++ {
body, err := get(fmt.Sprintf("http://127.0.0.1:%d", data.port))
if err != nil {
t.Fatal(err)
}
got := string(body)

expected := `<html>
<head><title>MySQLd 3-in-1 exporter</title></head>
<body>
<h1>MySQL 3-in-1 exporter</h1>
<li><a href="/metrics-hr">high-res metrics</a></li>
<li><a href="/metrics-mr">medium-res metrics</a></li>
<li><a href="/metrics-lr">low-res metrics</a></li>
</body>
</html>
`
if got != expected {
t.Fatalf("got '%s' but expected '%s'", got, expected)
}
}

func testDefaultGatherer(t *testing.T, data binData) {
metricPath := "/metrics"
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()

cmd := exec.CommandContext(
ctx,
data.bin,
"--web.telemetry-path", metricPath,
"--web.listen-address", fmt.Sprintf(":%d", data.port),
)
cmd.Env = append(os.Environ(), "DATA_SOURCE_NAME=127.0.0.1:3306")

if err := cmd.Start(); err != nil {
t.Fatal(err)
}
defer cmd.Wait()
defer cmd.Process.Kill()

const resolution = "hr"
body, err := get(fmt.Sprintf("http://127.0.0.1:%d%s-%s", data.port, metricPath, resolution))
if err != nil {
t.Fatalf("unable to get metrics for '%s' resolution: %s", resolution, err)
}
got := string(body)

metricsPrefixes := []string{
"go_gc_duration_seconds",
"go_goroutines",
"go_memstats",
}

for _, prefix := range metricsPrefixes {
if !strings.Contains(got, prefix) {
t.Fatalf("no metric starting with %s in resolution %s", prefix, resolution)
}
}
}

func get(urlToGet string) (body []byte, err error) {
tries := 60

// Get data, but we need to wait a bit for http server
for i := 0; i <= tries; i++ {
// Try to get main page
resp, err = http.Get("http://127.0.0.1:9104")
body, err = getBody(urlToGet)
if err == nil {
break
return body, err
}

// If there is a syscall.ECONNREFUSED error (web server not available) then retry
Expand All @@ -302,27 +372,23 @@ func testLandingPage(t *testing.T, data binData) {
}
}

t.Fatalf("%#v", err)
return nil, err
}

return nil, fmt.Errorf("failed to GET %s: %s", urlToGet, err)
}

func getBody(urlToGet string) ([]byte, error) {
resp, err := http.Get(urlToGet)
if err != nil {
return nil, err
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
return nil, err
}
got := string(body)

expected := `<html>
<head><title>MySQLd 3-in-1 exporter</title></head>
<body>
<h1>MySQL 3-in-1 exporter</h1>
<li><a href="/metrics-hr">high-res metrics</a></li>
<li><a href="/metrics-mr">medium-res metrics</a></li>
<li><a href="/metrics-lr">low-res metrics</a></li>
</body>
</html>
`
if got != expected {
t.Fatalf("got '%s' but expected '%s'", got, expected)
}
return body, nil
}

0 comments on commit 592653d

Please sign in to comment.