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

fixes-for-zk-v3.6 #38

Merged
merged 2 commits into from
May 19, 2021
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
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ services:
# multitarget
exp123:
image: dabealu/zookeeper-exporter
# build:
# context: .
ports:
- 9144:9141
command: --zk-hosts="zoo1:2181,zoo2:2181,zoo3:2181" --timeout=5
Expand Down
81 changes: 54 additions & 27 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,23 @@ import (
"net"
"net/http"
"regexp"
"strconv"
"strings"
"time"
)

const (
// template format: command, host_label
commandNotAllowedTmpl = "warning: %q command isn't allowed at %q, see '4lw.commands.whitelist' ZK config parameter"
instanceNotServingMessage = "This ZooKeeper instance is not currently serving requests"
cmdNotExecutedSffx = "is not executed because it is not in the whitelist."
)

var (
versionRE = regexp.MustCompile(`^([0-9]+\.[0-9]+\.[0-9]+).*$`)
metricNameReplacer = strings.NewReplacer("-", "_", ".", "_")
)

func main() {
location := flag.String("location", "/metrics", "metrics location")
listen := flag.String("listen", "0.0.0.0:9141", "address to listen on")
Expand Down Expand Up @@ -42,17 +55,14 @@ func main() {
}

log.Printf("info: zookeeper hosts: %v", hosts)

options := Options{
log.Printf("info: serving metrics at %s%s", *listen, *location)
serveMetrics(&Options{
Timeout: *timeout,
Hosts: hosts,
Location: *location,
Listen: *listen,
ClientCert: clientCert,
}

log.Printf("info: serving metrics at %s%s", *listen, *location)
serveMetrics(&options)
})
}

type Options struct {
Expand All @@ -63,12 +73,6 @@ type Options struct {
ClientCert *tls.Certificate
}

const cmdNotExecutedSffx = "is not executed because it is not in the whitelist."

var versionRE = regexp.MustCompile(`^([0-9]+\.[0-9]+\.[0-9]+).*$`)

var metricNameReplacer = strings.NewReplacer("-", "_", ".", "_")

func dial(host string, timeout time.Duration, clientCert *tls.Certificate) (net.Conn, error) {
dialer := net.Dialer{Timeout: timeout}
if clientCert == nil {
Expand Down Expand Up @@ -109,7 +113,7 @@ func getMetrics(options *Options) map[string]string {
lines := strings.Split(res, "\n")

// skip instance if it in a leader only state and doesnt serving client requets
if lines[0] == "This ZooKeeper instance is not currently serving requests" {
if lines[0] == instanceNotServingMessage {
metrics[zkUp] = "1"
metrics[fmt.Sprintf("zk_server_leader{%s}", hostLabel)] = "1"
continue
Expand All @@ -118,36 +122,52 @@ func getMetrics(options *Options) map[string]string {
// 'mntr' command isn't allowed in zk config, log as a warning
if strings.Contains(lines[0], cmdNotExecutedSffx) {
metrics[zkUp] = "0"
logNotAllowed("mntr", hostLabel)
log.Printf(commandNotAllowedTmpl, "mntr", hostLabel)
continue
}

// split each line into key-value pair
for _, l := range lines {
l = strings.Replace(l, "\t", " ", -1)
kv := strings.Split(l, " ")
if l == "" {
continue
}

kv := strings.Split(strings.Replace(l, "\t", " ", -1), " ")
key := kv[0]
value := kv[1]

switch kv[0] {
switch key {
case "zk_server_state":
zkLeader := fmt.Sprintf("zk_server_leader{%s}", hostLabel)
if kv[1] == "leader" {
if value == "leader" {
metrics[zkLeader] = "1"
} else {
metrics[zkLeader] = "0"
}

case "zk_version":
version := versionRE.ReplaceAllString(kv[1], "$1")

version := versionRE.ReplaceAllString(value, "$1")
metrics[fmt.Sprintf("zk_version{%s,version=%q}", hostLabel, version)] = "1"

case "zk_peer_state":
metrics[fmt.Sprintf("zk_peer_state{%s,state=%q}", hostLabel, kv[1])] = "1"

case "": // noop on empty string
metrics[fmt.Sprintf("zk_peer_state{%s,state=%q}", hostLabel, value)] = "1"

default:
metrics[fmt.Sprintf("%s{%s}", metricNameReplacer.Replace(kv[0]), hostLabel)] = kv[1]
var k string
if strings.Contains(key, "}") {
k = metricNameReplacer.Replace(key)
k = strings.Replace(k, "}", ",", 1)
k = fmt.Sprintf("%s%s}", k, hostLabel)
} else {
k = fmt.Sprintf("%s{%s}", metricNameReplacer.Replace(key), hostLabel)
}

if !isDigit(value) {
log.Printf("warning: skipping metric %q which holds not-digit value: %q", key, value)
continue
}

metrics[k] = value
}
}

Expand All @@ -158,7 +178,7 @@ func getMetrics(options *Options) map[string]string {
metrics[zkRuok] = "1"
} else {
if strings.Contains(res, cmdNotExecutedSffx) {
logNotAllowed("ruok", hostLabel)
log.Printf(commandNotAllowedTmpl, "ruok", hostLabel)
}
metrics[zkRuok] = "0"
}
Expand All @@ -172,8 +192,15 @@ func getMetrics(options *Options) map[string]string {
return metrics
}

func logNotAllowed(cmd, label string) {
log.Printf("warning: %s command isn't allowed at %s, see '4lw.commands.whitelist' ZK config parameter", cmd, label)
func isDigit(in string) bool {
// check input is an int
if _, err := strconv.Atoi(in); err != nil {
// not int, try float
if _, err := strconv.ParseFloat(in, 64); err != nil {
return false
}
}
return true
}

func sendZookeeperCmd(conn net.Conn, host, cmd string) string {
Expand Down