Add Prometheus metrics endpoint

Add the Prometheus http.Handler to serve metrics at MetricsPath ("/metrics"
by default). This allows Prometheus to scrape metrics from OAuth2 Proxy.

Add a new middleware NewRequestMetrics and attach it to the preAuth
chain. This will collect metrics on all requests made to OAuth2 Proxy

Collapse some calls to Prinf() and os.Exit(1) to Fatalf as they are
equivalent. main() has a strict 50 lines limit so brevity in these
calls appreciated
This commit is contained in:
Sean Jones
2021-02-15 13:45:26 +00:00
parent ce29b16d84
commit a7c8a233ba
12 changed files with 492 additions and 25 deletions
+44 -10
View File
@@ -1,8 +1,10 @@
package main
import (
"context"
"fmt"
"math/rand"
"net/http"
"os"
"os/signal"
"runtime"
@@ -12,6 +14,7 @@ import (
"github.com/ghodss/yaml"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/middleware"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/validation"
"github.com/spf13/pflag"
)
@@ -42,8 +45,7 @@ func main() {
opts, err := loadConfiguration(*config, *alphaConfig, configFlagSet, os.Args[1:])
if err != nil {
logger.Printf("ERROR: %v", err)
os.Exit(1)
logger.Fatalf("ERROR: %v", err)
}
if *convertConfig {
@@ -53,36 +55,68 @@ func main() {
return
}
err = validation.Validate(opts)
if err != nil {
logger.Printf("%s", err)
os.Exit(1)
if err = validation.Validate(opts); err != nil {
logger.Fatalf("%s", err)
}
validator := NewValidator(opts.EmailDomains, opts.AuthenticatedEmailsFile)
oauthproxy, err := NewOAuthProxy(opts, validator)
if err != nil {
logger.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err)
os.Exit(1)
logger.Fatalf("ERROR: Failed to initialise OAuth2 Proxy: %v", err)
}
rand.Seed(time.Now().UnixNano())
oauthProxyStop := make(chan struct{}, 1)
metricsStop := startMetricsServer(opts.MetricsAddress, oauthProxyStop)
s := &Server{
Handler: oauthproxy,
Opts: opts,
stop: make(chan struct{}, 1),
stop: oauthProxyStop,
}
// Observe signals in background goroutine.
go func() {
sigint := make(chan os.Signal, 1)
signal.Notify(sigint, os.Interrupt, syscall.SIGTERM)
<-sigint
s.stop <- struct{}{} // notify having caught signal
s.stop <- struct{}{} // notify having caught signal stop oauthproxy
close(metricsStop) // and the metrics endpoint
}()
s.ListenAndServe()
}
// startMetricsServer will start the metrics server on the specified address.
// It always return a channel to signal stop even when it does not run.
func startMetricsServer(address string, oauthProxyStop chan struct{}) chan struct{} {
stop := make(chan struct{}, 1)
// Attempt to setup the metrics endpoint if we have an address
if address != "" {
s := &http.Server{Addr: address, Handler: middleware.DefaultMetricsHandler}
go func() {
// ListenAndServe always returns a non-nil error. After Shutdown or
// Close, the returned error is ErrServerClosed
if err := s.ListenAndServe(); err != http.ErrServerClosed {
logger.Println(err)
// Stop the metrics shutdown go routine
close(stop)
// Stop the oauthproxy server, we have encounter an unexpected error
close(oauthProxyStop)
}
}()
go func() {
<-stop
if err := s.Shutdown(context.Background()); err != nil {
logger.Print(err)
}
}()
}
return stop
}
// loadConfiguration will load in the user's configuration.
// It will either load the alpha configuration (if alphaConfig is given)
// or the legacy configuration.