diff --git a/gqlgen/go.mod b/gqlgen/go.mod index 1ad9e8b..dd44a3d 100644 --- a/gqlgen/go.mod +++ b/gqlgen/go.mod @@ -4,5 +4,7 @@ go 1.15 require ( github.com/99designs/gqlgen v0.13.0 - github.com/graphmetrics/graphmetrics-go v0.2.3 + github.com/graphmetrics/graphmetrics-go v0.3.0 + github.com/graphmetrics/logger-go v0.2.1 + github.com/vektah/gqlparser/v2 v2.1.0 ) diff --git a/gqlgen/go.sum b/gqlgen/go.sum index 2190c63..57e77f6 100644 --- a/gqlgen/go.sum +++ b/gqlgen/go.sum @@ -2,14 +2,17 @@ github.com/99designs/gqlgen v0.13.0 h1:haLTcUp3Vwp80xMVEg5KRNwzfUrgFdRmtBY8fuB8s github.com/99designs/gqlgen v0.13.0/go.mod h1:NV130r6f4tpRWuAI+zsrSdooO/eWUv+Gyyoi3rEfXIk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0= github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMNtWtNvI9mIV6isjEb9lBMNv+77IGM= github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -18,10 +21,10 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graphmetrics/graphmetrics-go v0.2.3 h1:xQv1P2YMEausj52YpYNpXK7otoaH5ZauUYRs1HXnaNU= -github.com/graphmetrics/graphmetrics-go v0.2.3/go.mod h1:1LcstWZiHfrlCL3Usquk9vdYw6Z0Lptrnydq2KXecRY= -github.com/graphmetrics/logger-go v0.2.0 h1:vzxwoe3wtvXHqh7urQ4MYtrOjlwDGDxEOsmEy9NH+o8= -github.com/graphmetrics/logger-go v0.2.0/go.mod h1:T98PXH1RF/nRghhhnKr8S7N96H5A7beuXXwzJaBl0dw= +github.com/graphmetrics/graphmetrics-go v0.3.0 h1:MdzpN7VYkw3/frjjbJV5IguCUzc3WKiSa7j9RcVpTHE= +github.com/graphmetrics/graphmetrics-go v0.3.0/go.mod h1:rG6Yyqfux5SbrZIHzyHLoFR+d3utjsSThxY/VwC+QtU= +github.com/graphmetrics/logger-go v0.2.1 h1:7XBJKij+sY+b7ENi35xVk1UJzMGtj2wEoIeLAlo8cok= +github.com/graphmetrics/logger-go v0.2.1/go.mod h1:T98PXH1RF/nRghhhnKr8S7N96H5A7beuXXwzJaBl0dw= github.com/graphmetrics/sketches-go v0.2.0 h1:VVh4GE3rXlmiTa0jJN/MJgn2AIl/qV7fbUge0h8X9as= github.com/graphmetrics/sketches-go v0.2.0/go.mod h1:dQhQn7YYW04ysDLao9hlgtZmrC5PRvbyxrGUg57+I3o= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= diff --git a/gqlgen/handler.go b/gqlgen/handler.go index c4cb3f5..3f512c9 100644 --- a/gqlgen/handler.go +++ b/gqlgen/handler.go @@ -5,8 +5,12 @@ import ( "time" "github.com/99designs/gqlgen/graphql" + "github.com/graphmetrics/logger-go" + "github.com/vektah/gqlparser/v2/ast" + "github.com/graphmetrics/graphmetrics-go" "github.com/graphmetrics/graphmetrics-go/client" + "github.com/graphmetrics/graphmetrics-go/signature" ) type Extension interface { @@ -18,54 +22,81 @@ type Extension interface { } func NewExtension(cfg *graphmetrics.Configuration) Extension { - clientExt := cfg.ClientExtractor - if clientExt == nil { - clientExt = func(context.Context) client.Details { - return client.Details{} - } - } agg := graphmetrics.NewAggregator(cfg) go agg.Start() return &extensionImpl{ - agg: agg, - clientExt: clientExt, + aggregator: agg, + clientExtractor: cfg.GetClientExtractor(), + logger: cfg.GetLogger(), } } type extensionImpl struct { - agg *graphmetrics.Aggregator - clientExt client.Extractor + aggregator *graphmetrics.Aggregator + clientExtractor client.Extractor + schema *ast.Schema + + logger logger.Logger } func (*extensionImpl) ExtensionName() string { return "GraphMetricsExtension" } -func (*extensionImpl) Validate(_ graphql.ExecutableSchema) error { +func (e *extensionImpl) Validate(schema graphql.ExecutableSchema) error { + e.schema = schema.Schema() return nil } func (e *extensionImpl) InterceptOperation(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler { - // NOT IMPLEMENTED YET - return next(ctx) + operation := graphql.GetOperationContext(ctx) + caller := e.clientExtractor(ctx) + sign, err := signature.OperationSignature(e.schema, operation.RawQuery, operation.OperationName) + if err != nil { + e.logger.Error("unable to build operation signature", map[string]interface{}{ + "err": err, + "operation": operation.OperationName, + }) + } + hash := signature.OperationHash(sign) + + handler := next(ctx) + return func(ctx context.Context) *graphql.Response { + res := handler(ctx) + duration := time.Since(operation.Stats.OperationStart) + e.aggregator.PushOperation(&graphmetrics.OperationMessage{ + Name: operation.OperationName, + Type: string(operation.Operation.Operation), + Hash: hash, + Signature: sign, + HasErrors: len(res.Errors) > 0, + Duration: duration, + Client: caller, + }) + + return res + } } func (e *extensionImpl) InterceptField(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { - field := graphql.GetFieldContext(ctx) start := time.Now() + + field := graphql.GetFieldContext(ctx) + caller := e.clientExtractor(ctx) res, err = next(ctx) duration := time.Since(start) - e.agg.PushField(&graphmetrics.FieldMessage{ + e.aggregator.PushField(&graphmetrics.FieldMessage{ TypeName: field.Object, FieldName: field.Field.Name, ReturnType: field.Field.Definition.Type.String(), Error: err, Duration: duration, - Client: e.clientExt(ctx), + Client: caller, }) + return res, err } func (e *extensionImpl) Close() error { - return e.agg.Stop() + return e.aggregator.Stop() }