Skip to content

Commit

Permalink
Move the logic for collecting the metrics into a submodule.
Browse files Browse the repository at this point in the history
So that this logic can be included in other libraries without the main func.
  • Loading branch information
glennslaven committed Aug 21, 2019
1 parent 5d1e706 commit 693042c
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 173 deletions.
201 changes: 56 additions & 145 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,101 +1,51 @@
package main
package main // import "github.com/jonnenauha/prometheus_varnish_exporter"

import (
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"sync"
"time"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"

"github.com/jonnenauha/prometheus_varnish_exporter/varnishexporter"
)

var (
ApplicationName = "prometheus_varnish_exporter"
Version string
VersionHash string
VersionDate string

PrometheusExporter = NewPrometheusExporter()
VarnishVersion = NewVarnishVersion()
ExitHandler = &exitHandler{}

StartParams = &startParams{
ListenAddress: ":9131", // Reserved and publicly announced at https://github.com/prometheus/prometheus/wiki/Default-port-allocations
Path: "/metrics",
VarnishstatExe: "varnishstat",
Params: &varnishstatParams{},
}
logger *log.Logger
)

type startParams struct {
ListenAddress string
Path string
HealthPath string
VarnishstatExe string
VarnishDockerContainer string
Params *varnishstatParams

Verbose bool
ExitOnErrors bool
Test bool
Raw bool
WithGoMetrics bool

noExit bool // deprecated
}

type varnishstatParams struct {
Instance string
VSM string
}

func (p *varnishstatParams) isEmpty() bool {
return p.Instance == "" && p.VSM == ""
}

func (p *varnishstatParams) make() (params []string) {
// -n
if p.Instance != "" {
params = append(params, "-n", p.Instance)
}
// -N is not supported by 3.x
if p.VSM != "" && VarnishVersion.EqualsOrGreater(4, 0) {
params = append(params, "-N", p.VSM)
}
return params
}

func init() {
// prometheus conventions
flag.StringVar(&StartParams.ListenAddress, "web.listen-address", StartParams.ListenAddress, "Address on which to expose metrics and web interface.")
flag.StringVar(&StartParams.Path, "web.telemetry-path", StartParams.Path, "Path under which to expose metrics.")
flag.StringVar(&StartParams.HealthPath, "web.health-path", StartParams.HealthPath, "Path under which to expose healthcheck. Disabled unless configured.")
flag.StringVar(&varnishexporter.StartParams.ListenAddress, "web.listen-address", varnishexporter.StartParams.ListenAddress, "Address on which to expose metrics and web interface.")
flag.StringVar(&varnishexporter.StartParams.Path, "web.telemetry-path", varnishexporter.StartParams.Path, "Path under which to expose metrics.")
flag.StringVar(&varnishexporter.StartParams.HealthPath, "web.health-path", varnishexporter.StartParams.HealthPath, "Path under which to expose healthcheck. Disabled unless configured.")

// varnish
flag.StringVar(&StartParams.VarnishstatExe, "varnishstat-path", StartParams.VarnishstatExe, "Path to varnishstat.")
flag.StringVar(&StartParams.Params.Instance, "n", StartParams.Params.Instance, "varnishstat -n value.")
flag.StringVar(&StartParams.Params.VSM, "N", StartParams.Params.VSM, "varnishstat -N value.")
flag.StringVar(&varnishexporter.StartParams.VarnishstatExe, "varnishstat-path", varnishexporter.StartParams.VarnishstatExe, "Path to varnishstat.")
flag.StringVar(&varnishexporter.StartParams.Params.Instance, "n", varnishexporter.StartParams.Params.Instance, "varnishstat -n value.")
flag.StringVar(&varnishexporter.StartParams.Params.VSM, "N", varnishexporter.StartParams.Params.VSM, "varnishstat -N value.")

// docker
flag.StringVar(&StartParams.VarnishDockerContainer, "docker-container-name", StartParams.VarnishDockerContainer, "Docker container name to exec varnishstat in.")
flag.StringVar(&varnishexporter.StartParams.VarnishDockerContainer, "docker-container-name", varnishexporter.StartParams.VarnishDockerContainer, "Docker container name to exec varnishstat in.")

// modes
version := false
flag.BoolVar(&version, "version", version, "Print version and exit")
flag.BoolVar(&StartParams.ExitOnErrors, "exit-on-errors", StartParams.ExitOnErrors, "Exit process on scrape errors.")
flag.BoolVar(&StartParams.Verbose, "verbose", StartParams.Verbose, "Verbose logging.")
flag.BoolVar(&StartParams.Test, "test", StartParams.Test, "Test varnishstat availability, prints available metrics and exits.")
flag.BoolVar(&StartParams.Raw, "raw", StartParams.Test, "Raw stdout logging without timestamps.")
flag.BoolVar(&StartParams.WithGoMetrics, "with-go-metrics", StartParams.WithGoMetrics, "Export go runtime and http handler metrics")
flag.BoolVar(&varnishexporter.StartParams.ExitOnErrors, "exit-on-errors", varnishexporter.StartParams.ExitOnErrors, "Exit process on scrape errors.")
flag.BoolVar(&varnishexporter.StartParams.Verbose, "verbose", varnishexporter.StartParams.Verbose, "Verbose varnishexporter.Logging.")
flag.BoolVar(&varnishexporter.StartParams.Test, "test", varnishexporter.StartParams.Test, "Test varnishstat availability, prints available metrics and exits.")
flag.BoolVar(&varnishexporter.StartParams.Raw, "raw", varnishexporter.StartParams.Test, "Raw stdout varnishexporter.Logging without timestamps.")
flag.BoolVar(&varnishexporter.StartParams.WithGoMetrics, "with-go-metrics", varnishexporter.StartParams.WithGoMetrics, "Export go runtime and http handler metrics")

// deprecated
flag.BoolVar(&StartParams.noExit, "no-exit", StartParams.noExit, "Deprecated: see -exit-on-errors")
flag.BoolVar(&varnishexporter.StartParams.NoExit, "no-exit", varnishexporter.StartParams.NoExit, "Deprecated: see -exit-on-errors")

flag.Parse()

Expand All @@ -104,42 +54,40 @@ func init() {
os.Exit(0)
}

logger = log.New(os.Stdout, "", log.Ldate|log.Ltime)

if len(StartParams.Path) == 0 || StartParams.Path[0] != '/' {
logFatal("-web.telemetry-path cannot be empty and must start with a slash '/', given %q", StartParams.Path)
if len(varnishexporter.StartParams.Path) == 0 || varnishexporter.StartParams.Path[0] != '/' {
varnishexporter.LogFatal("-web.telemetry-path cannot be empty and must start with a slash '/', given %q", varnishexporter.StartParams.Path)
}
if len(StartParams.HealthPath) != 0 && StartParams.HealthPath[0] != '/' {
logFatal("-web.health-path must start with a slash '/' if configured, given %q", StartParams.HealthPath)
if len(varnishexporter.StartParams.HealthPath) != 0 && varnishexporter.StartParams.HealthPath[0] != '/' {
varnishexporter.LogFatal("-web.health-path must start with a slash '/' if configured, given %q", varnishexporter.StartParams.HealthPath)
}
if StartParams.Path == StartParams.HealthPath {
logFatal("-web.telemetry-path and -web.health-path cannot have same value")
if varnishexporter.StartParams.Path == varnishexporter.StartParams.HealthPath {
varnishexporter.LogFatal("-web.telemetry-path and -web.health-path cannot have same value")
}

// Don't log warning on !noExit as that would spam for the formed default value.
if StartParams.noExit {
logWarn("-no-exit is deprecated. As of v1.5 it is the default behavior not to exit process on scrape errors. You can remove this parameter.")
// Don't varnishexporter.Log warning on !noExit as that would spam for the formed default value.
if varnishexporter.StartParams.NoExit {
varnishexporter.LogWarn("-no-exit is deprecated. As of v1.5 it is the default behavior not to exit process on scrape errors. You can remove this parameter.")
}

// Test run or user explicitly wants to exit on any scrape errors during runtime.
ExitHandler.exitOnError = StartParams.Test == true || StartParams.ExitOnErrors == true
varnishexporter.ExitHandler.ExitOnError = varnishexporter.StartParams.Test == true || varnishexporter.StartParams.ExitOnErrors == true
}

func main() {
if b, err := json.MarshalIndent(StartParams, "", " "); err == nil {
logInfo("%s %s %s", ApplicationName, getVersion(false), b)
if b, err := json.MarshalIndent(varnishexporter.StartParams, "", " "); err == nil {
varnishexporter.LogInfo("%s %s %s", ApplicationName, getVersion(false), b)
} else {
logFatal(err.Error())
varnishexporter.LogFatal(err.Error())
}

// Initialize
if err := VarnishVersion.Initialize(); err != nil {
ExitHandler.Errorf("Varnish version initialize failed: %s", err.Error())
if err := varnishexporter.VarnishVersion.Initialize(); err != nil {
varnishexporter.ExitHandler.Errorf("Varnish version initialize failed: %s", err.Error())
}
if VarnishVersion.Valid() {
logInfo("Found varnishstat %s", VarnishVersion)
if err := PrometheusExporter.Initialize(); err != nil {
logFatal("Prometheus exporter initialize failed: %s", err.Error())
if varnishexporter.VarnishVersion.Valid() {
varnishexporter.LogInfo("Found varnishstat %s", varnishexporter.VarnishVersion)
if err := varnishexporter.PrometheusExporter.Initialize(); err != nil {
varnishexporter.LogFatal("Prometheus exporter initialize failed: %s", err.Error())
}
}

Expand All @@ -149,105 +97,68 @@ func main() {
metrics := make(chan prometheus.Metric)
go func() {
for m := range metrics {
if StartParams.Test {
logInfo("%s", m.Desc())
if varnishexporter.StartParams.Test {
varnishexporter.LogInfo("%s", m.Desc())
}
}
done <- true
}()
tStart := time.Now()
buf, err := ScrapeVarnish(metrics)
buf, err := varnishexporter.ScrapeVarnish(metrics)
close(metrics)
<-done

if err == nil {
logInfo("Test scrape done in %s", time.Now().Sub(tStart))
logRaw("")
varnishexporter.LogInfo("Test scrape done in %s", time.Now().Sub(tStart))
varnishexporter.LogRaw("")
} else {
if len(buf) > 0 {
logRaw("\n%s", buf)
varnishexporter.LogRaw("\n%s", buf)
}
ExitHandler.Errorf("Startup test: %s", err.Error())
varnishexporter.ExitHandler.Errorf("Startup test: %s", err.Error())
}
}
if StartParams.Test {
if varnishexporter.StartParams.Test {
return
}

// Start serving
logInfo("Server starting on %s with metrics path %s", StartParams.ListenAddress, StartParams.Path)
varnishexporter.LogInfo("Server starting on %s with metrics path %s", varnishexporter.StartParams.ListenAddress, varnishexporter.StartParams.Path)

if !StartParams.WithGoMetrics {
if !varnishexporter.StartParams.WithGoMetrics {
registry := prometheus.NewRegistry()
if err := registry.Register(PrometheusExporter); err != nil {
logFatal("registry.Register failed: %s", err.Error())
if err := registry.Register(varnishexporter.PrometheusExporter); err != nil {
varnishexporter.LogFatal("registry.Register failed: %s", err.Error())
}
handler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{
ErrorLog: logger,
ErrorLog: varnishexporter.Logger,
})
http.Handle(StartParams.Path, handler)
http.Handle(varnishexporter.StartParams.Path, handler)
} else {
prometheus.MustRegister(PrometheusExporter)
http.Handle(StartParams.Path, promhttp.Handler())
prometheus.MustRegister(varnishexporter.PrometheusExporter)
http.Handle(varnishexporter.StartParams.Path, promhttp.Handler())
}

if StartParams.Path != "/" {
if varnishexporter.StartParams.Path != "/" {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`<html>
<head><title>Varnish Exporter</title></head>
<body>
<h1>Varnish Exporter</h1>
<p><a href="` + StartParams.Path + `">Metrics</a></p>
<p><a href="` + varnishexporter.StartParams.Path + `">Metrics</a></p>
</body>
</html>`))
})
}
if StartParams.HealthPath != "" {
http.HandleFunc(StartParams.HealthPath, func(w http.ResponseWriter, r *http.Request) {
if varnishexporter.StartParams.HealthPath != "" {
http.HandleFunc(varnishexporter.StartParams.HealthPath, func(w http.ResponseWriter, r *http.Request) {
// As noted in the "up" metric, needs some way to determine if everything is actually Ok.
// For now, this just lets us check that we're accepting connections
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Ok")
})
}
logFatalError(http.ListenAndServe(StartParams.ListenAddress, nil))
}

type exitHandler struct {
sync.RWMutex
exitOnError bool
err error
}

func (ex *exitHandler) Errorf(format string, a ...interface{}) error {
return ex.Set(fmt.Errorf(format, a...))
}

func (ex *exitHandler) HasError() bool {
ex.RLock()
hasError := ex.err != nil
ex.RUnlock()
return hasError
}

func (ex *exitHandler) Set(err error) error {
ex.Lock()
defer ex.Unlock()

if err == nil {
ex.err = nil
return nil
}

errDiffers := ex.err == nil || ex.err.Error() != err.Error()
ex.err = err

if ex.exitOnError {
logFatal("%s", err.Error())
} else if errDiffers {
logError("%s", err.Error())
}
return err
varnishexporter.LogFatalError(http.ListenAndServe(varnishexporter.StartParams.ListenAddress, nil))
}

func getVersion(date bool) (version string) {
Expand Down
12 changes: 8 additions & 4 deletions prometheus.go → varnishexporter/prometheus.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package varnishexporter

import (
"regexp"
Expand All @@ -13,6 +13,10 @@ const (
exporterNamespace = "varnish"
)

var (
PrometheusExporter = NewPrometheusExporter()
)

// prometheusExporter

type prometheusExporter struct {
Expand Down Expand Up @@ -53,7 +57,7 @@ func (pe *prometheusExporter) Describe(ch chan<- *prometheus.Desc) {
}

if StartParams.Verbose {
logInfo("prometheus.Collector.Describe %s", time.Now().Sub(start))
LogInfo("prometheus.Collector.Describe %s", time.Now().Sub(start))
}
}

Expand Down Expand Up @@ -84,7 +88,7 @@ func (pe *prometheusExporter) Collect(ch chan<- prometheus.Metric) {

if err == nil {
if hadError {
logInfo("Successful scrape")
LogInfo("Successful scrape")
}
pe.up.Set(1)
} else {
Expand All @@ -101,7 +105,7 @@ func (pe *prometheusExporter) Collect(ch chan<- prometheus.Metric) {
if err != nil {
postfix = " (scrape failed)"
}
logInfo("prometheus.Collector.Collect %s%s", time.Now().Sub(start), postfix)
LogInfo("prometheus.Collector.Collect %s%s", time.Now().Sub(start), postfix)
}
}

Expand Down
Loading

0 comments on commit 693042c

Please sign in to comment.