forked from electricdusk/rushlink
		
	Merge pull request 'Add request_duration_seconds metric' (#72) from metrics into master
Reviewed-on: dsprenkels/rushlink#72
This commit is contained in:
		
						commit
						29ee3dc6fd
					
				
							
								
								
									
										106
									
								
								metrics.go
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								metrics.go
									
									
									
									
									
								
							@ -14,42 +14,59 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const metricNamespace = "rushlink"
 | 
					const metricNamespace = "rushlink"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var metricRequestsTotalCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
 | 
					// metricURLsTotalGauge counts the number of requests that are handled by
 | 
				
			||||||
	Namespace: metricNamespace,
 | 
					// the application, partitioned by status code and HTTP method.
 | 
				
			||||||
	Subsystem: "http",
 | 
					//
 | 
				
			||||||
	Name:      "requests_total",
 | 
					// This counter is updated by the router.
 | 
				
			||||||
	Help:      "How many HTTP requests processed, partitioned by status code and HTTP method.",
 | 
					var metricRequestsTotalCounter = prometheus.NewCounterVec(
 | 
				
			||||||
}, []string{"code", "method"})
 | 
						prometheus.CounterOpts{
 | 
				
			||||||
 | 
							Namespace: metricNamespace,
 | 
				
			||||||
 | 
							Subsystem: "http",
 | 
				
			||||||
 | 
							Name:      "requests_total",
 | 
				
			||||||
 | 
							Help:      "How many HTTP requests processed, partitioned by status code and HTTP method.",
 | 
				
			||||||
 | 
						}, []string{"code", "method"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func metricURLsTotal(database *db.Database) float64 {
 | 
					// metricRequestsLatencyNanoSeconds keeps track of the request latencies for
 | 
				
			||||||
	var metric float64
 | 
					// each http request.
 | 
				
			||||||
	if err := database.Transaction(func(tx *db.Database) error {
 | 
					//
 | 
				
			||||||
		var count int64
 | 
					// This historogram is updated by the router.
 | 
				
			||||||
		if err := database.Model(&db.Paste{}).Count(&count).Error; err != nil {
 | 
					var metricRequestsLatencyNanoSeconds = prometheus.NewHistogramVec(
 | 
				
			||||||
			return err
 | 
						prometheus.HistogramOpts{
 | 
				
			||||||
		}
 | 
							Namespace: metricNamespace,
 | 
				
			||||||
		metric = float64(count)
 | 
							Subsystem: "http",
 | 
				
			||||||
		return nil
 | 
							Name:      "request_duration_seconds",
 | 
				
			||||||
	}); err != nil {
 | 
							Buckets: []float64{
 | 
				
			||||||
		log.Printf("error: %v", errors.Wrap(err, "fetching pastes_total metric"))
 | 
								float64(500e-6),
 | 
				
			||||||
		return 0
 | 
								float64(1e-3),
 | 
				
			||||||
	}
 | 
								float64(2e-3),
 | 
				
			||||||
	return metric
 | 
								float64(5e-3),
 | 
				
			||||||
}
 | 
								float64(10e-3),
 | 
				
			||||||
 | 
								float64(20e-3),
 | 
				
			||||||
 | 
								float64(50e-3),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Help: "The latency of each HTTP request, partitioned by status code and HTTP method.",
 | 
				
			||||||
 | 
						}, []string{"code", "method"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// metricURLsTotalGauge measures the amount of pastes stored in the database,
 | 
				
			||||||
 | 
					// partitioned by type and state.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Its values are computed on the fly by updateMetrics().
 | 
				
			||||||
 | 
					var metricURLsTotalGauge = prometheus.NewGaugeVec(
 | 
				
			||||||
 | 
						prometheus.GaugeOpts{
 | 
				
			||||||
 | 
							Namespace: metricNamespace,
 | 
				
			||||||
 | 
							Subsystem: "pastes",
 | 
				
			||||||
 | 
							Name:      "urls_total",
 | 
				
			||||||
 | 
							Help:      "The current amount of pastes in the database, partitioned by state and type.",
 | 
				
			||||||
 | 
						}, []string{"state", "type"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// StartMetricsServer starts sering Prometheus metrics exports on addr
 | 
					// StartMetricsServer starts sering Prometheus metrics exports on addr
 | 
				
			||||||
func StartMetricsServer(addr string, database *db.Database, fs *db.FileStore) {
 | 
					func StartMetricsServer(addr string, database *db.Database, fs *db.FileStore) {
 | 
				
			||||||
	prometheus.MustRegister(metricRequestsTotalCounter)
 | 
						prometheus.MustRegister(metricRequestsTotalCounter)
 | 
				
			||||||
 | 
						prometheus.MustRegister(metricRequestsLatencyNanoSeconds)
 | 
				
			||||||
	prometheus.MustRegister(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
 | 
						prometheus.MustRegister(metricURLsTotalGauge)
 | 
				
			||||||
		Namespace: metricNamespace,
 | 
					 | 
				
			||||||
		Subsystem: "pastes",
 | 
					 | 
				
			||||||
		Name:      "urls_total",
 | 
					 | 
				
			||||||
		Help:      "The current amount of pastes in the database.",
 | 
					 | 
				
			||||||
	}, func() float64 { return metricURLsTotal(database) }))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	router := mux.NewRouter()
 | 
						router := mux.NewRouter()
 | 
				
			||||||
	router.Handle("/metrics", promhttp.Handler()).Methods("GET")
 | 
						router.Handle("/metrics", &MetricsHandler{database}).Methods("GET")
 | 
				
			||||||
	srv := &http.Server{
 | 
						srv := &http.Server{
 | 
				
			||||||
		Handler:      router,
 | 
							Handler:      router,
 | 
				
			||||||
		Addr:         addr,
 | 
							Addr:         addr,
 | 
				
			||||||
@ -58,3 +75,34 @@ func StartMetricsServer(addr string, database *db.Database, fs *db.FileStore) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	log.Fatal(srv.ListenAndServe())
 | 
						log.Fatal(srv.ListenAndServe())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MetricsHandler struct {
 | 
				
			||||||
 | 
						db *db.Database
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (mh *MetricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
						mh.updateMetrics()
 | 
				
			||||||
 | 
						promhttp.Handler().ServeHTTP(w, r)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (mh *MetricsHandler) updateMetrics() {
 | 
				
			||||||
 | 
						// Update metricURLsTotalGauge
 | 
				
			||||||
 | 
						results := make([](struct {
 | 
				
			||||||
 | 
							Type  db.PasteType
 | 
				
			||||||
 | 
							State db.PasteState
 | 
				
			||||||
 | 
							Count float64
 | 
				
			||||||
 | 
						}), 0)
 | 
				
			||||||
 | 
						query := mh.db.Unscoped().Model(&db.Paste{}).Select("type", "state", "COUNT(*) as count").Group("type, state").Find(&results)
 | 
				
			||||||
 | 
						if err := query.Error; err != nil {
 | 
				
			||||||
 | 
							log.Printf("error: %v", errors.Wrap(err, "fetching pastes_total metric"))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						metricURLsTotalGauge.Reset()
 | 
				
			||||||
 | 
						for _, r := range results {
 | 
				
			||||||
 | 
							labels := map[string]string{
 | 
				
			||||||
 | 
								"type":  r.Type.String(),
 | 
				
			||||||
 | 
								"state": r.State.String(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							metricURLsTotalGauge.With(labels).Set(r.Count)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										10
									
								
								router.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								router.go
									
									
									
									
									
								
							@ -58,10 +58,18 @@ func (rl *rushlink) recoveryMiddleware(next http.Handler) http.Handler {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (rl *rushlink) metricsMiddleware(next http.Handler) http.Handler {
 | 
					func (rl *rushlink) metricsMiddleware(next http.Handler) http.Handler {
 | 
				
			||||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
						return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							tick := time.Now()
 | 
				
			||||||
		srw := statusResponseWriter{Inner: w}
 | 
							srw := statusResponseWriter{Inner: w}
 | 
				
			||||||
		next.ServeHTTP(&srw, r)
 | 
							next.ServeHTTP(&srw, r)
 | 
				
			||||||
 | 
							tock := time.Now()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		status := strconv.Itoa(srw.StatusCode)
 | 
							status := strconv.Itoa(srw.StatusCode)
 | 
				
			||||||
		metricRequestsTotalCounter.WithLabelValues(status, r.Method).Inc()
 | 
							labels := map[string]string{"code": status, "method": r.Method}
 | 
				
			||||||
 | 
							// Update requests counter metric
 | 
				
			||||||
 | 
							metricRequestsTotalCounter.With(labels).Inc()
 | 
				
			||||||
 | 
							// Update request latency metric
 | 
				
			||||||
 | 
							elapsed := tock.Sub(tick)
 | 
				
			||||||
 | 
							metricRequestsLatencyNanoSeconds.With(labels).Observe(elapsed.Seconds())
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user