Skip to content
Snippets Groups Projects
Unverified Commit f202c71d authored by Oliver's avatar Oliver Committed by GitHub
Browse files

Misc refactoring, tests, switch from CircleCI to Drone (#238)

* minor refactoring and a bit more test coverage

* use drone for tests

* remove old glide config file

* remove circleCi config
parent 9a564252
No related branches found
No related tags found
No related merge requests found
# https://circleci.com/docs/2.0/
version: 2
jobs:
tests:
docker:
- image: golang:1.11
- image: redis:5
command: --port 6379
- image: redis:5
command: --port 6380
- image: grokzen/redis-cluster
- image: tile38/tile38:latest
working_directory: /go/src/github.com/oliver006/redis_exporter
steps:
- checkout
- run:
shell: /bin/bash
name: go fmt
command: |
! gofmt -l $(find . -path ./vendor -prune -o -type f -name '*.go' -print) 2>&1 | read
- run: echo " ! gofmt -d main.go 2>&1 | read " | bash
- run: echo " ! gofmt -d exporter/*.go 2>&1 | read " | bash
- run: go get github.com/mattn/goveralls
- run: go get golang.org/x/tools/cmd/cover
- run: go vet ./exporter/...
- run: TEST_REDIS_CLUSTER_MASTER_URI=localhost:7000 TEST_REDIS_CLUSTER_SLAVE_URI=localhost:7005 go test -v -covermode=atomic -cover -race -coverprofile=coverage.txt ./exporter/...
- run: /go/bin/goveralls -coverprofile=coverage.txt -service=circle-ci -repotoken=7rWMRpzVs2gcKnhfdJNbGyE6ycJ6M4fNs
- run: bash <(curl -s https://codecov.io/bash)
build:
docker:
- image: golang:1.11
working_directory: /go/src/github.com/oliver006/redis_exporter
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: true
- run:
name: Install utilities
command: |
apt-get update
apt-get install -y zip
- run:
name: Install Docker client
command: |
set -x
VER="18.03.1-ce"
curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz
tar -xz -C /tmp -f /tmp/docker-$VER.tgz
mv /tmp/docker/* /usr/bin
- run: ./build.sh
workflows:
version: 2
test-build-and-release:
jobs:
- tests:
filters:
tags:
only: /.*/
- build:
requires:
- tests
filters:
tags:
only: /^v.*/
branches:
ignore: /.*/
kind: pipeline
name: default
workspace:
base: /go
path: src/github.com/oliver006/redis_exporter
services:
- name: redis
image: redis:5
ports:
- 6379
- name: moar-redis
image: redis:5
ports:
- 6380
- name: redis-cluster
image: grokzen/redis-cluster
ports:
- 7000
- 7001
- 7002
- 7003
- 7004
- 7005
- name: tile38
image: tile38/tile38:latest
ports:
- 9851
steps:
- name: tests
image: "golang:1.12"
environment:
TEST_TILE38_URI: "tile38:9851"
TEST_SECOND_REDIS_URI: "redis://moar-redis:6380"
TEST_REDIS_CLUSTER_MASTER_URI: "redis-cluster:7000"
TEST_REDIS_CLUSTER_SLAVE_URI: "redis-cluster:7005"
COVERALLS_TOKEN:
from_secret: coveralls-token
commands:
- 'echo " ! gofmt -d main.go 2>&1 | read " | bash'
- 'echo " ! gofmt -d exporter/*.go 2>&1 | read " | bash'
- 'go vet ./exporter/...'
- 'go build'
- "sleep 10"
- 'go test -v -covermode=atomic -cover -race -coverprofile=coverage.txt ./exporter/... --redis.addr=redis'
- 'go get github.com/mattn/goveralls'
- '/go/bin/goveralls -coverprofile=coverage.txt -service=drone.io'
when:
event:
- push
- name: coverage-codecov
image: plugins/codecov
settings:
token:
from_secret: codecov-token
files:
- coverage.txt
when:
event:
- push
- name: release-create-docker-tags
image: alpine
commands:
- 'echo -n "latest,$DRONE_TAG" > .tags'
when:
event:
- tag
- name: release-docker-image-scratch
image: plugins/docker
settings:
dockerfile: ./Dockerfile
repo: oliver006/redis_exporter
target: scratch
build_args:
- 'TAG=${DRONE_TAG}'
- 'SHA1=${DRONE_COMMIT_SHA}'
username:
from_secret: docker_user
password:
from_secret: docker_pass
when:
event:
- tag
- name: release-docker-image-alpine
image: plugins/docker
settings:
dockerfile: ./Dockerfile
repo: oliver006/redis_exporter
target: alpine
build_args:
- 'TAG=${DRONE_TAG}'
- 'SHA1=${DRONE_COMMIT_SHA}'
username:
from_secret: docker_user
password:
from_secret: docker_pass
when:
event:
- tag
- name: release-github-binaries
image: "golang:1.12"
commands:
- './build-github-binaries.sh'
when:
event:
- tag
Loading
Loading
@@ -12,13 +12,12 @@ ARG SHA1
ENV SHA1=$SHA1
ARG TAG
ENV TAG=$TAG
ARG DATE
ENV DATE=$DATE
 
RUN apk --no-cache add ca-certificates
RUN CGO_ENABLED=0 GOOS=linux go build -o /redis_exporter \
-ldflags "-s -w -extldflags \"-static\" -X main.VERSION=$TAG -X main.COMMIT_SHA1=$SHA1 -X main.BUILD_DATE=$DATE" .
RUN BUILD_DATE=$(date +%F-%T) && CGO_ENABLED=0 GOOS=linux go build -o /redis_exporter \
-ldflags "-s -w -extldflags \"-static\" -X main.VERSION=$TAG -X main.COMMIT_SHA1=$SHA1 -X main.BUILD_DATE=$BUILD_DATE" .
 
RUN /redis_exporter -version
 
#
# Alpine release container
Loading
Loading
# Redis Metrics Exporter
 
[![Circle CI](https://circleci.com/gh/oliver006/redis_exporter.svg?style=shield)](https://circleci.com/gh/oliver006/redis_exporter) [![Coverage Status](https://coveralls.io/repos/github/oliver006/redis_exporter/badge.svg?branch=master)](https://coveralls.io/github/oliver006/redis_exporter?branch=master) [![codecov](https://codecov.io/gh/oliver006/redis_exporter/branch/master/graph/badge.svg)](https://codecov.io/gh/oliver006/redis_exporter)
[![Build Status](https://cloud.drone.io/api/badges/oliver006/redis_exporter/status.svg)](https://cloud.drone.io/oliver006/redis_exporter)
[![Coverage Status](https://coveralls.io/repos/github/oliver006/redis_exporter/badge.svg?branch=master)](https://coveralls.io/github/oliver006/redis_exporter?branch=master) [![codecov](https://codecov.io/gh/oliver006/redis_exporter/branch/master/graph/badge.svg)](https://codecov.io/gh/oliver006/redis_exporter)
 
Prometheus exporter for Redis metrics.\
Supports Redis 2.x, 3.x, 4.x, and 5.x
Loading
Loading
@@ -80,7 +82,7 @@ oc process -f https://raw.githubusercontent.com/oliver006/redis_exporter/master/
| oc create -f -
```
 
*NOTE*: Some of the parameters can be ommited if no authentication is used or the default redis config is applied.
*NOTE*: Some of the parameters can be omitted if no authentication is used or the default redis config is applied.
 
```sh
oc process -f https://raw.githubusercontent.com/oliver006/redis_exporter/master/contrib/openshift-template.yaml \
Loading
Loading
#!/usr/bin/env bash
echo "Build Docker images"
docker version
echo "docker login"
docker login -u $DOCKER_USER -p $DOCKER_PASS
docker info
echo "docker login done"
export BUILD_ARGS="--rm=false --build-arg TAG=${CIRCLE_TAG} --build-arg SHA1=${CIRCLE_SHA1} --build-arg DATE=$(date +%F-%T)"
echo "BUILD_ARGS: $BUILD_ARGS"
# Scratch image
docker build --target scratch \
-t "21zoo/redis_exporter:$CIRCLE_TAG" \
-t "21zoo/redis_exporter:latest" \
-t "oliver006/redis_exporter:$CIRCLE_TAG" \
-t "oliver006/redis_exporter:latest" \
$BUILD_ARGS .
docker push "21zoo/redis_exporter:$CIRCLE_TAG"
docker push "21zoo/redis_exporter:latest"
docker push "oliver006/redis_exporter:$CIRCLE_TAG"
docker push "oliver006/redis_exporter:latest"
# Alpine image
docker build --target alpine \
-t "21zoo/redis_exporter:$CIRCLE_TAG-alpine" \
-t "21zoo/redis_exporter:alpine" \
-t "oliver006/redis_exporter:$CIRCLE_TAG-alpine" \
-t "oliver006/redis_exporter:alpine" \
$BUILD_ARGS .
docker push "21zoo/redis_exporter:$CIRCLE_TAG-alpine"
docker push "21zoo/redis_exporter:alpine"
docker push "oliver006/redis_exporter:$CIRCLE_TAG-alpine"
docker push "oliver006/redis_exporter:alpine"
#!/usr/bin/env sh
 
echo "Building binaries for Github"
echo ""
export CGO_ENABLED=0
export GO_LDFLAGS="-s -w -extldflags \"-static\" -X main.VERSION=$CIRCLE_TAG -X main.COMMIT_SHA1=$CIRCLE_SHA1 -X main.BUILD_DATE=$(date +%F-%T)"
export GO_LDFLAGS="-s -w -extldflags \"-static\" -X main.VERSION=$DRONE_TAG -X main.COMMIT_SHA1=$DRONE_COMMIT_SHA -X main.BUILD_DATE=$(date +%F-%T)"
echo "GO_LDFLAGS: $GO_LDFLAGS"
 
go get github.com/mitchellh/gox
go get github.com/tcnksm/ghr
 
gox -rebuild --osarch="darwin/amd64" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && tar -cvzf redis_exporter-$CIRCLE_TAG.darwin-amd64.tar.gz redis_exporter && rm redis_exporter && cd ..
gox -rebuild --osarch="darwin/386" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && tar -cvzf redis_exporter-$CIRCLE_TAG.darwin-386.tar.gz redis_exporter && rm redis_exporter && cd ..
gox -rebuild --osarch="linux/amd64" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && tar -cvzf redis_exporter-$CIRCLE_TAG.linux-amd64.tar.gz redis_exporter && rm redis_exporter && cd ..
gox -rebuild --osarch="linux/386" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && tar -cvzf redis_exporter-$CIRCLE_TAG.linux-386.tar.gz redis_exporter && rm redis_exporter && cd ..
gox -rebuild --osarch="netbsd/amd64" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && tar -cvzf redis_exporter-$CIRCLE_TAG.netbsd-amd64.tar.gz redis_exporter && rm redis_exporter && cd ..
gox -rebuild --osarch="netbsd/386" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && tar -cvzf redis_exporter-$CIRCLE_TAG.netbsd-386.tar.gz redis_exporter && rm redis_exporter && cd ..
gox -rebuild --osarch="windows/amd64" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && zip -9 redis_exporter-$CIRCLE_TAG.windows-amd64.zip redis_exporter.exe && rm redis_exporter.exe && cd ..
gox -rebuild --osarch="windows/386" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && zip -9 redis_exporter-$CIRCLE_TAG.windows-386.zip redis_exporter.exe && rm redis_exporter.exe && cd ..
gox -rebuild --osarch="darwin/amd64" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && tar -cvzf redis_exporter-$DRONE_TAG.darwin-amd64.tar.gz redis_exporter && rm redis_exporter && cd ..
gox -rebuild --osarch="darwin/386" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && tar -cvzf redis_exporter-$DRONE_TAG.darwin-386.tar.gz redis_exporter && rm redis_exporter && cd ..
gox -rebuild --osarch="linux/amd64" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && tar -cvzf redis_exporter-$DRONE_TAG.linux-amd64.tar.gz redis_exporter && rm redis_exporter && cd ..
gox -rebuild --osarch="linux/386" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && tar -cvzf redis_exporter-$DRONE_TAG.linux-386.tar.gz redis_exporter && rm redis_exporter && cd ..
gox -rebuild --osarch="netbsd/amd64" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && tar -cvzf redis_exporter-$DRONE_TAG.netbsd-amd64.tar.gz redis_exporter && rm redis_exporter && cd ..
gox -rebuild --osarch="netbsd/386" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && tar -cvzf redis_exporter-$DRONE_TAG.netbsd-386.tar.gz redis_exporter && rm redis_exporter && cd ..
gox -rebuild --osarch="windows/amd64" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && zip -9 redis_exporter-$DRONE_TAG.windows-amd64.zip redis_exporter.exe && rm redis_exporter.exe && cd ..
gox -rebuild --osarch="windows/386" -ldflags "$GO_LDFLAGS" -output "dist/redis_exporter" && cd dist && zip -9 redis_exporter-$DRONE_TAG.windows-386.zip redis_exporter.exe && rm redis_exporter.exe && cd ..
 
echo "Upload to Github"
ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME --replace $CIRCLE_TAG dist/
ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME --replace $DRONE_TAG dist/
 
echo "Done"
Loading
Loading
@@ -40,13 +40,14 @@ type Exporter struct {
singleKeys []dbKeyPair
keyValues *prometheus.GaugeVec
keySizes *prometheus.GaugeVec
script []byte
scriptValues *prometheus.GaugeVec
duration prometheus.Gauge
scrapeErrors prometheus.Gauge
totalScrapes prometheus.Counter
metrics map[string]*prometheus.GaugeVec
 
LuaScript []byte
metricsMtx sync.RWMutex
sync.RWMutex
}
Loading
Loading
@@ -218,7 +219,7 @@ func (e *Exporter) initGauges() {
e.metrics["latency_spike_last"] = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: e.namespace,
Name: "latency_spike_last",
Help: "When the latency spike last occured",
Help: "When the latency spike last occurred",
}, []string{"addr", "alias", "event_name"})
e.metrics["latency_spike_milliseconds"] = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: e.namespace,
Loading
Loading
@@ -265,12 +266,10 @@ func parseKeyArg(keysArgString string) (keys []dbKeyPair, err error) {
db = strings.Replace(strings.TrimSpace(frags[0]), "db", "", -1)
key, err = url.QueryUnescape(strings.TrimSpace(frags[1]))
default:
err = fmt.Errorf("invalid key list argument: %s", k)
return keys, err
return keys, fmt.Errorf("invalid key list argument: %s", k)
}
if err != nil {
err = fmt.Errorf("couldn't parse db/key string: %s", k)
return keys, err
return keys, fmt.Errorf("couldn't parse db/key string: %s", k)
}
 
keys = append(keys, dbKeyPair{db, key})
Loading
Loading
@@ -320,14 +319,12 @@ func NewRedisExporter(host RedisHost, namespace, checkSingleKeys, checkKeys stri
var err error
 
if e.keys, err = parseKeyArg(checkKeys); err != nil {
log.Fatalf("Couldn't parse check-keys: %#v", err)
return &e, err
return &e, fmt.Errorf("Couldn't parse check-keys: %#v", err)
}
log.Debugf("keys: %#v", e.keys)
 
if e.singleKeys, err = parseKeyArg(checkSingleKeys); err != nil {
log.Fatalf("Couldn't parse check-single-keys: %#v", err)
return &e, err
return &e, fmt.Errorf("Couldn't parse check-single-keys: %#v", err)
}
log.Debugf("singleKeys: %#v", e.singleKeys)
 
Loading
Loading
@@ -349,11 +346,6 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
ch <- e.scrapeErrors.Desc()
}
 
// SetScript sets the Lua Redis script to be used.
func (e *Exporter) SetScript(script []byte) {
e.script = script
}
// Collect fetches new metrics from the RedisHost and updates the appropriate metrics.
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
scrapes := make(chan scrapeResult)
Loading
Loading
@@ -528,13 +520,94 @@ func (e *Exporter) extractTile38Metrics(info []string, addr string, alias string
return nil
}
 
func (e *Exporter) extractInfoMetrics(info, addr string, alias string, scrapes chan<- scrapeResult, dbCount int) error {
var fieldClass string
lines := strings.Split(info, "\r\n")
func (e *Exporter) handleMetricsCommandStats(addr string, alias string, fieldKey string, fieldValue string) {
/*
Format:
cmdstat_get:calls=21,usec=175,usec_per_call=8.33
cmdstat_set:calls=61,usec=3139,usec_per_call=51.46
cmdstat_setex:calls=75,usec=1260,usec_per_call=16.80
*/
splitKey := strings.Split(fieldKey, "_")
if len(splitKey) != 2 {
return
}
splitValue := strings.Split(fieldValue, ",")
if len(splitValue) != 3 {
return
}
var calls float64
var usecTotal float64
var err error
if calls, err = extractVal(splitValue[0]); err != nil {
return
}
if usecTotal, err = extractVal(splitValue[1]); err != nil {
return
}
e.metricsMtx.RLock()
defer e.metricsMtx.RUnlock()
cmd := splitKey[1]
e.metrics["commands_total"].WithLabelValues(addr, alias, cmd).Set(calls)
e.metrics["commands_duration_seconds_total"].WithLabelValues(addr, alias, cmd).Set(usecTotal / 1e6)
}
func (e *Exporter) handleMetricsReplication(addr string, alias string, fieldKey string, fieldValue string) {
e.metricsMtx.RLock()
defer e.metricsMtx.RUnlock()
// only slaves have this field
if fieldKey == "master_link_status" {
if fieldValue == "up" {
e.metrics["master_link_up"].WithLabelValues(addr, alias).Set(1)
} else {
e.metrics["master_link_up"].WithLabelValues(addr, alias).Set(0)
}
return
}
// not a slave, try extracting master metrics
if slaveOffset, slaveIp, slavePort, slaveState, lag, ok := parseConnectedSlaveString(fieldKey, fieldValue); ok {
e.metrics["connected_slave_offset"].WithLabelValues(
addr,
alias,
slaveIp,
slavePort,
slaveState,
).Set(slaveOffset)
if lag > -1 {
e.metrics["connected_slave_lag_seconds"].WithLabelValues(
addr,
alias,
slaveIp,
slavePort,
slaveState,
).Set(lag)
}
}
}
func (e *Exporter) handleMetricsServer(addr string, alias string, fieldKey string, fieldValue string) {
if fieldKey == "uptime_in_seconds" {
if uptime, err := strconv.ParseFloat(fieldValue, 64); err == nil {
e.metricsMtx.RLock()
e.metrics["start_time_seconds"].WithLabelValues(addr, alias).Set(float64(time.Now().Unix()) - uptime)
e.metricsMtx.RUnlock()
}
}
}
 
func (e *Exporter) extractInfoMetrics(info, addr string, alias string, scrapes chan<- scrapeResult, dbCount int) error {
instanceInfo := map[string]string{}
slaveInfo := map[string]string{}
handledDBs := map[string]bool{}
fieldClass := ""
lines := strings.Split(info, "\r\n")
for _, line := range lines {
log.Debugf("info: %s", line)
if len(line) > 0 && line[0] == '#' {
Loading
Loading
@@ -567,85 +640,14 @@ func (e *Exporter) extractInfoMetrics(info, addr string, alias string, scrapes c
switch fieldClass {
 
case "Replication":
// only slave have this field
if fieldKey == "master_link_status" {
e.metricsMtx.RLock()
if fieldValue == "up" {
e.metrics["master_link_up"].WithLabelValues(addr, alias).Set(1)
} else {
e.metrics["master_link_up"].WithLabelValues(addr, alias).Set(0)
}
e.metricsMtx.RUnlock()
continue
}
if slaveOffset, slaveIp, slavePort, slaveState, lag, ok := parseConnectedSlaveString(fieldKey, fieldValue); ok {
e.metricsMtx.RLock()
e.metrics["connected_slave_offset"].WithLabelValues(
addr,
alias,
slaveIp,
slavePort,
slaveState,
).Set(slaveOffset)
if lag > -1 {
e.metrics["connected_slave_lag_seconds"].WithLabelValues(
addr,
alias,
slaveIp,
slavePort,
slaveState,
).Set(lag)
}
e.metricsMtx.RUnlock()
continue
}
e.handleMetricsReplication(addr, alias, fieldKey, fieldValue)
continue
 
case "Server":
if fieldKey == "uptime_in_seconds" {
if uptime, err := strconv.ParseFloat(fieldValue, 64); err == nil {
e.metricsMtx.RLock()
e.metrics["start_time_seconds"].WithLabelValues(addr, alias).Set(float64(time.Now().Unix()) - uptime)
e.metricsMtx.RUnlock()
}
}
e.handleMetricsServer(addr, alias, fieldKey, fieldValue)
 
case "Commandstats":
/*
Format:
cmdstat_get:calls=21,usec=175,usec_per_call=8.33
cmdstat_set:calls=61,usec=3139,usec_per_call=51.46
cmdstat_setex:calls=75,usec=1260,usec_per_call=16.80
*/
frags := strings.Split(fieldKey, "_")
if len(frags) != 2 {
continue
}
cmd := frags[1]
frags = strings.Split(fieldValue, ",")
if len(frags) != 3 {
continue
}
var calls float64
var usecTotal float64
var err error
if calls, err = extractVal(frags[0]); err != nil {
continue
}
if usecTotal, err = extractVal(frags[1]); err != nil {
continue
}
e.metricsMtx.RLock()
e.metrics["commands_total"].WithLabelValues(addr, alias, cmd).Set(calls)
e.metrics["commands_duration_seconds_total"].WithLabelValues(addr, alias, cmd).Set(usecTotal / 1e6)
e.metricsMtx.RUnlock()
e.handleMetricsCommandStats(addr, alias, fieldKey, fieldValue)
continue
 
case "Keyspace":
Loading
Loading
@@ -987,9 +989,9 @@ func (e *Exporter) scrapeRedisHost(scrapes chan<- scrapeResult, addr string, idx
}
}
 
if e.script != nil && len(e.script) > 0 {
if e.LuaScript != nil && len(e.LuaScript) > 0 {
log.Debug("e.script")
kv, err := redis.StringMap(doRedisCmd(c, "EVAL", e.script, 0, 0))
kv, err := redis.StringMap(doRedisCmd(c, "EVAL", e.LuaScript, 0, 0))
if err != nil {
log.Errorf("Collect script error: %v", err)
} else if kv != nil {
Loading
Loading
@@ -1061,7 +1063,7 @@ func (e *Exporter) setMetrics(scrapes <-chan scrapeResult) {
if len(scr.DB) > 0 {
labels["db"] = scr.DB
}
e.metrics[name].With(labels).Set(float64(scr.Value))
e.metrics[name].With(labels).Set(scr.Value)
}
}
 
Loading
Loading
Loading
Loading
@@ -36,7 +36,7 @@ const (
)
 
var (
redisAddr = flag.String("redis.addr", "localhost:6379", "Address of the test instance, without `redis://`")
redisAddr = flag.String("redis.addr", ":6379", "Address of the test instance, without `redis://`")
redisAlias = flag.String("redis.alias", "foo", "Alias of the test instance")
separator = flag.String("separator", ",", "separator used to split redis.addr, redis.password and redis.alias into several elements.")
 
Loading
Loading
@@ -45,7 +45,6 @@ var (
listKeys = []string{}
ts = int32(time.Now().Unix())
defaultRedisHost = RedisHost{}
defaultTileHost = RedisHost{Addrs: []string{":9851"}, Aliases: []string{"tile"}}
 
dbNumStr = "11"
altDBNumStr = "12"
Loading
Loading
@@ -216,7 +215,14 @@ func TestLatencySpike(t *testing.T) {
}
 
func TestTile38(t *testing.T) {
e, _ := NewRedisExporter(defaultTileHost, "test", "", "")
if os.Getenv("TEST_TILE38_URI") == "" {
t.SkipNow()
}
e, _ := NewRedisExporter(
RedisHost{Addrs: []string{os.Getenv("TEST_TILE38_URI")}, Aliases: []string{"tile"}},
"test", "", "",
)
 
chM := make(chan prometheus.Metric)
go func() {
Loading
Loading
@@ -420,7 +426,6 @@ func TestHostVariations(t *testing.T) {
}
 
func TestCountingKeys(t *testing.T) {
e, _ := NewRedisExporter(defaultRedisHost, "test", "", "")
 
scrapes := make(chan scrapeResult, 10000)
Loading
Loading
@@ -510,7 +515,6 @@ func TestExporterMetrics(t *testing.T) {
}
 
func TestExporterValues(t *testing.T) {
e, _ := NewRedisExporter(defaultRedisHost, "test", "", "")
 
setupDBKeys(t, defaultRedisHost.Addrs[0])
Loading
Loading
@@ -584,6 +588,7 @@ func TestParseConnectedSlaveString(t *testing.T) {
{k: "slave1", v: "offset=1,lag=0", offset: 1, ok: true},
{k: "slave1", v: "offset=1", offset: 1, ok: true, lag: -1},
{k: "slave2", v: "ip=1.2.3.4,state=online,offset=123,lag=42", offset: 123, ip: "1.2.3.4", state: "online", ok: true, lag: 42},
{k: "slave", v: "offset=1751844676,lag=0", ok: false},
{k: "slaveA", v: "offset=1751844676,lag=0", ok: false},
{k: "slave0", v: "offset=abc,lag=0", ok: false},
Loading
Loading
@@ -606,7 +611,6 @@ func TestParseConnectedSlaveString(t *testing.T) {
}
 
func TestKeyValuesAndSizes(t *testing.T) {
e, _ := NewRedisExporter(defaultRedisHost, "test", dbNumStrFull+"="+url.QueryEscape(keys[0]), "")
 
setupDBKeys(t, defaultRedisHost.Addrs[0])
Loading
Loading
@@ -653,8 +657,7 @@ func newKeyFixture(command string, key string, args ...interface{}) keyFixture {
func createKeyFixtures(t *testing.T, c redis.Conn, fixtures []keyFixture) {
for _, f := range fixtures {
args := append([]interface{}{f.key}, f.args...)
_, err := c.Do(f.command, args...)
if err != nil {
if _, err := c.Do(f.command, args...); err != nil {
t.Errorf("Error creating fixture: %#v, %#v", f, err)
}
}
Loading
Loading
@@ -662,18 +665,26 @@ func createKeyFixtures(t *testing.T, c redis.Conn, fixtures []keyFixture) {
 
func deleteKeyFixtures(t *testing.T, c redis.Conn, fixtures []keyFixture) {
for _, f := range fixtures {
_, err := c.Do("DEL", f.key)
if err != nil {
if _, err := c.Do("DEL", f.key); err != nil {
t.Errorf("Error deleting fixture: %#v, %#v", f, err)
}
}
}
 
func TestParseKeyArg(t *testing.T) {
parsed, err := parseKeyArg("")
if len(parsed) != 0 || err != nil {
if parsed, err := parseKeyArg(""); len(parsed) != 0 || err != nil {
t.Errorf("Parsing an empty string into a keys arg should yield an empty slice")
return
}
if parsed, err := parseKeyArg("my-key"); err != nil || len(parsed) != 1 || parsed[0].db != "0" || parsed[0].key != "my-key" {
t.Errorf("Expected DB: 0 and key: my-key, got: %#v", parsed[0])
return
}
if _, err := parseKeyArg("wrong=wrong=wrong"); err == nil {
t.Errorf("Expected an error")
return
}
}
 
Loading
Loading
@@ -913,9 +924,8 @@ func TestKeySizeList(t *testing.T) {
}
 
func TestScript(t *testing.T) {
e, _ := NewRedisExporter(defaultRedisHost, "test", "", "")
e.SetScript([]byte(`return {"a", "11", "b", "12", "c", "13"}`))
e.LuaScript = []byte(`return {"a", "11", "b", "12", "c", "13"}`)
nKeys := 3
 
setupDBKeys(t, defaultRedisHost.Addrs[0])
Loading
Loading
@@ -943,7 +953,6 @@ func TestScript(t *testing.T) {
}
 
func TestKeyValueInvalidDB(t *testing.T) {
e, _ := NewRedisExporter(defaultRedisHost, "test", "999="+url.QueryEscape(keys[0]), "")
 
chM := make(chan prometheus.Metric)
Loading
Loading
@@ -975,7 +984,6 @@ func TestKeyValueInvalidDB(t *testing.T) {
}
 
func TestCommandStats(t *testing.T) {
e, _ := NewRedisExporter(defaultRedisHost, "test", dbNumStrFull+"="+url.QueryEscape(keys[0]), "")
 
setupDBKeys(t, defaultRedisHost.Addrs[0])
Loading
Loading
@@ -1096,9 +1104,12 @@ func TestNonExistingHost(t *testing.T) {
func TestMoreThanOneHost(t *testing.T) {
 
firstHost := defaultRedisHost.Addrs[0]
secondHost := "redis://localhost:6380"
secondHostURI := os.Getenv("TEST_SECOND_REDIS_URI")
if secondHostURI == "" {
secondHostURI = "redis://localhost:6380"
}
 
c, err := redis.DialURL(secondHost)
c, err := redis.DialURL(secondHostURI)
if err != nil {
log.Printf("couldn't connect to second redis host, err: %s - skipping test \n", err)
t.SkipNow()
Loading
Loading
@@ -1117,8 +1128,8 @@ func TestMoreThanOneHost(t *testing.T) {
setupDBKeys(t, firstHost)
defer deleteKeysFromDB(t, firstHost)
 
setupDBKeys(t, secondHost)
defer deleteKeysFromDB(t, secondHost)
setupDBKeys(t, secondHostURI)
defer deleteKeysFromDB(t, secondHostURI)
 
_, err = c.Do("SELECT", dbNumStr)
if err != nil {
Loading
Loading
@@ -1135,7 +1146,7 @@ func TestMoreThanOneHost(t *testing.T) {
return
}
 
twoHostCfg := RedisHost{Addrs: []string{firstHost, secondHost}, Aliases: []string{"", ""}}
twoHostCfg := RedisHost{Addrs: []string{firstHost, secondHostURI}, Aliases: []string{"", ""}}
checkKey := dbNumStrFull + "=" + url.QueryEscape(keys[0])
e, _ := NewRedisExporter(twoHostCfg, "test", checkKey, "")
 
Loading
Loading
@@ -1146,8 +1157,8 @@ func TestMoreThanOneHost(t *testing.T) {
}()
 
want := map[string]float64{
firstHost: TestValue,
secondHost: secondHostValue,
firstHost: TestValue,
secondHostURI: secondHostValue,
}
 
for m := range chM {
Loading
Loading
@@ -1383,7 +1394,39 @@ func TestClusterSlave(t *testing.T) {
}
}
 
func TestCheckKeys(t *testing.T) {
ts := httptest.NewServer(promhttp.Handler())
defer ts.Close()
for _, tst := range []struct {
SingleCheckKey string
CheckKeys string
ExpectSuccess bool
}{
{"", "", true},
{"db1=key3", "", true},
{"check-key-01", "", true},
{"", "check-key-02", true},
{"wrong=wrong=1", "", false},
{"", "wrong=wrong=2", false},
} {
_, err := NewRedisExporter(defaultRedisHost, "test", tst.SingleCheckKey, tst.CheckKeys)
if tst.ExpectSuccess && err != nil {
t.Errorf("Expected success for test: %#v, got err: %s", tst, err)
return
}
if !tst.ExpectSuccess && err == nil {
t.Errorf("Expected failure for test: %#v, got no err", tst)
return
}
}
}
func init() {
flag.Parse()
ll := strings.ToLower(os.Getenv("LOG_LEVEL"))
if pl, err := log.ParseLevel(ll); err == nil {
log.Printf("Setting log level to: %s", ll)
Loading
Loading
@@ -1404,7 +1447,6 @@ func init() {
keysExpiring = append(keysExpiring, key)
}
 
flag.Parse()
addrs := strings.Split(*redisAddr, *separator)
if len(addrs) == 0 || len(addrs[0]) == 0 {
log.Fatal("Invalid parameter --redis.addr")
Loading
Loading
package: github.com/oliver006/redis_exporter
import:
- package: github.com/sirupsen/logrus
version: v0.11.0
- package: github.com/gomodule/redigo
version: v2.0.0
subpackages:
- redis
- package: github.com/prometheus/client_golang
version: v0.8.0
subpackages:
- prometheus
- package: github.com/cloudfoundry-community/go-cfenv
version: v1.17.0
- package: github.com/mitchellh/mapstructure
testImport:
- package: github.com/prometheus/client_model
subpackages:
- go
Loading
Loading
@@ -15,24 +15,6 @@ import (
)
 
var (
redisAddr = flag.String("redis.addr", getEnv("REDIS_ADDR", ""), "Address of one or more redis nodes, separated by separator")
redisFile = flag.String("redis.file", getEnv("REDIS_FILE", ""), "Path to file containing one or more redis nodes, separated by newline. NOTE: mutually exclusive with redis.addr")
redisPassword = flag.String("redis.password", getEnv("REDIS_PASSWORD", ""), "Password for one or more redis nodes, separated by separator")
redisPasswordFile = flag.String("redis.password-file", getEnv("REDIS_PASSWORD_FILE", ""), "File containing the password for one or more redis nodes, separated by separator. NOTE: mutually exclusive with redis.password")
redisAlias = flag.String("redis.alias", getEnv("REDIS_ALIAS", ""), "Redis instance alias for one or more redis nodes, separated by separator")
namespace = flag.String("namespace", getEnv("REDIS_EXPORTER_NAMESPACE", "redis"), "Namespace for metrics")
checkKeys = flag.String("check-keys", getEnv("REDIS_EXPORTER_CHECK_KEYS", ""), "Comma separated list of key-patterns to export value and length/size, searched for with SCAN")
checkSingleKeys = flag.String("check-single-keys", getEnv("REDIS_EXPORTER_CHECK_SINGLE_KEYS", ""), "Comma separated list of single keys to export value and length/size")
scriptPath = flag.String("script", getEnv("REDIS_EXPORTER_SCRIPT", ""), "Path to Lua Redis script for collecting extra metrics")
separator = flag.String("separator", getEnv("REDIS_EXPORTER_SEPARATOR", ","), "separator used to split redis.addr, redis.password and redis.alias into several elements.")
listenAddress = flag.String("web.listen-address", getEnv("REDIS_EXPORTER_WEB_LISTEN_ADDRESS", ":9121"), "Address to listen on for web interface and telemetry.")
metricPath = flag.String("web.telemetry-path", getEnv("REDIS_EXPORTER_WEB_TELEMETRY_PATH", "/metrics"), "Path under which to expose metrics.")
isDebug = flag.Bool("debug", getEnvBool("REDIS_EXPORTER_DEBUG"), "Output verbose debug information")
logFormat = flag.String("log-format", getEnv("REDIS_EXPORTER_LOG_FORMAT", "txt"), "Log format, valid options are txt and json")
showVersion = flag.Bool("version", false, "Show version information and exit")
useCfBindings = flag.Bool("use-cf-bindings", getEnvBool("REDIS_EXPORTER_USE-CF-BINDINGS"), "Use Cloud Foundry service bindings")
redisMetricsOnly = flag.Bool("redis-only-metrics", getEnvBool("REDIS_EXPORTER_REDIS_ONLY_METRICS"), "Whether to export go runtime metrics also")
// VERSION, BUILD_DATE, GIT_COMMIT are filled in by the build script
VERSION = "<<< filled in by build >>>"
BUILD_DATE = "<<< filled in by build >>>"
Loading
Loading
@@ -54,6 +36,25 @@ func getEnvBool(key string) (envValBool bool) {
}
 
func main() {
var (
redisAddr = flag.String("redis.addr", getEnv("REDIS_ADDR", ""), "Address of one or more redis nodes, separated by separator")
redisFile = flag.String("redis.file", getEnv("REDIS_FILE", ""), "Path to file containing one or more redis nodes, separated by newline. NOTE: mutually exclusive with redis.addr")
redisPassword = flag.String("redis.password", getEnv("REDIS_PASSWORD", ""), "Password for one or more redis nodes, separated by separator")
redisPasswordFile = flag.String("redis.password-file", getEnv("REDIS_PASSWORD_FILE", ""), "File containing the password for one or more redis nodes, separated by separator. NOTE: mutually exclusive with redis.password")
redisAlias = flag.String("redis.alias", getEnv("REDIS_ALIAS", ""), "Redis instance alias for one or more redis nodes, separated by separator")
namespace = flag.String("namespace", getEnv("REDIS_EXPORTER_NAMESPACE", "redis"), "Namespace for metrics")
checkKeys = flag.String("check-keys", getEnv("REDIS_EXPORTER_CHECK_KEYS", ""), "Comma separated list of key-patterns to export value and length/size, searched for with SCAN")
checkSingleKeys = flag.String("check-single-keys", getEnv("REDIS_EXPORTER_CHECK_SINGLE_KEYS", ""), "Comma separated list of single keys to export value and length/size")
scriptPath = flag.String("script", getEnv("REDIS_EXPORTER_SCRIPT", ""), "Path to Lua Redis script for collecting extra metrics")
separator = flag.String("separator", getEnv("REDIS_EXPORTER_SEPARATOR", ","), "separator used to split redis.addr, redis.password and redis.alias into several elements.")
listenAddress = flag.String("web.listen-address", getEnv("REDIS_EXPORTER_WEB_LISTEN_ADDRESS", ":9121"), "Address to listen on for web interface and telemetry.")
metricPath = flag.String("web.telemetry-path", getEnv("REDIS_EXPORTER_WEB_TELEMETRY_PATH", "/metrics"), "Path under which to expose metrics.")
logFormat = flag.String("log-format", getEnv("REDIS_EXPORTER_LOG_FORMAT", "txt"), "Log format, valid options are txt and json")
isDebug = flag.Bool("debug", getEnvBool("REDIS_EXPORTER_DEBUG"), "Output verbose debug information")
showVersion = flag.Bool("version", false, "Show version information and exit")
useCfBindings = flag.Bool("use-cf-bindings", getEnvBool("REDIS_EXPORTER_USE-CF-BINDINGS"), "Use Cloud Foundry service bindings")
redisMetricsOnly = flag.Bool("redis-only-metrics", getEnvBool("REDIS_EXPORTER_REDIS_ONLY_METRICS"), "Whether to export go runtime metrics also")
)
flag.Parse()
 
switch *logFormat {
Loading
Loading
@@ -101,8 +102,7 @@ func main() {
switch {
case *redisFile != "":
var err error
addrs, passwords, aliases, err = exporter.LoadRedisFile(*redisFile)
if err != nil {
if addrs, passwords, aliases, err = exporter.LoadRedisFile(*redisFile); err != nil {
log.Fatal(err)
}
case *useCfBindings:
Loading
Loading
@@ -122,11 +122,9 @@ func main() {
}
 
if *scriptPath != "" {
script, err := ioutil.ReadFile(*scriptPath)
if err != nil {
log.Fatalf("Error loading script file: %v", err)
if exp.LuaScript, err = ioutil.ReadFile(*scriptPath); err != nil {
log.Fatalf("Error loading script file %s err: %s", *scriptPath, err)
}
exp.SetScript(script)
}
 
buildInfo := prometheus.NewGaugeVec(prometheus.GaugeOpts{
Loading
Loading
@@ -137,8 +135,7 @@ func main() {
 
if *redisMetricsOnly {
registry := prometheus.NewRegistry()
registry.Register(exp)
registry.Register(buildInfo)
registry.MustRegister(exp)
handler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
http.Handle(*metricPath, handler)
} else {
Loading
Loading
@@ -156,7 +153,7 @@ func main() {
<p><a href='` + *metricPath + `'>Metrics</a></p>
</body>
</html>
`))
`))
})
 
log.Printf("Providing metrics at %s%s", *listenAddress, *metricPath)
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment