Skip to content

Commit

Permalink
Implement Metrics middleware via Prometheus
Browse files Browse the repository at this point in the history
Metrics is a middleware for logging duration of RPC requests via Prometheus.
It exposes two metrics: appName_rpc_error_requests_count and appName_rpc_responses_duration_seconds.
  • Loading branch information
sergeyfast committed Jul 6, 2017
1 parent 0d49aac commit cf5e44f
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 8 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,14 @@ All comments are optional.

# JSON-RPC 2.0 Supported Features

* [ ] Requests
* [x] Requests
* [x] Single requests
* [x] Batch requests
* [x] Notifications
* [ ] Parameters
* [x] Named
* [ ] Position
* [ ] Default values
* [x] Default values
* [ ] SMD Schema
* [ ] Input
* [ ] Output
Expand All @@ -120,15 +120,15 @@ All comments are optional.

# Server Library Features

* [ ] go generate
* [x] go generate
* [ ] Transports
* [x] HTTP
* [ ] Websocket
* [ ] RabbitMQ
* [ ] Server middleware
* [ ] Basic support
* [ ] Metrics
* [ ] Logging
* [x] Server middleware
* [x] Basic support
* [x] Metrics
* [x] Logging

# License

Expand Down
47 changes: 47 additions & 0 deletions middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package zenrpc
import (
"context"
"encoding/json"
"github.com/prometheus/client_golang/prometheus"
"log"
"strconv"
"time"
)

Expand All @@ -24,3 +26,48 @@ func Logger(l *log.Logger) MiddlewareFunc {
}
}
}

// Metrics is a middleware for logging duration of RPC requests via Prometheus. Default AppName is zenrpc.
// It exposes two metrics: appName_rpc_error_requests_count and appName_rpc_responses_duration_seconds.
func Metrics(appName string) MiddlewareFunc {
if appName == "" {
appName = "zenrpc"
}

rpcErrors := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: appName,
Subsystem: "rpc",
Name: "error_requests_count",
Help: "Error requests count by method and error code.",
}, []string{"method", "code"})

rpcDurations := prometheus.NewSummaryVec(prometheus.SummaryOpts{
Namespace: appName,
Subsystem: "rpc",
Name: "responses_duration_seconds",
Help: "Response time by method and error code.",
}, []string{"method", "code"})

prometheus.MustRegister(rpcErrors, rpcDurations)

return func(h InvokeFunc) InvokeFunc {
return func(ctx context.Context, method string, params json.RawMessage) Response {
start, code := time.Now(), ""
r := h(ctx, method, params)

// log metrics
if n := NamespaceFromContext(ctx); n != "" {
method = n + "." + method
}

if r.Error != nil {
code = strconv.Itoa(r.Error.Code)
rpcErrors.WithLabelValues(method, code).Inc()
}

rpcDurations.WithLabelValues(method, code).Observe(time.Since(start).Seconds())

return r
}
}
}
5 changes: 4 additions & 1 deletion testdata/arithsrv/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"flag"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/sergeyfast/zenrpc"
"github.com/sergeyfast/zenrpc/testdata"
"log"
Expand All @@ -17,9 +18,11 @@ func main() {
rpc.Register("arith", testdata.ArithService{})
rpc.Register("", testdata.ArithService{}) // public
rpc.Use(zenrpc.Logger(log.New(os.Stderr, "", log.LstdFlags)))
rpc.Use(zenrpc.Metrics(""))

http.Handle("/", rpc)
http.Handle("/metrics", promhttp.Handler())

log.Printf("starting arithsrv on %s", *addr)
log.Fatal(http.ListenAndServe(*addr, nil))
}
}

0 comments on commit cf5e44f

Please sign in to comment.