Adds implementation
This commit is contained in:
parent
ae63d6ce89
commit
d4e3679e76
101
metrics.go
Normal file
101
metrics.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Metrics provides a simple way to track latency and http status
|
||||||
|
type Metrics struct {
|
||||||
|
latency *prometheus.SummaryVec
|
||||||
|
status *prometheus.CounterVec
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(prefix string) (*Metrics, error) {
|
||||||
|
m := &Metrics{
|
||||||
|
latency: prometheus.NewSummaryVec(prometheus.SummaryOpts{
|
||||||
|
Namespace: prefix,
|
||||||
|
Subsystem: "http",
|
||||||
|
Name: "request_latency_ms",
|
||||||
|
Help: "Latency in ms of http requests grouped by req path",
|
||||||
|
}, []string{"path"}),
|
||||||
|
|
||||||
|
status: prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||||
|
Namespace: prefix,
|
||||||
|
Subsystem: "http",
|
||||||
|
Name: "status_count",
|
||||||
|
Help: "The count of http responses issued classified by status and api endpoint",
|
||||||
|
}, []string{"path", "code"}),
|
||||||
|
}
|
||||||
|
if err := m.registerPromMetrics(); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "registration")
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerPromMetrics registers all the metrics that eventmanger uses.
|
||||||
|
func (m *Metrics) registerPromMetrics() error {
|
||||||
|
if err := prometheus.Register(m.latency); err != nil {
|
||||||
|
return errors.Wrap(err, "http request latency")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := prometheus.Register(m.status); err != nil {
|
||||||
|
return errors.Wrap(err, "http response counter")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap calls a http.Handler and tracks status code and latency.
|
||||||
|
func (m *Metrics) Wrap(prefix string, h http.Handler) http.HandlerFunc {
|
||||||
|
return m.WrapFunc(prefix, h.ServeHTTP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapFunc calls a http.Handler and tracks status code and latency.
|
||||||
|
func (m *Metrics) WrapFunc(prefix string, h http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
start := time.Now()
|
||||||
|
defer func() {
|
||||||
|
m.HTTPLatency(prefix, start)
|
||||||
|
}()
|
||||||
|
|
||||||
|
lw := NewStatusRecorder(w)
|
||||||
|
h(lw, req)
|
||||||
|
|
||||||
|
m.HTTPStatus(prefix, lw.Status())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPLatency records a request latency for a given url path.
|
||||||
|
func (m *Metrics) HTTPLatency(path string, start time.Time) {
|
||||||
|
m.latency.WithLabelValues(path).Observe(msSince(start))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPStatus records counts of http statuses, bucketed according to status types.
|
||||||
|
func (m *Metrics) HTTPStatus(path string, status int) {
|
||||||
|
m.status.WithLabelValues(path, fmt.Sprintf("%d", bucketHTTPStatus(status))).Inc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// bucketHTTPStatus rounds down to the nearest hundred to facilitate categorizing http statuses.
|
||||||
|
func bucketHTTPStatus(i int) int {
|
||||||
|
return i - i%100
|
||||||
|
}
|
||||||
|
|
||||||
|
// msSince returns milliseconds since start.
|
||||||
|
func msSince(start time.Time) float64 {
|
||||||
|
return float64(time.Since(start)) / float64(time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buckets returns the default prometheus buckets scaled to milliseconds.
|
||||||
|
func buckets() []float64 {
|
||||||
|
r := []float64{}
|
||||||
|
|
||||||
|
for _, v := range prometheus.DefBuckets {
|
||||||
|
r = append(r, v*float64(time.Second/time.Millisecond))
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
29
resp.go
Normal file
29
resp.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StatusRecorder is a simple http status recorder
|
||||||
|
type StatusRecorder struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
|
||||||
|
status int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStatusRecorder returns an initialized StatusRecorder, with 200 as the
|
||||||
|
// default status.
|
||||||
|
func NewStatusRecorder(w http.ResponseWriter) *StatusRecorder {
|
||||||
|
return &StatusRecorder{ResponseWriter: w, status: http.StatusOK}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status returns the cached http status value.
|
||||||
|
func (sr *StatusRecorder) Status() int {
|
||||||
|
return sr.status
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteHeader caches the status, then calls the underlying ResponseWriter.
|
||||||
|
func (sr *StatusRecorder) WriteHeader(status int) {
|
||||||
|
sr.status = status
|
||||||
|
sr.ResponseWriter.WriteHeader(status)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user