package rushlink import ( "log" "net/http" "time" "gitea.hashru.nl/dsprenkels/rushlink/internal/db" "github.com/gorilla/mux" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) const metricNamespace = "rushlink" // metricURLsTotalGauge counts the number of requests that are handled by // the application, partitioned by status code and HTTP method. // // This counter is updated by throughout the application. var metricRequestsTotalCounter = prometheus.NewCounterVec( 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"}) // 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 func StartMetricsServer(addr string, database *db.Database, fs *db.FileStore) { prometheus.MustRegister(metricRequestsTotalCounter) prometheus.MustRegister(metricURLsTotalGauge) router := mux.NewRouter() router.Handle("/metrics", &MetricsHandler{database}).Methods("GET") srv := &http.Server{ Handler: router, Addr: addr, WriteTimeout: 15 * time.Second, ReadTimeout: 15 * time.Second, } 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)) } } }