diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d258e7e --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +# 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. + +VERSION := 0.1.0 +TARGET := nsq_exporter +GOFLAGS := -ldflags "-X main.Version=$(VERSION)" + +include Makefile.COMMON diff --git a/Makefile.COMMON b/Makefile.COMMON new file mode 100644 index 0000000..ac286fd --- /dev/null +++ b/Makefile.COMMON @@ -0,0 +1,131 @@ +# 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. + +# THE AUTHORITATIVE VERSION OF THIS MAKEFILE LIVES IN: +# +# https://github.com/prometheus/utils +# +# PLEASE MAKE ANY CHANGES THERE AND PROPAGATE THEM TO ALL PROMETHEUS +# REPOSITORIES THAT ARE USING THIS MAKEFILE. +# +# 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))) + +GO_VERSION ?= 1.5.3 +GOPATH ?= $(CURDIR)/.build/gopath +ROOTPKG ?= github.com/prometheus/$(TARGET) +SELFLINK ?= $(GOPATH)/src/$(ROOTPKG) + +# Check for the correct version of go in the path. If we find it, use it. +# Otherwise, prepare to build go locally. +ifeq ($(shell command -v "go" >/dev/null && go version | sed -e 's/^[^0-9.]*\([0-9.]*\).*/\1/'), $(GO_VERSION)) + GOCC ?= $(shell command -v "go") + GOFMT ?= $(shell command -v "gofmt") + GO ?= GOPATH=$(GOPATH) $(GOCC) +else + GOURL ?= https://golang.org/dl + GOPKG ?= go$(GO_VERSION).$(GOOS)-$(GOARCH).tar.gz + GOROOT ?= $(CURDIR)/.build/go$(GO_VERSION) + GOCC ?= $(GOROOT)/bin/go + GOFMT ?= $(GOROOT)/bin/gofmt + GO ?= GOPATH=$(GOPATH) GOROOT=$(GOROOT) $(GOCC) +endif + +# Use vendored dependencies if available. Otherwise try to download them. +ifneq (,$(wildcard vendor)) + DEPENDENCIES := $(shell find vendor/ -type f -iname '*.go') + GO := GO15VENDOREXPERIMENT=1 $(GO) +else + DEPENDENCIES := dependencies-stamp +endif + +# Never honor GOBIN, should it be set at all. +unexport GOBIN + +SUFFIX ?= $(GOOS)-$(GOARCH) +BINARY ?= $(TARGET) +ARCHIVE ?= $(TARGET)-$(VERSION).$(SUFFIX).tar.gz + +default: $(BINARY) + +$(BINARY): $(GOCC) $(SRC) $(DEPENDENCIES) Makefile Makefile.COMMON | $(SELFLINK) + cd $(SELFLINK) && $(GO) build $(GOFLAGS) -o $@ + +.PHONY: archive +archive: $(ARCHIVE) + +$(ARCHIVE): $(BINARY) + tar -czf $@ $< + +.PHONY: tag +tag: + git tag $(VERSION) + git push --tags + +.PHONY: test +test: $(GOCC) $(DEPENDENCIES) | $(SELFLINK) + cd $(SELFLINK) && $(GO) test $$($(GO) list ./... | grep -v /vendor/) + +.PHONY: format +format: $(GOCC) + find . -iname '*.go' | egrep -v "^\./\.build|./generated|\./vendor|\.(l|y)\.go" | xargs -n1 $(GOFMT) -w -s=true + +.PHONY: clean +clean: + rm -rf $(BINARY) $(ARCHIVE) .build *-stamp + + + +$(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 .build + # The archive contains a single directory called 'go/'. + curl -L $(GOURL)/$(GOPKG) | tar -C .build -xzf - + rm -rf $(GOROOT) + mv .build/go $(GOROOT) + +$(SELFLINK): + mkdir -p $(dir $@) + ln -s $(CURDIR) $@ + +# Download dependencies if project doesn't vendor them. +dependencies-stamp: $(GOCC) $(SRC) | $(SELFLINK) + $(GO) get -d + touch $@ diff --git a/README.md b/README.md index 3286fcf..0658e6d 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ NSQ exporter for prometheus.io, written in go. ## Building + make + + OR + go get -u github.com/lovoo/nsq_exporter go install github.com/lovoo/nsq_exporter diff --git a/collector/stats.go b/collector/stats.go index 575f32e..ce37235 100644 --- a/collector/stats.go +++ b/collector/stats.go @@ -25,20 +25,27 @@ type topic struct { Depth int64 `json:"depth"` BackendDepth int64 `json:"backend_depth"` MessageCount uint64 `json:"message_count"` + E2eLatency e2elatency `json:"e2e_processing_latency"` Channels []*channel `json:"channels"` } type channel struct { - Name string `json:"channel_name"` - Paused bool `json:"paused"` - Depth int64 `json:"depth"` - BackendDepth int64 `json:"backend_depth"` - MessageCount uint64 `json:"message_count"` - InFlightCount int `json:"in_flight_count"` - DeferredCount int `json:"deferred_count"` - RequeueCount uint64 `json:"requeue_count"` - TimeoutCount uint64 `json:"timeout_count"` - Clients []*client `json:"clients"` + Name string `json:"channel_name"` + Paused bool `json:"paused"` + Depth int64 `json:"depth"` + BackendDepth int64 `json:"backend_depth"` + MessageCount uint64 `json:"message_count"` + InFlightCount int `json:"in_flight_count"` + DeferredCount int `json:"deferred_count"` + RequeueCount uint64 `json:"requeue_count"` + TimeoutCount uint64 `json:"timeout_count"` + E2eLatency e2elatency `json:"e2e_processing_latency"` + Clients []*client `json:"clients"` +} + +type e2elatency struct { + Count int `json:"count"` + Percentiles []map[string]float64 `json:"percentiles"` } type client struct { @@ -59,7 +66,19 @@ type client struct { TLS bool `json:"tls"` } +func getPercentile(t *topic, percentile int) float64 { + if len(t.E2eLatency.Percentiles) > 0 { + if percentile == 99 { + return t.E2eLatency.Percentiles[0]["value"] + } else if percentile == 95 { + return t.E2eLatency.Percentiles[1]["value"] + } + } + return 0 +} + func getNsqdStats(nsqdURL string) (*stats, error) { + resp, err := http.Get(nsqdURL) if err != nil { return nil, err diff --git a/collector/stats_channel.go b/collector/stats_channel.go index 56cc8ff..77efb0b 100644 --- a/collector/stats_channel.go +++ b/collector/stats_channel.go @@ -58,6 +58,22 @@ func ChannelStats(namespace string) StatsCollector { Help: "In flight count", }, labels), }, + { + val: func(c *channel) float64 { return c.E2eLatency.Percentiles[0]["value"] }, + vec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "e2e_latency_99p", + Help: "e2e latency 99th percentile", + }, labels), + }, + { + val: func(c *channel) float64 { return c.E2eLatency.Percentiles[1]["value"] }, + vec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "e2e_latency_95p", + Help: "e2e latency 95th percentile", + }, labels), + }, { val: func(c *channel) float64 { return float64(c.DeferredCount) }, vec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ diff --git a/collector/stats_topic.go b/collector/stats_topic.go index faa5659..96280d8 100644 --- a/collector/stats_topic.go +++ b/collector/stats_topic.go @@ -41,6 +41,22 @@ func TopicStats(namespace string) StatsCollector { Help: "Queue backend depth", }, labels), }, + { + val: func(t *topic) float64 { return getPercentile(t, 99) }, + vec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "e2e_latency_99_percentile", + Help: "Queue e2e latency 99th percentile", + }, labels), + }, + { + val: func(t *topic) float64 { return getPercentile(t, 95) }, + vec: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "e2e_latency_95_percentile", + Help: "Queue e2e latency 95th percentile", + }, labels), + }, { val: func(t *topic) float64 { return float64(t.MessageCount) }, vec: prometheus.NewGaugeVec(prometheus.GaugeOpts{