diff --git a/.promu.yml b/.promu.yml new file mode 100644 index 0000000..be3441b --- /dev/null +++ b/.promu.yml @@ -0,0 +1,22 @@ +repository: + path: github.com/hnlq715/nginx-vts-exporter +build: + 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"}} +tarball: + files: + - LICENSE + +crossbuild: + platforms: + - linux/amd64 + - linux/386 + - darwin/amd64 + - darwin/386 + - windows/amd64 + - windows/386 \ No newline at end of file diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..ceab6e1 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1 \ No newline at end of file diff --git a/nginx_vts_exporter.go b/nginx_vts_exporter.go index 61e6cee..eba4f90 100644 --- a/nginx_vts_exporter.go +++ b/nginx_vts_exporter.go @@ -10,11 +10,11 @@ import ( "net/http" "os" "strconv" - "sync" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/prometheus/common/version" ) type NginxVts struct { @@ -142,42 +142,29 @@ func FloatToString(input_num float64) string { } type Exporter struct { - URI string - mutex sync.RWMutex + URI string - serverMetrics, upstreamMetrics, cacheMetrics map[string]*prometheus.GaugeVec + serverMetrics, upstreamMetrics, cacheMetrics map[string]*prometheus.Desc } -func newServerMetric(metricName string, docString string, labels []string) *prometheus.GaugeVec { - return prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: *metricsNamespace, - Name: "server_" + metricName, - Help: docString, - }, - labels, +func newServerMetric(metricName string, docString string, labels []string) *prometheus.Desc { + return prometheus.NewDesc( + prometheus.BuildFQName(*metricsNamespace, "server", metricName), + docString, labels, nil, ) } -func newUpstreamMetric(metricName string, docString string, labels []string) *prometheus.GaugeVec { - return prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: *metricsNamespace, - Name: "upstream_" + metricName, - Help: docString, - }, - labels, +func newUpstreamMetric(metricName string, docString string, labels []string) *prometheus.Desc { + return prometheus.NewDesc( + prometheus.BuildFQName(*metricsNamespace, "upstream", metricName), + docString, labels, nil, ) } -func newCacheMetric(metricName string, docString string, labels []string) *prometheus.GaugeVec { - return prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: *metricsNamespace, - Name: "cache_" + metricName, - Help: docString, - }, - labels, +func newCacheMetric(metricName string, docString string, labels []string) *prometheus.Desc { + return prometheus.NewDesc( + prometheus.BuildFQName(*metricsNamespace, "cache", metricName), + docString, labels, nil, ) } @@ -185,18 +172,18 @@ func NewExporter(uri string) *Exporter { return &Exporter{ URI: uri, - serverMetrics: map[string]*prometheus.GaugeVec{ + serverMetrics: map[string]*prometheus.Desc{ "connections": newServerMetric("connections", "nginx connections", []string{"status"}), "requests": newServerMetric("requests", "requests counter", []string{"host", "code"}), "bytes": newServerMetric("bytes", "request/response bytes", []string{"host", "direction"}), "cache": newServerMetric("cache", "cache counter", []string{"host", "status"}), }, - upstreamMetrics: map[string]*prometheus.GaugeVec{ + upstreamMetrics: map[string]*prometheus.Desc{ "requests": newUpstreamMetric("requests", "requests counter", []string{"upstream", "code"}), "bytes": newUpstreamMetric("bytes", "request/response bytes", []string{"upstream", "direction"}), "response": newUpstreamMetric("response", "request response time", []string{"upstream", "backend"}), }, - cacheMetrics: map[string]*prometheus.GaugeVec{ + cacheMetrics: map[string]*prometheus.Desc{ "requests": newCacheMetric("requests", "cache requests counter", []string{"zone", "status"}), "bytes": newCacheMetric("bytes", "cache request/response bytes", []string{"zone", "direction"}), }, @@ -205,51 +192,18 @@ func NewExporter(uri string) *Exporter { func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { for _, m := range e.serverMetrics { - m.Describe(ch) + ch <- m } for _, m := range e.upstreamMetrics { - m.Describe(ch) + ch <- m } for _, m := range e.cacheMetrics { - m.Describe(ch) + ch <- m } } func (e *Exporter) Collect(ch chan<- prometheus.Metric) { - e.mutex.Lock() - defer e.mutex.Unlock() - - e.resetMetrics() - e.scrape() - e.collectMetrics(ch) -} - -func (e *Exporter) resetMetrics() { - for _, m := range e.serverMetrics { - m.Reset() - } - for _, m := range e.upstreamMetrics { - m.Reset() - } - for _, m := range e.cacheMetrics { - m.Reset() - } -} - -func (e *Exporter) collectMetrics(metrics chan<- prometheus.Metric) { - for _, m := range e.serverMetrics { - m.Collect(metrics) - } - for _, m := range e.upstreamMetrics { - m.Collect(metrics) - } - for _, m := range e.cacheMetrics { - m.Collect(metrics) - } -} - -func (e *Exporter) scrape() { body, err := fetchHTTP(e.URI, 2*time.Second)() if err != nil { log.Println("fetchHTTP failed", err) @@ -270,63 +224,78 @@ func (e *Exporter) scrape() { return } - e.serverMetrics["connections"].WithLabelValues("active").Set(float64(nginxVtx.Connections.Active)) - e.serverMetrics["connections"].WithLabelValues("reading").Set(float64(nginxVtx.Connections.Reading)) - e.serverMetrics["connections"].WithLabelValues("waiting").Set(float64(nginxVtx.Connections.Waiting)) - e.serverMetrics["connections"].WithLabelValues("writing").Set(float64(nginxVtx.Connections.Writing)) - e.serverMetrics["connections"].WithLabelValues("accepted").Set(float64(nginxVtx.Connections.Accepted)) - e.serverMetrics["connections"].WithLabelValues("handled").Set(float64(nginxVtx.Connections.Handled)) - e.serverMetrics["connections"].WithLabelValues("requests").Set(float64(nginxVtx.Connections.Requests)) + // connections + ch <- prometheus.MustNewConstMetric(e.serverMetrics["connections"], prometheus.GaugeValue, float64(nginxVtx.Connections.Active), "active") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["connections"], prometheus.GaugeValue, float64(nginxVtx.Connections.Reading), "reading") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["connections"], prometheus.GaugeValue, float64(nginxVtx.Connections.Waiting), "waiting") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["connections"], prometheus.GaugeValue, float64(nginxVtx.Connections.Writing), "writing") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["connections"], prometheus.GaugeValue, float64(nginxVtx.Connections.Accepted), "accepted") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["connections"], prometheus.GaugeValue, float64(nginxVtx.Connections.Handled), "handled") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["connections"], prometheus.GaugeValue, float64(nginxVtx.Connections.Requests), "requests") + // ServerZones for host, s := range nginxVtx.ServerZones { - e.serverMetrics["requests"].WithLabelValues(host, "total").Set(float64(s.RequestCounter)) - e.serverMetrics["requests"].WithLabelValues(host, "1xx").Set(float64(s.Responses.OneXx)) - e.serverMetrics["requests"].WithLabelValues(host, "2xx").Set(float64(s.Responses.TwoXx)) - e.serverMetrics["requests"].WithLabelValues(host, "3xx").Set(float64(s.Responses.ThreeXx)) - e.serverMetrics["requests"].WithLabelValues(host, "4xx").Set(float64(s.Responses.FourXx)) - e.serverMetrics["requests"].WithLabelValues(host, "5xx").Set(float64(s.Responses.FiveXx)) - - e.serverMetrics["cache"].WithLabelValues(host, "bypass").Set(float64(s.Responses.Bypass)) - e.serverMetrics["cache"].WithLabelValues(host, "expired").Set(float64(s.Responses.Expired)) - e.serverMetrics["cache"].WithLabelValues(host, "hit").Set(float64(s.Responses.Hit)) - e.serverMetrics["cache"].WithLabelValues(host, "miss").Set(float64(s.Responses.Miss)) - e.serverMetrics["cache"].WithLabelValues(host, "revalidated").Set(float64(s.Responses.Revalidated)) - e.serverMetrics["cache"].WithLabelValues(host, "scarce").Set(float64(s.Responses.Scarce)) - e.serverMetrics["cache"].WithLabelValues(host, "stale").Set(float64(s.Responses.Stale)) - e.serverMetrics["cache"].WithLabelValues(host, "updating").Set(float64(s.Responses.Updating)) - - e.serverMetrics["bytes"].WithLabelValues(host, "in").Set(float64(s.InBytes)) - e.serverMetrics["bytes"].WithLabelValues(host, "out").Set(float64(s.OutBytes)) + ch <- prometheus.MustNewConstMetric(e.serverMetrics["requests"], prometheus.CounterValue, float64(s.RequestCounter), host, "total") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["requests"], prometheus.CounterValue, float64(s.Responses.OneXx), host, "1xx") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["requests"], prometheus.CounterValue, float64(s.Responses.TwoXx), host, "2xx") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["requests"], prometheus.CounterValue, float64(s.Responses.ThreeXx), host, "3xx") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["requests"], prometheus.CounterValue, float64(s.Responses.FourXx), host, "4xx") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["requests"], prometheus.CounterValue, float64(s.Responses.FiveXx), host, "5xx") + + ch <- prometheus.MustNewConstMetric(e.serverMetrics["cache"], prometheus.CounterValue, float64(s.Responses.Bypass), host, "bypass") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["cache"], prometheus.CounterValue, float64(s.Responses.Expired), host, "expired") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["cache"], prometheus.CounterValue, float64(s.Responses.Hit), host, "hit") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["cache"], prometheus.CounterValue, float64(s.Responses.Miss), host, "miss") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["cache"], prometheus.CounterValue, float64(s.Responses.Revalidated), host, "revalidated") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["cache"], prometheus.CounterValue, float64(s.Responses.Scarce), host, "scarce") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["cache"], prometheus.CounterValue, float64(s.Responses.Stale), host, "stale") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["cache"], prometheus.CounterValue, float64(s.Responses.Updating), host, "updating") + + ch <- prometheus.MustNewConstMetric(e.serverMetrics["bytes"], prometheus.CounterValue, float64(s.InBytes), host, "in") + ch <- prometheus.MustNewConstMetric(e.serverMetrics["bytes"], prometheus.CounterValue, float64(s.OutBytes), host, "out") } + // UpstreamZones for name, upstreamList := range nginxVtx.UpstreamZones { + var total, one, two, three, four, five, inbytes, outbytes float64 for _, s := range upstreamList { - e.upstreamMetrics["requests"].WithLabelValues(name, "total").Add(float64(s.RequestCounter)) - e.upstreamMetrics["requests"].WithLabelValues(name, "1xx").Add(float64(s.Responses.OneXx)) - e.upstreamMetrics["requests"].WithLabelValues(name, "2xx").Add(float64(s.Responses.TwoXx)) - e.upstreamMetrics["requests"].WithLabelValues(name, "3xx").Add(float64(s.Responses.ThreeXx)) - e.upstreamMetrics["requests"].WithLabelValues(name, "4xx").Add(float64(s.Responses.FourXx)) - e.upstreamMetrics["requests"].WithLabelValues(name, "5xx").Add(float64(s.Responses.FiveXx)) + total += float64(s.RequestCounter) + one += float64(s.Responses.OneXx) + two += float64(s.Responses.TwoXx) + three += float64(s.Responses.ThreeXx) + four += float64(s.Responses.FourXx) + five += float64(s.Responses.FiveXx) - e.upstreamMetrics["bytes"].WithLabelValues(name, "in").Add(float64(s.InBytes)) - e.upstreamMetrics["bytes"].WithLabelValues(name, "out").Add(float64(s.OutBytes)) + inbytes += float64(s.InBytes) + outbytes += float64(s.OutBytes) - e.upstreamMetrics["response"].WithLabelValues(name, s.Server).Add(float64(s.ResponseMsec)) + ch <- prometheus.MustNewConstMetric(e.upstreamMetrics["response"], prometheus.GaugeValue, float64(s.ResponseMsec), name, s.Server) } + + ch <- prometheus.MustNewConstMetric(e.upstreamMetrics["requests"], prometheus.CounterValue, total, name, "total") + ch <- prometheus.MustNewConstMetric(e.upstreamMetrics["requests"], prometheus.CounterValue, one, name, "1xx") + ch <- prometheus.MustNewConstMetric(e.upstreamMetrics["requests"], prometheus.CounterValue, two, name, "2xx") + ch <- prometheus.MustNewConstMetric(e.upstreamMetrics["requests"], prometheus.CounterValue, three, name, "3xx") + ch <- prometheus.MustNewConstMetric(e.upstreamMetrics["requests"], prometheus.CounterValue, four, name, "4xx") + ch <- prometheus.MustNewConstMetric(e.upstreamMetrics["requests"], prometheus.CounterValue, five, name, "5xx") + + ch <- prometheus.MustNewConstMetric(e.upstreamMetrics["bytes"], prometheus.CounterValue, inbytes, name, "in") + ch <- prometheus.MustNewConstMetric(e.upstreamMetrics["bytes"], prometheus.CounterValue, outbytes, name, "out") } + // CacheZones for zone, s := range nginxVtx.CacheZones { - e.cacheMetrics["requests"].WithLabelValues(zone, "bypass").Set(float64(s.Responses.Bypass)) - e.cacheMetrics["requests"].WithLabelValues(zone, "expired").Set(float64(s.Responses.Expired)) - e.cacheMetrics["requests"].WithLabelValues(zone, "hit").Set(float64(s.Responses.Hit)) - e.cacheMetrics["requests"].WithLabelValues(zone, "miss").Set(float64(s.Responses.Miss)) - e.cacheMetrics["requests"].WithLabelValues(zone, "revalidated").Set(float64(s.Responses.Revalidated)) - e.cacheMetrics["requests"].WithLabelValues(zone, "scarce").Set(float64(s.Responses.Scarce)) - e.cacheMetrics["requests"].WithLabelValues(zone, "stale").Set(float64(s.Responses.Stale)) - e.cacheMetrics["requests"].WithLabelValues(zone, "updating").Set(float64(s.Responses.Updating)) - - e.cacheMetrics["bytes"].WithLabelValues(zone, "in").Set(float64(s.InBytes)) - e.cacheMetrics["bytes"].WithLabelValues(zone, "out").Set(float64(s.OutBytes)) + ch <- prometheus.MustNewConstMetric(e.cacheMetrics["requests"], prometheus.CounterValue, float64(s.Responses.Bypass), zone, "bypass") + ch <- prometheus.MustNewConstMetric(e.cacheMetrics["requests"], prometheus.CounterValue, float64(s.Responses.Expired), zone, "expired") + ch <- prometheus.MustNewConstMetric(e.cacheMetrics["requests"], prometheus.CounterValue, float64(s.Responses.Hit), zone, "hit") + ch <- prometheus.MustNewConstMetric(e.cacheMetrics["requests"], prometheus.CounterValue, float64(s.Responses.Miss), zone, "miss") + ch <- prometheus.MustNewConstMetric(e.cacheMetrics["requests"], prometheus.CounterValue, float64(s.Responses.Revalidated), zone, "revalidated") + ch <- prometheus.MustNewConstMetric(e.cacheMetrics["requests"], prometheus.CounterValue, float64(s.Responses.Scarce), zone, "scarce") + ch <- prometheus.MustNewConstMetric(e.cacheMetrics["requests"], prometheus.CounterValue, float64(s.Responses.Stale), zone, "stale") + ch <- prometheus.MustNewConstMetric(e.cacheMetrics["requests"], prometheus.CounterValue, float64(s.Responses.Updating), zone, "updating") + + ch <- prometheus.MustNewConstMetric(e.cacheMetrics["bytes"], prometheus.CounterValue, float64(s.InBytes), zone, "in") + ch <- prometheus.MustNewConstMetric(e.cacheMetrics["bytes"], prometheus.CounterValue, float64(s.OutBytes), zone, "out") } } @@ -347,6 +316,7 @@ func fetchHTTP(uri string, timeout time.Duration) func() (io.ReadCloser, error) } var ( + showVersion = flag.Bool("version", false, "Print version information.") listenAddress = flag.String("telemetry.address", ":9913", "Address on which to expose metrics.") metricsEndpoint = flag.String("telemetry.endpoint", "/metrics", "Path under which to expose metrics.") metricsNamespace = flag.String("metrics.namespace", "nginx", "Prometheus metrics namespace.") @@ -354,9 +324,21 @@ var ( insecure = flag.Bool("insecure", true, "Ignore server certificate if using https") ) +func init() { + prometheus.MustRegister(version.NewCollector("nginx_vts_exporter")) +} + func main() { flag.Parse() + if *showVersion { + fmt.Fprintln(os.Stdout, version.Print("nginx_vts_exporter")) + os.Exit(0) + } + + log.Printf("Starting nginx_vts_exporter %s", version.Info()) + log.Printf("Build context %s", version.BuildContext()) + exporter := NewExporter(*nginxScrapeURI) prometheus.MustRegister(exporter) prometheus.Unregister(prometheus.NewProcessCollector(os.Getpid(), "")) diff --git a/vendor/github.com/prometheus/common/version/info.go b/vendor/github.com/prometheus/common/version/info.go new file mode 100644 index 0000000..84489a5 --- /dev/null +++ b/vendor/github.com/prometheus/common/version/info.go @@ -0,0 +1,89 @@ +// Copyright 2016 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. + +package version + +import ( + "bytes" + "fmt" + "runtime" + "strings" + "text/template" + + "github.com/prometheus/client_golang/prometheus" +) + +// Build information. Populated at build-time. +var ( + Version string + Revision string + Branch string + BuildUser string + BuildDate string + GoVersion = runtime.Version() +) + +// NewCollector returns a collector which exports metrics about current version information. +func NewCollector(program string) *prometheus.GaugeVec { + buildInfo := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: program, + Name: "build_info", + Help: fmt.Sprintf( + "A metric with a constant '1' value labeled by version, revision, branch, and goversion from which %s was built.", + program, + ), + }, + []string{"version", "revision", "branch", "goversion"}, + ) + buildInfo.WithLabelValues(Version, Revision, Branch, GoVersion).Set(1) + return buildInfo +} + +// versionInfoTmpl contains the template used by Info. +var versionInfoTmpl = ` +{{.program}}, version {{.version}} (branch: {{.branch}}, revision: {{.revision}}) + build user: {{.buildUser}} + build date: {{.buildDate}} + go version: {{.goVersion}} +` + +// Print returns version information. +func Print(program string) string { + m := map[string]string{ + "program": program, + "version": Version, + "revision": Revision, + "branch": Branch, + "buildUser": BuildUser, + "buildDate": BuildDate, + "goVersion": GoVersion, + } + t := template.Must(template.New("version").Parse(versionInfoTmpl)) + + var buf bytes.Buffer + if err := t.ExecuteTemplate(&buf, "version", m); err != nil { + panic(err) + } + return strings.TrimSpace(buf.String()) +} + +// Info returns version, branch and revision information. +func Info() string { + return fmt.Sprintf("(version=%s, branch=%s, revision=%s)", Version, Branch, Revision) +} + +// BuildContext returns goVersion, buildUser and buildDate information. +func BuildContext() string { + return fmt.Sprintf("(go=%s, user=%s, date=%s)", GoVersion, BuildUser, BuildDate) +} diff --git a/vendor/vendor.json b/vendor/vendor.json index c48b67d..c75d03b 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -56,6 +56,12 @@ "revision": "0d5de9d6d8629cb8bee6d4674da4127cd8b615a3", "revisionTime": "2016-11-14T13:47:43Z" }, + { + "checksumSHA1": "8P2UBs8q/HZQlGxeXL15hxlfTIA=", + "path": "github.com/prometheus/common/version", + "revision": "dd2f054febf4a6c00f2343686efb775948a8bff4", + "revisionTime": "2017-01-08T23:12:12Z" + }, { "checksumSHA1": "6gusrsr0iRPTOBie4dn2MG2v9OI=", "path": "github.com/prometheus/procfs",