*: Add go runtime, process, HTTP and gRPC metrics
This commit is contained in:
		| @@ -22,6 +22,7 @@ type Config struct { | |||||||
| 	Issuer    string    `json:"issuer"` | 	Issuer    string    `json:"issuer"` | ||||||
| 	Storage   Storage   `json:"storage"` | 	Storage   Storage   `json:"storage"` | ||||||
| 	Web       Web       `json:"web"` | 	Web       Web       `json:"web"` | ||||||
|  | 	Telemetry Telemetry `json:"telemetry"` | ||||||
| 	OAuth2    OAuth2    `json:"oauth2"` | 	OAuth2    OAuth2    `json:"oauth2"` | ||||||
| 	GRPC      GRPC      `json:"grpc"` | 	GRPC      GRPC      `json:"grpc"` | ||||||
| 	Expiry    Expiry    `json:"expiry"` | 	Expiry    Expiry    `json:"expiry"` | ||||||
| @@ -104,6 +105,11 @@ type Web struct { | |||||||
| 	AllowedOrigins []string `json:"allowedOrigins"` | 	AllowedOrigins []string `json:"allowedOrigins"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Telemetry is the config format for telemetry including the HTTP server config. | ||||||
|  | type Telemetry struct { | ||||||
|  | 	HTTP string `json:"http"` | ||||||
|  | } | ||||||
|  |  | ||||||
| // GRPC is the config for the gRPC API. | // GRPC is the config for the gRPC API. | ||||||
| type GRPC struct { | type GRPC struct { | ||||||
| 	// The port to listen on. | 	// The port to listen on. | ||||||
|   | |||||||
| @@ -14,6 +14,9 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/ghodss/yaml" | 	"github.com/ghodss/yaml" | ||||||
|  | 	grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus" | ||||||
|  | 	"github.com/prometheus/client_golang/prometheus" | ||||||
|  | 	"github.com/prometheus/client_golang/prometheus/promhttp" | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
| 	"github.com/spf13/cobra" | 	"github.com/spf13/cobra" | ||||||
| 	"google.golang.org/grpc" | 	"google.golang.org/grpc" | ||||||
| @@ -93,7 +96,25 @@ func serve(cmd *cobra.Command, args []string) error { | |||||||
|  |  | ||||||
| 	logger.Infof("config issuer: %s", c.Issuer) | 	logger.Infof("config issuer: %s", c.Issuer) | ||||||
|  |  | ||||||
|  | 	prometheusRegistry := prometheus.NewRegistry() | ||||||
|  | 	err = prometheusRegistry.Register(prometheus.NewGoCollector()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("failed to register Go runtime metrics: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = prometheusRegistry.Register(prometheus.NewProcessCollector(os.Getpid(), "")) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("failed to register process metrics: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	grpcMetrics := grpcprometheus.NewServerMetrics() | ||||||
|  | 	err = prometheusRegistry.Register(grpcMetrics) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("failed to register gRPC server metrics: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	var grpcOptions []grpc.ServerOption | 	var grpcOptions []grpc.ServerOption | ||||||
|  |  | ||||||
| 	if c.GRPC.TLSCert != "" { | 	if c.GRPC.TLSCert != "" { | ||||||
| 		if c.GRPC.TLSClientCA != "" { | 		if c.GRPC.TLSClientCA != "" { | ||||||
| 			// Parse certificates from certificate file and key file for server. | 			// Parse certificates from certificate file and key file for server. | ||||||
| @@ -117,7 +138,11 @@ func serve(cmd *cobra.Command, args []string) error { | |||||||
| 				ClientAuth:   tls.RequireAndVerifyClientCert, | 				ClientAuth:   tls.RequireAndVerifyClientCert, | ||||||
| 				ClientCAs:    cPool, | 				ClientCAs:    cPool, | ||||||
| 			} | 			} | ||||||
| 			grpcOptions = append(grpcOptions, grpc.Creds(credentials.NewTLS(&tlsConfig))) | 			grpcOptions = append(grpcOptions, | ||||||
|  | 				grpc.Creds(credentials.NewTLS(&tlsConfig)), | ||||||
|  | 				grpc.StreamInterceptor(grpcMetrics.StreamServerInterceptor()), | ||||||
|  | 				grpc.UnaryInterceptor(grpcMetrics.UnaryServerInterceptor()), | ||||||
|  | 			) | ||||||
| 		} else { | 		} else { | ||||||
| 			opt, err := credentials.NewServerTLSFromFile(c.GRPC.TLSCert, c.GRPC.TLSKey) | 			opt, err := credentials.NewServerTLSFromFile(c.GRPC.TLSCert, c.GRPC.TLSKey) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| @@ -199,6 +224,7 @@ func serve(cmd *cobra.Command, args []string) error { | |||||||
| 		Web:                    c.Frontend, | 		Web:                    c.Frontend, | ||||||
| 		Logger:                 logger, | 		Logger:                 logger, | ||||||
| 		Now:                    now, | 		Now:                    now, | ||||||
|  | 		PrometheusRegistry:     prometheusRegistry, | ||||||
| 	} | 	} | ||||||
| 	if c.Expiry.SigningKeys != "" { | 	if c.Expiry.SigningKeys != "" { | ||||||
| 		signingKeys, err := time.ParseDuration(c.Expiry.SigningKeys) | 		signingKeys, err := time.ParseDuration(c.Expiry.SigningKeys) | ||||||
| @@ -222,7 +248,17 @@ func serve(cmd *cobra.Command, args []string) error { | |||||||
| 		return fmt.Errorf("failed to initialize server: %v", err) | 		return fmt.Errorf("failed to initialize server: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	telemetryServ := http.NewServeMux() | ||||||
|  | 	telemetryServ.Handle("/metrics", promhttp.HandlerFor(prometheusRegistry, promhttp.HandlerOpts{})) | ||||||
|  |  | ||||||
| 	errc := make(chan error, 3) | 	errc := make(chan error, 3) | ||||||
|  | 	if c.Telemetry.HTTP != "" { | ||||||
|  | 		logger.Infof("listening (http/telemetry) on %s", c.Telemetry.HTTP) | ||||||
|  | 		go func() { | ||||||
|  | 			err := http.ListenAndServe(c.Telemetry.HTTP, telemetryServ) | ||||||
|  | 			errc <- fmt.Errorf("listening on %s failed: %v", c.Telemetry.HTTP, err) | ||||||
|  | 		}() | ||||||
|  | 	} | ||||||
| 	if c.Web.HTTP != "" { | 	if c.Web.HTTP != "" { | ||||||
| 		logger.Infof("listening (http) on %s", c.Web.HTTP) | 		logger.Infof("listening (http) on %s", c.Web.HTTP) | ||||||
| 		go func() { | 		go func() { | ||||||
| @@ -247,6 +283,7 @@ func serve(cmd *cobra.Command, args []string) error { | |||||||
| 				} | 				} | ||||||
| 				s := grpc.NewServer(grpcOptions...) | 				s := grpc.NewServer(grpcOptions...) | ||||||
| 				api.RegisterDexServer(s, server.NewAPI(serverConfig.Storage, logger)) | 				api.RegisterDexServer(s, server.NewAPI(serverConfig.Storage, logger)) | ||||||
|  | 				grpcMetrics.InitializeMetrics(s) | ||||||
| 				err = s.Serve(list) | 				err = s.Serve(list) | ||||||
| 				return fmt.Errorf("listening on %s failed: %v", c.GRPC.Addr, err) | 				return fmt.Errorf("listening on %s failed: %v", c.GRPC.Addr, err) | ||||||
| 			}() | 			}() | ||||||
|   | |||||||
| @@ -20,6 +20,10 @@ web: | |||||||
|   # tlsCert: /etc/dex/tls.crt |   # tlsCert: /etc/dex/tls.crt | ||||||
|   # tlsKey: /etc/dex/tls.key |   # tlsKey: /etc/dex/tls.key | ||||||
|  |  | ||||||
|  | # Configuration for telemetry | ||||||
|  | telemetry: | ||||||
|  |   http: 0.0.0.0:5558 | ||||||
|  |  | ||||||
| # Uncomment this block to enable the gRPC API. This values MUST be different | # Uncomment this block to enable the gRPC API. This values MUST be different | ||||||
| # from the HTTP endpoints. | # from the HTTP endpoints. | ||||||
| # grpc: | # grpc: | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"path" | 	"path" | ||||||
|  | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"sync/atomic" | 	"sync/atomic" | ||||||
| @@ -15,8 +16,10 @@ import ( | |||||||
|  |  | ||||||
| 	"golang.org/x/crypto/bcrypt" | 	"golang.org/x/crypto/bcrypt" | ||||||
|  |  | ||||||
|  | 	"github.com/felixge/httpsnoop" | ||||||
| 	"github.com/gorilla/handlers" | 	"github.com/gorilla/handlers" | ||||||
| 	"github.com/gorilla/mux" | 	"github.com/gorilla/mux" | ||||||
|  | 	"github.com/prometheus/client_golang/prometheus" | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
|  |  | ||||||
| 	"github.com/coreos/dex/connector" | 	"github.com/coreos/dex/connector" | ||||||
| @@ -75,6 +78,8 @@ type Config struct { | |||||||
| 	Web WebConfig | 	Web WebConfig | ||||||
|  |  | ||||||
| 	Logger logrus.FieldLogger | 	Logger logrus.FieldLogger | ||||||
|  |  | ||||||
|  | 	PrometheusRegistry *prometheus.Registry | ||||||
| } | } | ||||||
|  |  | ||||||
| // WebConfig holds the server's frontend templates and asset configuration. | // WebConfig holds the server's frontend templates and asset configuration. | ||||||
| @@ -214,9 +219,26 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy) | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	requestCounter := prometheus.NewCounterVec(prometheus.CounterOpts{ | ||||||
|  | 		Name: "http_requests_total", | ||||||
|  | 		Help: "Count of all HTTP requests.", | ||||||
|  | 	}, []string{"handler", "code", "method"}) | ||||||
|  |  | ||||||
|  | 	err = c.PrometheusRegistry.Register(requestCounter) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("server: Failed to register Prometheus HTTP metrics: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	instrumentHandlerCounter := func(handlerName string, handler http.Handler) http.HandlerFunc { | ||||||
|  | 		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 			m := httpsnoop.CaptureMetrics(handler, w, r) | ||||||
|  | 			requestCounter.With(prometheus.Labels{"handler": handlerName, "code": strconv.Itoa(m.Code), "method": r.Method}).Inc() | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	r := mux.NewRouter() | 	r := mux.NewRouter() | ||||||
| 	handleFunc := func(p string, h http.HandlerFunc) { | 	handleFunc := func(p string, h http.HandlerFunc) { | ||||||
| 		r.HandleFunc(path.Join(issuerURL.Path, p), h) | 		r.HandleFunc(path.Join(issuerURL.Path, p), instrumentHandlerCounter(p, h)) | ||||||
| 	} | 	} | ||||||
| 	handlePrefix := func(p string, h http.Handler) { | 	handlePrefix := func(p string, h http.Handler) { | ||||||
| 		prefix := path.Join(issuerURL.Path, p) | 		prefix := path.Join(issuerURL.Path, p) | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ import ( | |||||||
|  |  | ||||||
| 	oidc "github.com/coreos/go-oidc" | 	oidc "github.com/coreos/go-oidc" | ||||||
| 	"github.com/kylelemons/godebug/pretty" | 	"github.com/kylelemons/godebug/pretty" | ||||||
|  | 	"github.com/prometheus/client_golang/prometheus" | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
| 	"golang.org/x/crypto/bcrypt" | 	"golang.org/x/crypto/bcrypt" | ||||||
| 	"golang.org/x/oauth2" | 	"golang.org/x/oauth2" | ||||||
| @@ -93,6 +94,7 @@ func newTestServer(ctx context.Context, t *testing.T, updateConfig func(c *Confi | |||||||
| 			Dir: filepath.Join(os.Getenv("GOPATH"), "src/github.com/coreos/dex/web"), | 			Dir: filepath.Join(os.Getenv("GOPATH"), "src/github.com/coreos/dex/web"), | ||||||
| 		}, | 		}, | ||||||
| 		Logger:             logger, | 		Logger:             logger, | ||||||
|  | 		PrometheusRegistry: prometheus.NewRegistry(), | ||||||
| 	} | 	} | ||||||
| 	if updateConfig != nil { | 	if updateConfig != nil { | ||||||
| 		updateConfig(&config) | 		updateConfig(&config) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user