diff --git a/app.go b/app.go index 889d5a8..88d5f84 100644 --- a/app.go +++ b/app.go @@ -11,6 +11,7 @@ import ( "net/url" "os" "sort" + "strconv" "strings" "text/template" "time" @@ -20,8 +21,10 @@ import ( "golang.org/x/net/context" "github.com/Luzifer/badge-gen/cache" + "github.com/Luzifer/go_helpers/accessLogger" "github.com/Luzifer/rconfig" "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus" "github.com/tdewolff/minify" "github.com/tdewolff/minify/svg" ) @@ -117,6 +120,7 @@ func main() { r := mux.NewRouter() r.HandleFunc("/v1/badge", generateBadge).Methods("GET") r.HandleFunc("/{service}/{parameters:.*}", generateServiceBadge).Methods("GET") + r.Handle("/metrics", prometheus.Handler()) r.HandleFunc("/", handleDemoPage) http.ListenAndServe(cfg.Listen, r) @@ -127,6 +131,9 @@ func generateServiceBadge(res http.ResponseWriter, r *http.Request) { service := vars["service"] params := strings.Split(vars["parameters"], "/") + al := accessLogger.New(res) + start := time.Now() + ctx, cancel := context.WithTimeout(context.Background(), 1500*time.Millisecond) defer cancel() @@ -142,7 +149,17 @@ func generateServiceBadge(res http.ResponseWriter, r *http.Request) { return } - renderBadgeToResponse(res, r, title, text, color) + renderBadgeToResponse(al, r, title, text, color) + + duration := float64(time.Since(start)) / float64(time.Microsecond) + + requestCount.WithLabelValues( + service, + strings.ToLower(r.Method), + strconv.FormatInt(int64(al.StatusCode), 10), + ).Inc() + responseSize.WithLabelValues(service).Observe(float64(al.Size)) + requestDuration.WithLabelValues(service).Observe(duration) } func generateBadge(res http.ResponseWriter, r *http.Request) { diff --git a/metrics.go b/metrics.go new file mode 100644 index 0000000..d5fe097 --- /dev/null +++ b/metrics.go @@ -0,0 +1,38 @@ +package main + +import "github.com/prometheus/client_golang/prometheus" + +var ( + requestCount *prometheus.CounterVec + requestDuration *prometheus.SummaryVec + responseSize *prometheus.SummaryVec +) + +func init() { + initMetrics() +} + +func initMetrics() { + so := prometheus.SummaryOpts{ + Subsystem: "badge_gen", + } + + reqCnt := prometheus.NewCounterVec(prometheus.CounterOpts{ + Subsystem: so.Subsystem, + Name: "requests_total", + Help: "Total number of HTTP requests made.", + ConstLabels: so.ConstLabels, + }, []string{"handler", "method", "code"}) + + so.Name = "response_size_bytes" + so.Help = "The HTTP response sizes in bytes." + resSz := prometheus.NewSummaryVec(so, []string{"handler"}) + + so.Name = "request_duration_microseconds" + so.Help = "The HTTP request latencies in microseconds." + reqDur := prometheus.NewSummaryVec(so, []string{"handler"}) + + requestCount = prometheus.MustRegisterOrGet(reqCnt).(*prometheus.CounterVec) + requestDuration = prometheus.MustRegisterOrGet(reqDur).(*prometheus.SummaryVec) + responseSize = prometheus.MustRegisterOrGet(resSz).(*prometheus.SummaryVec) +} diff --git a/service_github.go b/service_github.go index 52fb2fb..d539d81 100644 --- a/service_github.go +++ b/service_github.go @@ -5,15 +5,26 @@ import ( "errors" "net/http" "regexp" + "strconv" "strings" "time" + "github.com/prometheus/client_golang/prometheus" + "golang.org/x/net/context" "golang.org/x/net/context/ctxhttp" ) +var ( + githubRemainingLimit prometheus.Gauge +) + func init() { registerServiceHandler("github", githubServiceHandler{}) + githubRemainingLimit = prometheus.MustRegisterOrGet(prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "github_remaining_limit", + Help: "Remaining requests in GitHub rate limit until next reset", + })).(prometheus.Gauge) } type githubRelease struct { @@ -275,5 +286,12 @@ func (g githubServiceHandler) fetchAPI(ctx context.Context, path string, headers } defer resp.Body.Close() + if rlr := resp.Header.Get("X-RateLimit-Remaining"); rlr != "" { + v, err := strconv.ParseInt(rlr, 10, 64) + if err == nil { + githubRemainingLimit.Set(float64(v)) + } + } + return json.NewDecoder(resp.Body).Decode(out) }