pax_global_header 0000666 0000000 0000000 00000000064 13226661727 0014526 g ustar 00root root 0000000 0000000 52 comment=0fa675070710f98376ae681624cb2a4e2b321269 prometheus-apache-exporter-0.5.0+ds/ 0000775 0000000 0000000 00000000000 13226661727 0017412 5 ustar 00root root 0000000 0000000 prometheus-apache-exporter-0.5.0+ds/.promu.yml 0000664 0000000 0000000 00000001150 13226661727 0021352 0 ustar 00root root 0000000 0000000 repository: path: github.com/Lusitaniae/apache_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 prometheus-apache-exporter-0.5.0+ds/Dockerfile 0000664 0000000 0000000 00000000207 13226661727 0021403 0 ustar 00root root 0000000 0000000 FROM quay.io/prometheus/busybox:latest COPY apache_exporter /bin/apache_exporter ENTRYPOINT ["/bin/apache_exporter"] EXPOSE 9117 prometheus-apache-exporter-0.5.0+ds/LICENSE 0000664 0000000 0000000 00000002063 13226661727 0020420 0 ustar 00root root 0000000 0000000 The MIT License (MIT) Copyright (c) 2015 neezgee Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. prometheus-apache-exporter-0.5.0+ds/Makefile 0000664 0000000 0000000 00000003307 13226661727 0021055 0 ustar 00root root 0000000 0000000 # 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. GO := GO15VENDOREXPERIMENT=1 go PROMU := $(GOPATH)/bin/promu pkgs = $(shell $(GO) list ./... | grep -v /vendor/) PREFIX ?= $(shell pwd) BIN_DIR ?= $(shell pwd) DOCKER_IMAGE_NAME ?= apache-exporter DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD)) all: format build test test: @echo ">> running tests" @$(GO) test -short $(pkgs) style: @echo ">> checking code style" @! gofmt -d $(shell find . -path ./vendor -prune -o -name '*.go' -print) | grep '^' format: @echo ">> formatting code" @$(GO) fmt $(pkgs) vet: @echo ">> vetting code" @$(GO) vet $(pkgs) build: promu @echo ">> building binaries" @$(PROMU) build --prefix $(PREFIX) tarball: promu @echo ">> building release tarball" @$(PROMU) tarball --prefix $(PREFIX) $(BIN_DIR) docker: @echo ">> building docker image" @docker build -t "$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" . promu: @GOOS=$(shell uname -s | tr A-Z a-z) \ GOARCH=$(subst x86_64,amd64,$(patsubst i%86,386,$(shell uname -m))) \ $(GO) get -u github.com/prometheus/promu .PHONY: all style format build test vet tarball docker promu prometheus-apache-exporter-0.5.0+ds/README.md 0000664 0000000 0000000 00000007426 13226661727 0020702 0 ustar 00root root 0000000 0000000 # Apache Exporter for Prometheus [![Build Status][buildstatus]][circleci] [][quay] [][hub] Exports apache mod_status statistics via HTTP for Prometheus consumption. With working golang environment it can be built with `go get`. There is a [good article](https://machineperson.github.io/monitoring/2016/01/04/exporting-apache-metrics-to-prometheus.html) with build HOWTO and usage example. Help on flags: ``` -insecure Ignore server certificate if using https. (default false) -log.level value Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal, panic]. (default info) -scrape_uri string URI to apache stub status page. (default "http://localhost/server-status/?auto") -telemetry.address string Address on which to expose metrics. (default ":9117") -telemetry.endpoint string Path under which to expose metrics. (default "/metrics") -version Version of the Apache exporter. ``` Tested on Apache 2.2 and Apache 2.4. If your server-status page is secured by http auth, add the credentials to the scrape URL following this example: ``` http://user:password@localhost/server-status?auto ``` # Using Docker ## Build Run the following commands from the project root directory. ``` docker build -t apache_exporter . ``` ## Run ``` docker run -d -p 9117:9117 apache_exporter \ -scrape_uri "https://your.server.com/server-status/?auto" ``` ## Collectors Apache metrics: ``` # HELP apache_accesses_total Current total apache accesses (*) # TYPE apache_accesses_total counter # HELP apache_scoreboard Apache scoreboard statuses # TYPE apache_scoreboard gauge # HELP apache_sent_kilobytes_total Current total kbytes sent (*) # TYPE apache_sent_kilobytes_total counter # HELP apache_cpu_load CPU Load (*) # TYPE apache_cpu_load gauge # HELP apache_up Could the apache server be reached # TYPE apache_up gauge # HELP apache_uptime_seconds_total Current uptime in seconds (*) # TYPE apache_uptime_seconds_total counter # HELP apache_workers Apache worker statuses # TYPE apache_workers gauge ``` Exporter process metrics: ``` # HELP http_request_duration_microseconds The HTTP request latencies in microseconds. # TYPE http_request_duration_microseconds summary # HELP http_request_size_bytes The HTTP request sizes in bytes. # TYPE http_request_size_bytes summary # HELP http_response_size_bytes The HTTP response sizes in bytes. # TYPE http_response_size_bytes summary # HELP process_cpu_seconds_total Total user and system CPU time spent in seconds. # TYPE process_cpu_seconds_total counter # HELP process_max_fds Maximum number of open file descriptors. # TYPE process_max_fds gauge # HELP process_open_fds Number of open file descriptors. # TYPE process_open_fds gauge # HELP process_resident_memory_bytes Resident memory size in bytes. # TYPE process_resident_memory_bytes gauge # HELP process_start_time_seconds Start time of the process since unix epoch in seconds. # TYPE process_start_time_seconds gauge # HELP process_virtual_memory_bytes Virtual memory size in bytes. # TYPE process_virtual_memory_bytes gauge ``` Metrics marked '(*)' are only available if ExtendedStatus is On in apache webserver configuration. In version 2.3.6, loading mod_status will toggle ExtendedStatus On by default. ## Author The exporter was originally created by [neezgee](https://github.com/neezgee). [buildstatus]: https://circleci.com/gh/Lusitaniae/apache_exporter/tree/master.svg?style=shield [quay]: https://quay.io/repository/Lusitaniae/apache-exporter [circleci]: https://circleci.com/gh/Lusitaniae/apache_exporter [hub]: https://hub.docker.com/r/lusotycoon/apache-exporter/ prometheus-apache-exporter-0.5.0+ds/VERSION 0000664 0000000 0000000 00000000006 13226661727 0020456 0 ustar 00root root 0000000 0000000 0.5.0 prometheus-apache-exporter-0.5.0+ds/apache_exporter.go 0000664 0000000 0000000 00000017337 13226661727 0023125 0 ustar 00root root 0000000 0000000 package main import ( "crypto/tls" "flag" "fmt" "io/ioutil" "net/http" "os" "strconv" "strings" "sync" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/prometheus/common/version" ) const ( namespace = "apache" // For Prometheus metrics. ) var ( listeningAddress = flag.String("telemetry.address", ":9117", "Address on which to expose metrics.") metricsEndpoint = flag.String("telemetry.endpoint", "/metrics", "Path under which to expose metrics.") scrapeURI = flag.String("scrape_uri", "http://localhost/server-status/?auto", "URI to apache stub status page.") insecure = flag.Bool("insecure", false, "Ignore server certificate if using https.") showVersion = flag.Bool("version", false, "Print version information.") ) type Exporter struct { URI string mutex sync.Mutex client *http.Client up *prometheus.Desc scrapeFailures prometheus.Counter accessesTotal *prometheus.Desc kBytesTotal *prometheus.Desc cpuload prometheus.Gauge uptime *prometheus.Desc workers *prometheus.GaugeVec scoreboard *prometheus.GaugeVec connections *prometheus.GaugeVec } func NewExporter(uri string) *Exporter { return &Exporter{ URI: uri, up: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "up"), "Could the apache server be reached", nil, nil), scrapeFailures: prometheus.NewCounter(prometheus.CounterOpts{ Namespace: namespace, Name: "exporter_scrape_failures_total", Help: "Number of errors while scraping apache.", }), accessesTotal: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "accesses_total"), "Current total apache accesses (*)", nil, nil), kBytesTotal: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "sent_kilobytes_total"), "Current total kbytes sent (*)", nil, nil), cpuload: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, Name: "cpuload", Help: "The current percentage CPU used by each worker and in total by all workers combined (*)", }), uptime: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "uptime_seconds_total"), "Current uptime in seconds (*)", nil, nil), workers: prometheus.NewGaugeVec(prometheus.GaugeOpts{ Namespace: namespace, Name: "workers", Help: "Apache worker statuses", }, []string{"state"}, ), scoreboard: prometheus.NewGaugeVec(prometheus.GaugeOpts{ Namespace: namespace, Name: "scoreboard", Help: "Apache scoreboard statuses", }, []string{"state"}, ), connections: prometheus.NewGaugeVec(prometheus.GaugeOpts{ Namespace: namespace, Name: "connections", Help: "Apache connection statuses", }, []string{"state"}, ), client: &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: *insecure}, }, }, } } func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { ch <- e.up ch <- e.accessesTotal ch <- e.kBytesTotal ch <- e.uptime e.cpuload.Describe(ch) e.scrapeFailures.Describe(ch) e.workers.Describe(ch) e.scoreboard.Describe(ch) e.connections.Describe(ch) } // Split colon separated string into two fields func splitkv(s string) (string, string) { if len(s) == 0 { return s, s } slice := strings.SplitN(s, ":", 2) if len(slice) == 1 { return slice[0], "" } return strings.TrimSpace(slice[0]), strings.TrimSpace(slice[1]) } var scoreboardLabelMap = map[string]string{ "_": "idle", "S": "startup", "R": "read", "W": "reply", "K": "keepalive", "D": "dns", "C": "closing", "L": "logging", "G": "graceful_stop", "I": "idle_cleanup", ".": "open_slot", } func (e *Exporter) updateScoreboard(scoreboard string) { e.scoreboard.Reset() for _, v := range scoreboardLabelMap { e.scoreboard.WithLabelValues(v) } for _, worker_status := range scoreboard { s := string(worker_status) label, ok := scoreboardLabelMap[s] if !ok { label = s } e.scoreboard.WithLabelValues(label).Inc() } } func (e *Exporter) collect(ch chan<- prometheus.Metric) error { resp, err := e.client.Get(e.URI) if err != nil { ch <- prometheus.MustNewConstMetric(e.up, prometheus.GaugeValue, 0) return fmt.Errorf("Error scraping apache: %v", err) } ch <- prometheus.MustNewConstMetric(e.up, prometheus.GaugeValue, 1) data, err := ioutil.ReadAll(resp.Body) resp.Body.Close() if resp.StatusCode != 200 { if err != nil { data = []byte(err.Error()) } return fmt.Errorf("Status %s (%d): %s", resp.Status, resp.StatusCode, data) } lines := strings.Split(string(data), "\n") connectionInfo := false for _, l := range lines { key, v := splitkv(l) if err != nil { continue } switch { case key == "Total Accesses": val, err := strconv.ParseFloat(v, 64) if err != nil { return err } ch <- prometheus.MustNewConstMetric(e.accessesTotal, prometheus.CounterValue, val) case key == "Total kBytes": val, err := strconv.ParseFloat(v, 64) if err != nil { return err } ch <- prometheus.MustNewConstMetric(e.kBytesTotal, prometheus.CounterValue, val) case key == "CPULoad": val, err := strconv.ParseFloat(v, 64) if err != nil { return err } e.cpuload.Set(val) case key == "Uptime": val, err := strconv.ParseFloat(v, 64) if err != nil { return err } ch <- prometheus.MustNewConstMetric(e.uptime, prometheus.CounterValue, val) case key == "BusyWorkers": val, err := strconv.ParseFloat(v, 64) if err != nil { return err } e.workers.WithLabelValues("busy").Set(val) case key == "IdleWorkers": val, err := strconv.ParseFloat(v, 64) if err != nil { return err } e.workers.WithLabelValues("idle").Set(val) case key == "Scoreboard": e.updateScoreboard(v) e.scoreboard.Collect(ch) case key == "ConnsTotal": val, err := strconv.ParseFloat(v, 64) if err != nil { return err } e.connections.WithLabelValues("total").Set(val) connectionInfo = true case key == "ConnsAsyncWriting": val, err := strconv.ParseFloat(v, 64) if err != nil { return err } e.connections.WithLabelValues("writing").Set(val) connectionInfo = true case key == "ConnsAsyncKeepAlive": val, err := strconv.ParseFloat(v, 64) if err != nil { return err } e.connections.WithLabelValues("keepalive").Set(val) connectionInfo = true case key == "ConnsAsyncClosing": val, err := strconv.ParseFloat(v, 64) if err != nil { return err } e.connections.WithLabelValues("closing").Set(val) connectionInfo = true } } e.cpuload.Collect(ch) e.workers.Collect(ch) if connectionInfo { e.connections.Collect(ch) } return nil } func (e *Exporter) Collect(ch chan<- prometheus.Metric) { e.mutex.Lock() // To protect metrics from concurrent collects. defer e.mutex.Unlock() if err := e.collect(ch); err != nil { log.Errorf("Error scraping apache: %s", err) e.scrapeFailures.Inc() e.scrapeFailures.Collect(ch) } return } func main() { flag.Parse() if *showVersion { fmt.Fprintln(os.Stdout, version.Print("apache_exporter")) os.Exit(0) } exporter := NewExporter(*scrapeURI) prometheus.MustRegister(exporter) prometheus.MustRegister(version.NewCollector("apache_exporter")) log.Infoln("Starting apache_exporter", version.Info()) log.Infoln("Build context", version.BuildContext()) log.Infof("Starting Server: %s", *listeningAddress) http.Handle(*metricsEndpoint, prometheus.Handler()) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(`