mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Api metrics (#1014)
* api metrics support * comments reflected * metrics middleware fix
This commit is contained in:
@@ -14,8 +14,11 @@ import (
|
|||||||
"github.com/gin-contrib/cors"
|
"github.com/gin-contrib/cors"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"go.opencensus.io/stats"
|
||||||
"go.opencensus.io/tag"
|
"go.opencensus.io/tag"
|
||||||
"go.opencensus.io/trace"
|
"go.opencensus.io/trace"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func optionalCorsWrap(r *gin.Engine) {
|
func optionalCorsWrap(r *gin.Engine) {
|
||||||
@@ -75,6 +78,54 @@ func traceWrap(c *gin.Context) {
|
|||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apiMetricsWrap(s *Server) {
|
||||||
|
|
||||||
|
measure := func(engine *gin.Engine) func(*gin.Context) {
|
||||||
|
var routes gin.RoutesInfo
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
if routes == nil {
|
||||||
|
routes = engine.Routes()
|
||||||
|
}
|
||||||
|
start := time.Now()
|
||||||
|
// get the handler url, example: /v1/apps/:app
|
||||||
|
url := ""
|
||||||
|
for _, r := range routes {
|
||||||
|
if r.Handler == c.HandlerName() {
|
||||||
|
url = r.Path
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, err := tag.New(c.Request.Context(),
|
||||||
|
tag.Upsert(pathKey, url),
|
||||||
|
tag.Upsert(methodKey, c.Request.Method),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
stats.Record(ctx, apiRequestCount.M(1))
|
||||||
|
c.Next()
|
||||||
|
|
||||||
|
status := strconv.Itoa(c.Writer.Status())
|
||||||
|
ctx, err = tag.New(ctx,
|
||||||
|
tag.Upsert(statusKey, status),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
stats.Record(ctx, apiLatency.M(float64(time.Since(start))/float64(time.Millisecond)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r := s.Router
|
||||||
|
r.Use(measure(r))
|
||||||
|
if s.webListenPort != s.adminListenPort {
|
||||||
|
a := s.AdminRouter
|
||||||
|
a.Use(measure(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func panicWrap(c *gin.Context) {
|
func panicWrap(c *gin.Context) {
|
||||||
defer func(c *gin.Context) {
|
defer func(c *gin.Context) {
|
||||||
if rec := recover(); rec != nil {
|
if rec := recover(); rec != nil {
|
||||||
|
|||||||
@@ -537,6 +537,7 @@ func New(ctx context.Context, opts ...ServerOption) *Server {
|
|||||||
setMachineID()
|
setMachineID()
|
||||||
s.Router.Use(loggerWrap, traceWrap, panicWrap) // TODO should be opts
|
s.Router.Use(loggerWrap, traceWrap, panicWrap) // TODO should be opts
|
||||||
optionalCorsWrap(s.Router) // TODO should be an opt
|
optionalCorsWrap(s.Router) // TODO should be an opt
|
||||||
|
apiMetricsWrap(s)
|
||||||
s.bindHandlers(ctx)
|
s.bindHandlers(ctx)
|
||||||
|
|
||||||
s.appListeners = new(appListeners)
|
s.appListeners = new(appListeners)
|
||||||
@@ -572,6 +573,7 @@ func WithPrometheus() ServerOption {
|
|||||||
}
|
}
|
||||||
s.promExporter = exporter
|
s.promExporter = exporter
|
||||||
view.RegisterExporter(exporter)
|
view.RegisterExporter(exporter)
|
||||||
|
registerViews()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
68
api/server/stats.go
Normal file
68
api/server/stats.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"go.opencensus.io/stats"
|
||||||
|
"go.opencensus.io/stats/view"
|
||||||
|
"go.opencensus.io/tag"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
apiRequestCount = stats.Int64("api/request_count", "Number of API requests", stats.UnitDimensionless)
|
||||||
|
apiLatency = stats.Float64("api/latency", "API latency", stats.UnitMilliseconds)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pathKey = makeKey("path")
|
||||||
|
methodKey = makeKey("method")
|
||||||
|
statusKey = makeKey("status")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultLatencyDistribution = view.Distribution(0, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ApiRequestCountView = &view.View{
|
||||||
|
Name: "api/request_count",
|
||||||
|
Description: "Count of API requests started",
|
||||||
|
Measure: apiRequestCount,
|
||||||
|
TagKeys: []tag.Key{pathKey, methodKey},
|
||||||
|
Aggregation: view.Count(),
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiResponseCountView = &view.View{
|
||||||
|
Name: "api/response_count",
|
||||||
|
Description: "API response count",
|
||||||
|
TagKeys: []tag.Key{pathKey, methodKey, statusKey},
|
||||||
|
Measure: apiLatency,
|
||||||
|
Aggregation: view.Count(),
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiLatencyView = &view.View{
|
||||||
|
Name: "api/latency",
|
||||||
|
Description: "Latency distribution of API requests",
|
||||||
|
Measure: apiLatency,
|
||||||
|
TagKeys: []tag.Key{pathKey, methodKey, statusKey},
|
||||||
|
Aggregation: defaultLatencyDistribution,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerViews() {
|
||||||
|
err := view.Register(
|
||||||
|
ApiRequestCountView,
|
||||||
|
ApiResponseCountView,
|
||||||
|
ApiLatencyView,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Fatal("cannot register view")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeKey(name string) tag.Key {
|
||||||
|
key, err := tag.NewKey(name)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user