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

Adjust exporter to work with maxscale 2.5 #13

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
10 changes: 5 additions & 5 deletions .promu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ build:
- name: maxscale_exporter
flags: -a -tags netgo
ldflags: |
-X {{repoPath}}/vendor/github.com/prometheus/common/version.Version={{.Version}}
-X {{repoPath}}/vendor/github.com/prometheus/common/version.Revision={{.Revision}}
-X {{repoPath}}/vendor/github.com/prometheus/common/version.Branch={{.Branch}}
-X {{repoPath}}/vendor/github.com/prometheus/common/version.BuildUser={{user}}@{{host}}
-X {{repoPath}}/vendor/github.com/prometheus/common/version.BuildDate={{date "20060102-15:04:05"}}
-X {{repoPath}}/version.Version={{.Version}}
-X {{repoPath}}/version.Revision={{.Revision}}
-X {{repoPath}}/version.Branch={{.Branch}}
-X {{repoPath}}/version.BuildUser={{user}}@{{host}}
-X {{repoPath}}/version.BuildDate={{date "20060102-15:04:05"}}
tarball:
files:
- LICENSE
Expand Down
6 changes: 2 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
FROM golang:1.8 AS build
FROM golang:1.15 AS build

WORKDIR /go/src/app
COPY . .
RUN go get -d -v ./...
RUN go install -v ./...
RUN go get github.com/VoIPGRID/maxscale_exporter
RUN make build

FROM alpine:3.10
FROM alpine

COPY --from=build /go/src/app/maxscale_exporter /bin/maxscale_exporter
USER nobody
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.8
FROM golang:1.15
WORKDIR /go/src/app
COPY . .
RUN go get -d -v ./...
Expand Down
63 changes: 0 additions & 63 deletions Gopkg.lock

This file was deleted.

7 changes: 0 additions & 7 deletions Gopkg.toml

This file was deleted.

1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.

GO ?= GO15VENDOREXPERIMENT=1 go
GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH)))
GOARCH := $(shell $(GO) env GOARCH)
GOHOSTARCH := $(shell $(GO) env GOHOSTARCH)
Expand Down
204 changes: 204 additions & 0 deletions exporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package main

import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"strings"

"github.com/prometheus/client_golang/prometheus"
)

const (
namespace = "maxscale"
)

var (
descServerUp = newDesc("server", "up", "Is the server up", serverLabelNames, prometheus.GaugeValue)
descServerMaster = newDesc("server", "master", "Is the server master", serverLabelNames, prometheus.GaugeValue)
descServerConnections = newDesc("server", "connections", "Current number of connections to the server", serverLabelNames, prometheus.GaugeValue)
descServerTotalConnections = newDesc("server", "total_connections", "Total connections", serverLabelNames, prometheus.CounterValue)
descServerReusedConnections = newDesc("server", "reused_connections", "Reused connections", serverLabelNames, prometheus.CounterValue)
descServerActiveOperations = newDesc("server", "active_operations", "Curren number of active operations", serverLabelNames, prometheus.GaugeValue)
descServiceCurrentSessions = newDesc("service", "current_sessions", "Amount of sessions currently active", serviceLabelNames, prometheus.GaugeValue)
descServiceSessionsTotal = newDesc("service", "total_sessions", "Total amount of sessions", serviceLabelNames, prometheus.CounterValue)
descQueryStatisticsRead = newDesc("query_statistics", "read", "Total reads", queryStatisticsLabelNames, prometheus.CounterValue)
descQueryStatisticsWrite = newDesc("query_statistics", "write", "Total writes", queryStatisticsLabelNames, prometheus.CounterValue)
)

type Exporter struct {
Address string // address of the maxscale instance
ctx context.Context
up prometheus.Gauge
}

var (
serverLabelNames = []string{"server"}
serviceLabelNames = []string{"service"}
queryStatisticsLabelNames = []string{"service", "server"}
)

func NewExporter(ctx context.Context, address string) *Exporter {
return &Exporter{
Address: address,
ctx: ctx,
up: prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "up",
Help: "Was the last scrape of MaxScale successful?",
}),
}
}

// Describe describes all the metrics ever exported by the MaxScale exporter. It
// implements prometheus.Collector.
func (m *Exporter) Describe(ch chan<- *prometheus.Desc) {
ch <- descServerUp.Desc
ch <- descServerMaster.Desc
ch <- descServerConnections.Desc
ch <- descServerTotalConnections.Desc
ch <- descServerReusedConnections.Desc
ch <- descServerActiveOperations.Desc
ch <- descServiceCurrentSessions.Desc
ch <- descServiceSessionsTotal.Desc
ch <- descQueryStatisticsRead.Desc
ch <- descQueryStatisticsWrite.Desc

ch <- m.up.Desc()
}

// Collect fetches the stats from configured MaxScale location and delivers them
// as Prometheus metrics. It implements prometheus.Collector.
func (m *Exporter) Collect(ch chan<- prometheus.Metric) {
parseErrors := false

if err := m.parseServers(ch); err != nil {
parseErrors = true
log.Print(err)
}

if err := m.parseServices(ch); err != nil {
parseErrors = true
log.Print(err)
}

m.up.Set(boolToFloat(!parseErrors))
ch <- m.up
}

const contentTypeJSON = "application/json"

func (m *Exporter) fetchJSON(path string, v interface{}) error {
url := "http://" + m.Address + "/v1" + path
// build the request
req, err := http.NewRequestWithContext(m.ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
req.Header.Set("Accept", contentTypeJSON)

// execute the request
resp, err := http.DefaultClient.Do(req)
if err != nil {
return fmt.Errorf("error while getting %v: %w", url, err)
}
defer resp.Body.Close()

// check status code
if status := resp.StatusCode; status != http.StatusOK {
return fmt.Errorf("unexpected status code %v for url %v", status, url)
}

// check content type
if contentType := resp.Header.Get("Content-Type"); contentType != contentTypeJSON {
return fmt.Errorf("unexpected content type %v", contentType)
}

// decode JSON body
respObj := Response{}
err = json.NewDecoder(resp.Body).Decode(&respObj)
if err != nil {
return err
}

return json.Unmarshal(respObj.Data, v)
}

func boolToFloat(value bool) float64 {
if value {
return 1
}

return 0
}

func (m *Exporter) parseServers(ch chan<- prometheus.Metric) error {
var servers []ServerData
err := m.fetchJSON("/servers", &servers)
if err != nil {
return err
}

for _, server := range servers {
ch <- descServerConnections.new(
float64(server.Attributes.Statistics.Connections),
server.Attributes.Name,
)

ch <- descServerReusedConnections.new(
float64(server.Attributes.Statistics.ReusedConnections),
server.Attributes.Name,
)
ch <- descServerTotalConnections.new(
float64(server.Attributes.Statistics.TotalConnections),
server.Attributes.Name,
)
ch <- descServerActiveOperations.new(
float64(server.Attributes.Statistics.ActiveOperations),
server.Attributes.Name,
)

ch <- descServerMaster.new(
boolToFloat(strings.HasPrefix(server.Attributes.State, "Master,")),
server.Attributes.Name,
)
ch <- descServerUp.new(
boolToFloat(strings.HasSuffix(server.Attributes.State, ", Running")),
server.Attributes.Name,
)
}

return nil
}

func (m *Exporter) parseServices(ch chan<- prometheus.Metric) error {
var services []ServiceData
err := m.fetchJSON("/services", &services)
if err != nil {
return err
}

for _, service := range services {
ch <- descServiceCurrentSessions.new(
float64(service.Attributes.Statistics.Connections),
service.ID,
)

for _, statistics := range service.Attributes.RouterDiagnostics.ServerQueryStatistics {
labelValues := []string{service.ID, statistics.ID}

ch <- descQueryStatisticsRead.new(
float64(statistics.Read),
labelValues...,
)
ch <- descQueryStatisticsWrite.new(
float64(statistics.Write),
labelValues...,
)
}
}

return nil
}
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module github.com/VoIPGRID/maxscale_exporter

go 1.15

require (
github.com/prometheus/client_golang v1.7.1
github.com/prometheus/common v0.15.0
github.com/prometheus/procfs v0.2.0 // indirect
)
Loading