diff --git a/handlers/about.go b/handlers/about.go index 3145e4a..2240b0c 100644 --- a/handlers/about.go +++ b/handlers/about.go @@ -1,15 +1,12 @@ package handlers import ( - "log" "net/http" "text/template" ) // AboutHandler handles the about page func AboutHandler(w http.ResponseWriter, r *http.Request) { - log.Println("Serving about page") - t := template.Must(template.New("about.html").Delims("[[", "]]").ParseFiles("templates/about.html", "templates/footer.html")) t.Execute(w, map[string]interface{}{ "google_analytics_key": googleAnalyticsKey, diff --git a/handlers/assets.go b/handlers/assets.go index 34c1a63..fcb0089 100644 --- a/handlers/assets.go +++ b/handlers/assets.go @@ -1,13 +1,11 @@ package handlers import ( - "log" "net/http" ) // AssetsHandler handles serving static files func AssetsHandler(w http.ResponseWriter, r *http.Request) { - log.Println("Serving " + r.URL.Path[1:]) w.Header().Set("Cache-Control", "max-age=86400") http.ServeFile(w, r, r.URL.Path[1:]) @@ -15,6 +13,5 @@ func AssetsHandler(w http.ResponseWriter, r *http.Request) { // FaviconHandler handles serving the favicon.ico func FaviconHandler(w http.ResponseWriter, r *http.Request) { - log.Println("Serving " + r.URL.Path[1:]) http.ServeFile(w, r, "assets/favicon.ico") } diff --git a/handlers/home.go b/handlers/home.go index a156cda..5845ea1 100644 --- a/handlers/home.go +++ b/handlers/home.go @@ -13,8 +13,6 @@ import ( // HomeHandler handles the homepage func HomeHandler(w http.ResponseWriter, r *http.Request) { - log.Println("Serving home page") - if r.URL.Path[1:] == "" { db, err := bolt.Open(DBPath, 0755, &bolt.Options{Timeout: 1 * time.Second}) if err != nil { diff --git a/main.go b/main.go index a638ae2..0bebfc3 100644 --- a/main.go +++ b/main.go @@ -12,11 +12,15 @@ import ( "github.com/gojp/goreportcard/handlers" "github.com/boltdb/bolt" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" ) var ( - addr = flag.String("http", ":8000", "HTTP listen address") - dev = flag.Bool("dev", false, "dev mode") + addr = flag.String("http", ":8000", "HTTP listen address") + metricsAddr = flag.String("metrics", ":8001", "The address to listen on for metric-related HTTP requests.") + + dev = flag.Bool("dev", false, "dev mode") ) func makeHandler(name string, dev bool, fn func(http.ResponseWriter, *http.Request, string, bool)) http.HandlerFunc { @@ -76,6 +80,42 @@ func initDB() error { return err } +// metrics provides functionality for monitoring the application status +type metrics struct { + responseTimes *prometheus.SummaryVec +} + +// setupMetrics creates custom Prometheus metrics for monitoring +// application statistics. +func setupMetrics() *metrics { + m := &metrics{} + m.responseTimes = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Name: "response_time_ms", + Help: "Time to serve requests", + }, + []string{"endpoint"}, + ) + + prometheus.MustRegister(m.responseTimes) + return m +} + +// recordDuration records the length of a request from start to now +func (m metrics) recordDuration(start time.Time, name string) { + elapsed := time.Since(start) + m.responseTimes.WithLabelValues(name).Observe(float64(elapsed.Nanoseconds())) + log.Printf("Served %s in %s", name, elapsed) +} + +// instrument adds metric instrumentation to the handler passed in as argument +func (m metrics) instrument(path string, h http.HandlerFunc) (string, http.HandlerFunc) { + return path, func(w http.ResponseWriter, r *http.Request) { + defer m.recordDuration(time.Now(), path) + h.ServeHTTP(w, r) + } +} + func main() { flag.Parse() if err := os.MkdirAll("_repos/src/github.com", 0755); err != nil && !os.IsExist(err) { @@ -87,14 +127,18 @@ func main() { log.Fatal("ERROR: could not open bolt db: ", err) } - http.HandleFunc("/assets/", handlers.AssetsHandler) - http.HandleFunc("/favicon.ico", handlers.FaviconHandler) - http.HandleFunc("/checks", handlers.CheckHandler) - http.HandleFunc("/report/", makeHandler("report", *dev, handlers.ReportHandler)) - http.HandleFunc("/badge/", makeHandler("badge", *dev, handlers.BadgeHandler)) - http.HandleFunc("/high_scores/", handlers.HighScoresHandler) - http.HandleFunc("/about/", handlers.AboutHandler) - http.HandleFunc("/", handlers.HomeHandler) + m := setupMetrics() + + http.HandleFunc(m.instrument("/assets/", handlers.AssetsHandler)) + http.HandleFunc(m.instrument("/favicon.ico", handlers.FaviconHandler)) + http.HandleFunc(m.instrument("/checks", handlers.CheckHandler)) + http.HandleFunc(m.instrument("/report/", makeHandler("report", *dev, handlers.ReportHandler))) + http.HandleFunc(m.instrument("/badge/", makeHandler("badge", *dev, handlers.BadgeHandler))) + http.HandleFunc(m.instrument("/high_scores/", handlers.HighScoresHandler)) + http.HandleFunc(m.instrument("/about/", handlers.AboutHandler)) + http.HandleFunc(m.instrument("/", handlers.HomeHandler)) + + http.Handle("/metrics", promhttp.Handler()) log.Printf("Running on %s ...", *addr) log.Fatal(http.ListenAndServe(*addr, nil)) diff --git a/vendor/github.com/prometheus/client_golang b/vendor/github.com/prometheus/client_golang new file mode 160000 index 0000000..353b8c3 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang @@ -0,0 +1 @@ +Subproject commit 353b8c3f3776541879f9abfd8fa8b1ae162ab394 diff --git a/vendor/github.com/prometheus/client_model b/vendor/github.com/prometheus/client_model new file mode 160000 index 0000000..6f38060 --- /dev/null +++ b/vendor/github.com/prometheus/client_model @@ -0,0 +1 @@ +Subproject commit 6f3806018612930941127f2a7c6c453ba2c527d2 diff --git a/vendor/github.com/prometheus/common b/vendor/github.com/prometheus/common new file mode 160000 index 0000000..2f17f4a --- /dev/null +++ b/vendor/github.com/prometheus/common @@ -0,0 +1 @@ +Subproject commit 2f17f4a9d485bf34b4bfaccc273805040e4f86c8 diff --git a/vendor/github.com/prometheus/procfs b/vendor/github.com/prometheus/procfs new file mode 160000 index 0000000..e645f4e --- /dev/null +++ b/vendor/github.com/prometheus/procfs @@ -0,0 +1 @@ +Subproject commit e645f4e5aaa8506fc71d6edbc5c4ff02c04c46f2