12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- package metrics
- import (
- "context"
- "fmt"
- "net"
- "net/http"
- _ "net/http/pprof"
- "runtime"
- "sync"
- "time"
- "github.com/gorilla/mux"
- "github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promhttp"
- "github.com/rs/zerolog"
- "golang.org/x/net/trace"
- )
- const (
- shutdownTimeout = time.Second * 15
- startupTime = time.Millisecond * 500
- )
- func newMetricsHandler(readyServer *ReadyServer) *mux.Router {
- router := mux.NewRouter()
- router.PathPrefix("/debug/").Handler(http.DefaultServeMux)
- router.Handle("/metrics", promhttp.Handler())
- router.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) {
- _, _ = fmt.Fprintf(w, "OK\n")
- })
- if readyServer != nil {
- router.Handle("/ready", readyServer)
- }
- return router
- }
- func ServeMetrics(
- l net.Listener,
- shutdownC <-chan struct{},
- readyServer *ReadyServer,
- log *zerolog.Logger,
- ) (err error) {
- var wg sync.WaitGroup
- // Metrics port is privileged, so no need for further access control
- trace.AuthRequest = func(*http.Request) (bool, bool) { return true, true }
- // TODO: parameterize ReadTimeout and WriteTimeout. The maximum time we can
- // profile CPU usage depends on WriteTimeout
- h := newMetricsHandler(readyServer)
- server := &http.Server{
- ReadTimeout: 10 * time.Second,
- WriteTimeout: 10 * time.Second,
- Handler: h,
- }
- wg.Add(1)
- go func() {
- defer wg.Done()
- err = server.Serve(l)
- }()
- log.Info().Msgf("Starting metrics server on %s", fmt.Sprintf("%v/metrics", l.Addr()))
- // server.Serve will hang if server.Shutdown is called before the server is
- // fully started up. So add artificial delay.
- time.Sleep(startupTime)
- <-shutdownC
- ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
- _ = server.Shutdown(ctx)
- cancel()
- wg.Wait()
- if err == http.ErrServerClosed {
- log.Info().Msg("Metrics server stopped")
- return nil
- }
- log.Err(err).Msg("Metrics server failed")
- return err
- }
- func RegisterBuildInfo(buildTime string, version string) {
- buildInfo := prometheus.NewGaugeVec(
- prometheus.GaugeOpts{
- // Don't namespace build_info, since we want it to be consistent across all Cloudflare services
- Name: "build_info",
- Help: "Build and version information",
- },
- []string{"goversion", "revision", "version"},
- )
- prometheus.MustRegister(buildInfo)
- buildInfo.WithLabelValues(runtime.Version(), buildTime, version).Set(1)
- }
|