Added Prometheus metrics, don't forget to restrict access on /metrics endpoint.
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
package instrumentation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
prometheusclient "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
existStatusFailure = "failure"
|
||||
exitStatusSuccess = "success"
|
||||
)
|
||||
|
||||
var (
|
||||
requestStartedCounter prometheusclient.Counter
|
||||
requestCompletedCounter prometheusclient.Counter
|
||||
resolverStartedCounter *prometheusclient.CounterVec
|
||||
resolverCompletedCounter *prometheusclient.CounterVec
|
||||
timeToResolveField *prometheusclient.HistogramVec
|
||||
timeToHandleRequest *prometheusclient.HistogramVec
|
||||
)
|
||||
|
||||
// Register registers the prometheus metrics.
|
||||
func Register() {
|
||||
requestStartedCounter = prometheusclient.NewCounter(
|
||||
prometheusclient.CounterOpts{
|
||||
Name: "graphql_request_started_total",
|
||||
Help: "Total number of requests started on the graphql server.",
|
||||
},
|
||||
)
|
||||
|
||||
requestCompletedCounter = prometheusclient.NewCounter(
|
||||
prometheusclient.CounterOpts{
|
||||
Name: "graphql_request_completed_total",
|
||||
Help: "Total number of requests completed on the graphql server.",
|
||||
},
|
||||
)
|
||||
|
||||
resolverStartedCounter = prometheusclient.NewCounterVec(
|
||||
prometheusclient.CounterOpts{
|
||||
Name: "graphql_resolver_started_total",
|
||||
Help: "Total number of resolver started on the graphql server.",
|
||||
},
|
||||
[]string{"object", "field"},
|
||||
)
|
||||
|
||||
resolverCompletedCounter = prometheusclient.NewCounterVec(
|
||||
prometheusclient.CounterOpts{
|
||||
Name: "graphql_resolver_completed_total",
|
||||
Help: "Total number of resolver completed on the graphql server.",
|
||||
},
|
||||
[]string{"object", "field"},
|
||||
)
|
||||
|
||||
timeToResolveField = prometheusclient.NewHistogramVec(prometheusclient.HistogramOpts{
|
||||
Name: "graphql_resolver_duration_ms",
|
||||
Help: "The time taken to resolve a field by graphql server.",
|
||||
Buckets: prometheusclient.ExponentialBuckets(1, 2, 11),
|
||||
}, []string{"exitStatus"})
|
||||
|
||||
timeToHandleRequest = prometheusclient.NewHistogramVec(prometheusclient.HistogramOpts{
|
||||
Name: "graphql_request_duration_ms",
|
||||
Help: "The time taken to handle a request by graphql server.",
|
||||
Buckets: prometheusclient.ExponentialBuckets(1, 2, 11),
|
||||
}, []string{"exitStatus"})
|
||||
|
||||
prometheusclient.MustRegister(
|
||||
requestStartedCounter,
|
||||
requestCompletedCounter,
|
||||
resolverStartedCounter,
|
||||
resolverCompletedCounter,
|
||||
timeToResolveField,
|
||||
timeToHandleRequest,
|
||||
)
|
||||
}
|
||||
|
||||
// UnRegister unregisters
|
||||
func UnRegister() {
|
||||
prometheusclient.Unregister(requestStartedCounter)
|
||||
prometheusclient.Unregister(requestCompletedCounter)
|
||||
prometheusclient.Unregister(resolverStartedCounter)
|
||||
prometheusclient.Unregister(resolverCompletedCounter)
|
||||
prometheusclient.Unregister(timeToResolveField)
|
||||
prometheusclient.Unregister(timeToHandleRequest)
|
||||
}
|
||||
|
||||
// ResolverMiddleware is a resolver middleware that logs metrics for prometheus.
|
||||
func ResolverMiddleware() graphql.FieldMiddleware {
|
||||
return func(ctx context.Context, next graphql.Resolver) (interface{}, error) {
|
||||
rctx := graphql.GetResolverContext(ctx)
|
||||
|
||||
resolverStartedCounter.WithLabelValues(rctx.Object, rctx.Field.Name).Inc()
|
||||
|
||||
observerStart := time.Now()
|
||||
|
||||
res, err := next(ctx)
|
||||
|
||||
var exitStatus string
|
||||
if err != nil {
|
||||
exitStatus = existStatusFailure
|
||||
} else {
|
||||
exitStatus = exitStatusSuccess
|
||||
}
|
||||
|
||||
timeToResolveField.With(prometheusclient.Labels{"exitStatus": exitStatus}).
|
||||
Observe(float64(time.Since(observerStart).Nanoseconds() / int64(time.Millisecond)))
|
||||
|
||||
resolverCompletedCounter.WithLabelValues(rctx.Object, rctx.Field.Name).Inc()
|
||||
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
// RequestMiddleware is a request middleware that logs metrics for prometheus.
|
||||
func RequestMiddleware() graphql.RequestMiddleware {
|
||||
return func(ctx context.Context, next func(ctx context.Context) []byte) []byte {
|
||||
requestStartedCounter.Inc()
|
||||
|
||||
observerStart := time.Now()
|
||||
|
||||
res := next(ctx)
|
||||
|
||||
rctx := graphql.GetResolverContext(ctx)
|
||||
reqCtx := graphql.GetRequestContext(ctx)
|
||||
errList := reqCtx.GetErrors(rctx)
|
||||
|
||||
var exitStatus string
|
||||
if len(errList) > 0 {
|
||||
exitStatus = existStatusFailure
|
||||
} else {
|
||||
exitStatus = exitStatusSuccess
|
||||
}
|
||||
|
||||
timeToHandleRequest.With(prometheusclient.Labels{"exitStatus": exitStatus}).
|
||||
Observe(float64(time.Since(observerStart).Nanoseconds() / int64(time.Millisecond)))
|
||||
|
||||
requestCompletedCounter.Inc()
|
||||
|
||||
return res
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user