Add request_duration_seconds metric #72
81
metrics.go
81
metrics.go
@ -14,42 +14,37 @@ 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 throughout the application.
|
||||||
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 {
|
// metricURLsTotalGauge measures the amount of pastes stored in the database,
|
||||||
var metric float64
|
// partitioned by type and state.
|
||||||
if err := database.Transaction(func(tx *db.Database) error {
|
//
|
||||||
var count int64
|
// Its values are computed on the fly by updateMetrics().
|
||||||
if err := database.Model(&db.Paste{}).Count(&count).Error; err != nil {
|
var metricURLsTotalGauge = prometheus.NewGaugeVec(
|
||||||
return err
|
prometheus.GaugeOpts{
|
||||||
}
|
Namespace: metricNamespace,
|
||||||
metric = float64(count)
|
Subsystem: "pastes",
|
||||||
return nil
|
Name: "urls_total",
|
||||||
}); err != nil {
|
Help: "The current amount of pastes in the database, partitioned by state and type.",
|
||||||
log.Printf("error: %v", errors.Wrap(err, "fetching pastes_total metric"))
|
}, []string{"state", "type"})
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return metric
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(metricURLsTotalGauge)
|
||||||
prometheus.MustRegister(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
|
|
||||||
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,
|
||||||
mrngm marked this conversation as resolved
Outdated
|
|||||||
@ -58,3 +53,31 @@ 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
|
||||||
|
for state := db.PasteStateUndef; state <= db.PasteStateDeleted; state++ {
|
||||||
|
for ty := db.PasteTypeUndef; ty <= db.PasteTypeFileUpload; ty++ {
|
||||||
|
var count int64
|
||||||
|
query := mh.db.Unscoped().Model(&db.Paste{}).Where("type = ? AND state = ?", ty, state).Count(&count)
|
||||||
|
if err := query.Error; err != nil {
|
||||||
|
log.Printf("error: %v", errors.Wrap(err, "fetching pastes_total metric"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
labels := map[string]string{
|
||||||
|
"state": state.String(),
|
||||||
|
"type": ty.String(),
|
||||||
|
}
|
||||||
|
metricURLsTotalGauge.With(labels).Set(float64(count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -60,8 +60,11 @@ 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) {
|
||||||
srw := statusResponseWriter{Inner: w}
|
srw := statusResponseWriter{Inner: w}
|
||||||
next.ServeHTTP(&srw, r)
|
next.ServeHTTP(&srw, r)
|
||||||
|
|
||||||
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()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user
(technically, the PR is for
request_duration_seconds
, so anything other than that should be omitted... or change the PR title and description)