github.com/qlik-oss/gopherciser@v0.18.6/metrics/transport.go (about) 1 package metrics 2 3 import ( 4 "context" 5 "flag" 6 "fmt" 7 "net/http" 8 "net/url" 9 "os" 10 "runtime" 11 "strings" 12 "time" 13 14 "github.com/prometheus/client_golang/prometheus" 15 "github.com/prometheus/client_golang/prometheus/promhttp" 16 "github.com/prometheus/client_golang/prometheus/push" 17 18 "github.com/qlik-oss/gopherciser/version" 19 ) 20 21 func setupMetrics(actions []string, apimetrics bool) error { 22 if apimetrics { // If not used these api calls would still be registered. Likely no issue 23 prometheus.MustRegister(ApiCallDuration) 24 prometheus.MustRegister(ApiCallDurationQuantile) 25 err := gopherRegistry.Register(ApiCallDuration) 26 if err != nil { 27 return err 28 } 29 err = gopherRegistry.Register(ApiCallDurationQuantile) 30 if err != nil { 31 return err 32 } 33 } 34 prometheus.MustRegister(GopherActions) 35 prometheus.MustRegister(GopherWarnings) 36 prometheus.MustRegister(GopherErrors) 37 prometheus.MustRegister(GopherUsersTotal) 38 prometheus.MustRegister(GopherActiveUsers) 39 prometheus.MustRegister(GopherResponseTimes) 40 prometheus.MustRegister(GopherActionLatencyHist) 41 prometheus.MustRegister(BuildInfo) 42 43 err := gopherRegistry.Register(GopherActions) 44 if err != nil { 45 return err 46 } 47 err = gopherRegistry.Register(GopherWarnings) 48 if err != nil { 49 return err 50 } 51 err = gopherRegistry.Register(GopherErrors) 52 if err != nil { 53 return err 54 } 55 err = gopherRegistry.Register(GopherUsersTotal) 56 if err != nil { 57 return err 58 } 59 err = gopherRegistry.Register(GopherActiveUsers) 60 if err != nil { 61 return err 62 } 63 err = gopherRegistry.Register(GopherResponseTimes) 64 if err != nil { 65 return err 66 } 67 err = gopherRegistry.Register(GopherActionLatencyHist) 68 if err != nil { 69 return err 70 } 71 err = gopherRegistry.Register(BuildInfo) 72 if err != nil { 73 return err 74 } 75 76 // Initialize metrics 77 for _, action := range actions { 78 GopherActions.WithLabelValues("success", action).Add(0) 79 GopherActions.WithLabelValues("failure", action).Add(0) 80 } 81 82 BuildInfo.WithLabelValues(version.Version, version.Revision, runtime.Version(), runtime.GOARCH, runtime.GOOS).Set(1) 83 84 return nil 85 } 86 87 // PushMetrics handles the constant pushing of metrics to prometheus 88 func PushMetrics(ctx context.Context, metricsTarget, job string, groupingKeys, actions []string, apiMetrics bool) error { 89 err := setupMetrics(actions, apiMetrics) 90 if err != nil { 91 return err 92 } 93 94 _, err = url.Parse(metricsTarget) 95 if err != nil { 96 return fmt.Errorf("can't parse metricsAddress <%s>, metrics will not be pushed", metricsTarget) 97 } 98 99 var addr = flag.String("push-address", metricsTarget, "The address to push prometheus metrics") 100 pusher := push.New(*addr, job).Gatherer(gopherRegistry) 101 for _, gk := range groupingKeys { 102 kv := strings.SplitN(gk, "=", 2) 103 if len(kv) < 2 || len(kv[0]) == 0 { 104 return fmt.Errorf("can't parse grouping key %q: must be in 'key=value' form", gk) 105 } 106 pusher = pusher.Grouping(kv[0], kv[1]) 107 } 108 109 //Pushes prometheus metrics every minute 110 const interval time.Duration = 1 * time.Minute 111 ticker := time.NewTicker(interval) 112 go func() { 113 for { 114 select { 115 case <-ticker.C: 116 err := pusher.Push() 117 if err != nil { 118 _, _ = fmt.Fprintf(os.Stderr, "Push error received: %s", err) 119 } 120 case <-ctx.Done(): 121 _ = pusher.Push() // push the latest values, but ignore error when shutting down 122 123 ticker.Stop() 124 return 125 } 126 } 127 }() 128 return nil 129 } 130 131 // PullMetrics handle the serving of prometheus metrics on the metrics endpoint 132 func PullMetrics(ctx context.Context, metricsPort int, actions []string) error { 133 err := setupMetrics(actions, true) 134 if err != nil { 135 return err 136 } 137 138 var addr = flag.String("pull-address", fmt.Sprintf(":%d", metricsPort), "The address to listen on for HTTP requests.") 139 srv := &http.Server{Addr: *addr} 140 141 http.Handle("/metrics", promhttp.Handler()) 142 143 go func() { 144 if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { 145 _, _ = fmt.Fprintf(os.Stderr, "Httpserver: ListenAndServe() error: %s", err) 146 } 147 }() 148 149 go func() { 150 <-ctx.Done() 151 //nolint:staticcheck 152 shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second) 153 defer shutdownCancel() 154 if err := srv.Shutdown(shutdownCtx); err != nil { 155 _, _ = fmt.Fprintf(os.Stderr, "Httpserver: Shutdown() error: %s", err) 156 } 157 }() 158 159 return nil 160 }