Commit 60e8f761 authored by Eugene Chertikhin's avatar Eugene Chertikhin

initial

parent 7a1bef1f
.build
mysqld_exporter
*.tar.gz
*.test
*-stamp
.idea
*.iml
This diff is collapsed.
# Copyright 2013 The Prometheus Authors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
VERSION := 0.1.0
TARGET := mysqld_exporter
include Makefile.COMMON
# Copyright 2015 The Prometheus Authors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file provides common Makefile infrastructure for several Prometheus
# components. This includes make tasks for downloading Go, setting up a
# self-contained build environment, fetching Go dependencies, building
# binaries, running tests, and doing release management. This file is intended
# to be included from a project's Makefile, which needs to define the following
# variables, at a minimum:
#
# * VERSION - The current version of the project in question.
# * TARGET - The desired name of the built binary.
#
# Many of the variables defined below are defined conditionally (using '?'),
# which allows the project's main Makefile to override any of these settings, if
# needed. See also:
#
# https://www.gnu.org/software/make/manual/html_node/Flavors.html#Flavors.
#
# The including Makefile may define any number of extra targets that are
# specific to that project.
VERSION ?= $(error VERSION not set in including Makefile)
TARGET ?= $(error TARGET not set in including Makefile)
SRC ?= $(shell find . -type f -name "*.go" ! -path "./.build/*")
GOOS := $(shell uname | tr A-Z a-z)
GOARCH := $(subst x86_64,amd64,$(patsubst i%86,386,$(shell uname -m)))
ifeq ($(GOOS),darwin)
RELEASE_SUFFIX ?= -osx$(shell sw_vers -productVersion)
endif
GO_VERSION ?= 1.4.1
GOURL ?= https://golang.org/dl
GOPKG ?= go$(GO_VERSION).$(GOOS)-$(GOARCH)$(RELEASE_SUFFIX).tar.gz
GOPATH := $(CURDIR)/.build/gopath
GOCC ?= $(GOROOT)/bin/go
GO ?= GOROOT=$(GOROOT) GOPATH=$(GOPATH) $(GOCC)
GOFMT ?= $(GOROOT)/bin/gofmt
ifeq ($(shell type go >/dev/null && go version | sed 's/.*go\([0-9.]*\).*/\1/'), $(GO_VERSION))
GOROOT := $(shell go env GOROOT)
else
GOROOT := $(CURDIR)/.build/go$(GO_VERSION)
endif
# Never honor GOBIN, should it be set at all.
unexport GOBIN
SUFFIX ?= $(GOOS)-$(GOARCH)
BINARY ?= $(TARGET)
ARCHIVE ?= $(TARGET)-$(VERSION).$(SUFFIX).tar.gz
ROOTPKG ?= github.com/prometheus/$(TARGET)
SELFLINK ?= $(GOPATH)/src/$(ROOTPKG)
default: $(BINARY)
$(GOCC):
@echo Go version $(GO_VERSION) required but not found in PATH.
@echo About to download and install go$(GO_VERSION) to $(GOROOT)
@echo Abort now if you want to manually install it system-wide instead.
@echo
@sleep 5
mkdir -p $(GOROOT)
curl -L $(GOURL)/$(GOPKG) | tar -C $(GOROOT) --strip 1 -xz
$(SELFLINK):
mkdir -p $(dir $@)
ln -s $(CURDIR) $@
dependencies-stamp: $(SRC) | $(SELFLINK)
$(GO) get -d
touch $@
$(BINARY): $(GOCC) $(SRC) dependencies-stamp Makefile Makefile.COMMON
$(GO) build $(GOFLAGS) -o $@
$(ARCHIVE): $(BINARY)
tar -czf $@ $<
.PHONY: tag
tag:
git tag $(VERSION)
git push --tags
.PHONY: test
test: $(GOCC) dependencies-stamp
$(GO) test ./...
.PHONY: format
format:
find . -iname '*.go' | egrep -v "^\./\.build|./generated|\./Godeps|\.(l|y)\.go" | xargs -n1 $(GOFMT) -w -s=true
.PHONY: clean
clean:
rm -rf $(BINARY) $(ARCHIVE) .build *-stamp
Exporter for mysql daemon
by Eugene Chertikhin <e.chertikhin@crestwavetech.ru>
This product includes software developed at
SoundCloud Ltd. (http://soundcloud.com/).
# mysqld_exporter (UNDER CONSTRUCTION)
# mysqld_exporter
Exporter for MySQL server metrics http://prometheus.io/
## Building and running
make
./mysqld_exporter <flags>
## Configuration
The configuration is in JSON. An example with all possible options:
```
{
"config" : {
"mysql_connection" : "login:password@host/dbname"
}
}
```
Name | Description
---------|------------
login | Login name for connect with server
password | Password for connect with server
host | Server hostname or ip. May be omitted
dbname | Name of database
{
"config" : {
"mysql_connection" : "root:123@/mysql"
}
}
package main
import (
"encoding/json"
"flag"
"io/ioutil"
"log"
"net/http"
"strconv"
"sync"
"database/sql"
_ "github.com/go-sql-driver/mysql"
"github.com/prometheus/client_golang/prometheus"
)
const (
namespace = "mysql"
)
var (
listenAddress = flag.String("web.listen-address", ":9091", "Address to listen on for web interface and telemetry.")
metricPath = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
configFile = flag.String("config.file", "mysqld_exporter.conf", "Path to config file.")
)
type Config struct {
Config map[string]string `json:"config"`
}
type Exporter struct {
config Config
mutex sync.RWMutex
up prometheus.Gauge
totalScrapes, errorScrapes prometheus.Counter
metrics map[string]prometheus.Gauge
}
func getConfig(file string) (*Config, error) {
config := &Config{}
bytes, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
return config, json.Unmarshal(bytes, &config)
}
// return new empty exporter
func NewMySQLExporter(config *Config) *Exporter {
return &Exporter{
config: *config,
up: prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "up",
Help: "Was the last scrape of mysqld successful.",
}),
totalScrapes: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: namespace,
Name: "exporter_total_scrapes",
Help: "Current total mysqld scrapes.",
}),
errorScrapes: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: namespace,
Name: "exporter_error_scrapes",
Help: "Error mysqld scrapes.",
}),
metrics: map[string]prometheus.Gauge{},
}
}
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
for _, m := range e.metrics {
m.Describe(ch)
}
ch <- e.up.Desc()
ch <- e.totalScrapes.Desc()
ch <- e.errorScrapes.Desc()
}
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
scrapes := make(chan []string)
go e.scrape(scrapes)
e.mutex.Lock()
defer e.mutex.Unlock()
e.setMetrics(scrapes)
ch <- e.up
ch <- e.totalScrapes
ch <- e.errorScrapes
e.collectMetrics(ch)
}
func (e *Exporter) scrape(scrapes chan<- []string) {
defer close(scrapes)
e.totalScrapes.Inc()
e.up.Set(0)
db, err := sql.Open("mysql", e.config.Config["mysql_connection"])
if err != nil {
log.Printf("error open connection to db using %s", e.config.Config["mysql_connection"])
return
}
defer db.Close()
rows, err := db.Query("SHOW STATUS")
if err != nil {
log.Printf("error running query on db %s", e.config.Config["mysql_connection"])
return
}
defer rows.Close()
e.up.Set(1) // from this point db is ok
var key, val []byte
for rows.Next() {
// get RawBytes from data
err = rows.Scan(&key, &val)
if err != nil {
log.Printf("error getting result set ")
return
}
var res []string = make([]string, 2)
res[0] = string(key)
res[1] = string(val)
scrapes <- res
}
return
}
func (e *Exporter) setMetrics(scrapes <-chan []string) {
for row := range scrapes {
name := row[0]
value, err := strconv.ParseInt(row[1], 10, 64)
if err != nil {
// not really important
// log.Printf("Error while parsing field value %s (%s): %v", row[0], row[1], err)
e.errorScrapes.Inc()
continue
}
if _, ok := e.metrics[name]; !ok {
e.metrics[name] = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: name,
})
}
e.metrics[name].Set(float64(value))
}
}
func (e *Exporter) collectMetrics(metrics chan<- prometheus.Metric) {
for _, m := range e.metrics {
m.Collect(metrics)
}
}
func main() {
flag.Parse()
config, err := getConfig(*configFile)
if err != nil {
log.Fatal("couldn't read config file ", *configFile, " : ", err)
}
exporter := NewMySQLExporter(config)
prometheus.MustRegister(exporter)
http.Handle(*metricPath, prometheus.Handler())
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`<html>
<head><title>MySQLd exporter</title></head>
<body>
<h1>MySQLd exporter</h1>
<p><a href='` + *metricPath + `'>Metrics</a></p>
</body>
</html>
`))
})
log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment