Add instrumentation with prometheus

This commit is contained in:
Las Zenow 2017-06-05 16:17:14 +00:00
parent 2a72154308
commit f4ca9e2dbc
6 changed files with 138 additions and 32 deletions

14
lib/instrument/dummy.go Normal file
View file

@ -0,0 +1,14 @@
// +build noprometheus
package instrument
import "time"
type dummyInst struct{}
func Init() Instrument {
return &dummyInst{}
}
func (i dummyInst) Visit(section string, id string, search string, fmt string) {}
func (i dummyInst) Duration(section string, duration time.Duration) {}

View file

@ -0,0 +1,8 @@
package instrument
import "time"
type Instrument interface {
Visit(section string, id string, search string, fmt string)
Duration(section string, duration time.Duration)
}

View file

@ -0,0 +1,66 @@
// +build !noprometheus
package instrument
import (
"time"
log "github.com/cihub/seelog"
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
const (
promAddr = ":9123"
)
type promInst struct {
visits *prometheus.CounterVec
reqDur *prometheus.HistogramVec
}
func Init() Instrument {
go promHandle()
visits := prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "trantor_visits_total",
Help: "Number of visits.",
},
[]string{"section", "id", "search", "fmt"},
)
reqDur := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "trantor_request_duration_seconds",
Help: "Duration of the request in seconds.",
},
[]string{"section"},
)
prometheus.MustRegister(visits)
prometheus.MustRegister(reqDur)
return &promInst{
visits: visits,
reqDur: reqDur,
}
}
func promHandle() {
server := http.Server{
Addr: promAddr,
Handler: promhttp.Handler(),
}
log.Error(server.ListenAndServe())
}
func (in promInst) Visit(section string, id string, search string, fmt string) {
in.visits.WithLabelValues(section, id, search, fmt).Inc()
}
func (in promInst) Duration(section string, duration time.Duration) {
in.reqDur.WithLabelValues(section).Observe(duration.Seconds())
}

View file

@ -5,14 +5,15 @@ import (
"net/http"
"strings"
"time"
"github.com/gorilla/mux"
"gitlab.com/trantor/trantor/lib/database"
"gitlab.com/trantor/trantor/lib/instrument"
"gitlab.com/trantor/trantor/lib/storage"
)
const (
stats_version = 2
statsChanSize = 100
)
@ -28,22 +29,26 @@ type handler struct {
}
type StatsGatherer struct {
db database.DB
store storage.Store
template *Template
hostname string
channel chan statsRequest
ro bool
db database.DB
store storage.Store
template *Template
instrument instrument.Instrument
hostname string
channel chan statsRequest
ro bool
}
func InitStats(database database.DB, store storage.Store, hostname string, template *Template, ro bool) *StatsGatherer {
in := instrument.Init()
sg := StatsGatherer{
channel: make(chan statsRequest, statsChanSize),
db: database,
store: store,
template: template,
hostname: hostname,
ro: ro,
channel: make(chan statsRequest, statsChanSize),
db: database,
store: store,
instrument: in,
template: template,
hostname: hostname,
ro: ro,
}
go sg.worker()
@ -65,33 +70,42 @@ func (sg StatsGatherer) Gather(function func(handler)) func(http.ResponseWriter,
ro: sg.ro,
}
t0 := time.Now()
function(h)
sg.channel <- statsRequest{r}
t1 := time.Now()
sg.channel <- statsRequest{r, t1.Sub(t0)}
}
}
type statsRequest struct {
r *http.Request
r *http.Request
duration time.Duration
}
func (sg StatsGatherer) worker() {
for req := range sg.channel {
var err error
pattern := strings.Split(req.r.URL.Path, "/")
id := mux.Vars(req.r)["id"]
if len(pattern) > 1 && pattern[1] != "" && id != "" {
switch pattern[1] {
case "download":
id := mux.Vars(req.r)["id"]
err = sg.db.IncDownloads(id)
case "book":
id := mux.Vars(req.r)["id"]
err = sg.db.IncViews(id)
case "read":
id := mux.Vars(req.r)["id"]
err = sg.db.IncViews(id)
}
search := strings.Join(req.r.Form["q"], " ")
fmt := req.r.FormValue("fmt")
pattern := strings.Split(req.r.URL.Path, "/")
section := "/"
if len(pattern) > 1 && pattern[1] != "" {
section = pattern[1]
}
sg.instrument.Visit(section, id, search, fmt)
sg.instrument.Duration(section, req.duration*time.Microsecond)
switch section {
case "download":
err = sg.db.IncDownloads(id)
case "book":
err = sg.db.IncViews(id)
case "read":
err = sg.db.IncViews(id)
}
if err != nil {
log.Warn("Problem incrementing visits: ", err)
}

View file

@ -152,7 +152,7 @@ func UpdateLogger(loggerConfig string) error {
return log.ReplaceLogger(logger)
}
func InitRouter(db database.DB, sg *StatsGatherer, assetsPath string) {
func InitRouter(db database.DB, sg *StatsGatherer, assetsPath string) http.Handler {
const idPattern = "[0-9a-zA-Z\\-\\_]{16}"
r := mux.NewRouter()
@ -203,7 +203,7 @@ func InitRouter(db database.DB, sg *StatsGatherer, assetsPath string) {
r.HandleFunc("/news/edit", sg.Gather(editNewsHandler)).Methods("GET")
r.HandleFunc("/news/edit", sg.Gather(postNewsHandler)).Methods("POST")
http.Handle("/", r)
return r
}
func fileServer(servePath string, prefix string) func(w http.ResponseWriter, r *http.Request) {

View file

@ -62,6 +62,10 @@ func main() {
trantor.InitUpload(db, store)
trantor.InitTasks(db, *loggerConfig)
trantor.InitRouter(db, sg, *assetsPath)
log.Error(http.ListenAndServe(*httpAddr, nil))
router := trantor.InitRouter(db, sg, *assetsPath)
server := http.Server{
Addr: *httpAddr,
Handler: router,
}
log.Error(server.ListenAndServe())
}