github.com/maeglindeveloper/gqlgen@v0.13.1-0.20210413081235-57808b12a0a0/graphql/handler/apollotracing/tracer.go (about) 1 package apollotracing 2 3 import ( 4 "context" 5 "sync" 6 "time" 7 8 "github.com/99designs/gqlgen/graphql" 9 "github.com/vektah/gqlparser/v2/ast" 10 ) 11 12 type ( 13 Tracer struct{} 14 15 TracingExtension struct { 16 mu sync.Mutex 17 Version int `json:"version"` 18 StartTime time.Time `json:"startTime"` 19 EndTime time.Time `json:"endTime"` 20 Duration time.Duration `json:"duration"` 21 Parsing Span `json:"parsing"` 22 Validation Span `json:"validation"` 23 Execution struct { 24 Resolvers []*ResolverExecution `json:"resolvers"` 25 } `json:"execution"` 26 } 27 28 Span struct { 29 StartOffset time.Duration `json:"startOffset"` 30 Duration time.Duration `json:"duration"` 31 } 32 33 ResolverExecution struct { 34 Path ast.Path `json:"path"` 35 ParentType string `json:"parentType"` 36 FieldName string `json:"fieldName"` 37 ReturnType string `json:"returnType"` 38 StartOffset time.Duration `json:"startOffset"` 39 Duration time.Duration `json:"duration"` 40 } 41 ) 42 43 var _ interface { 44 graphql.HandlerExtension 45 graphql.ResponseInterceptor 46 graphql.FieldInterceptor 47 } = Tracer{} 48 49 func (Tracer) ExtensionName() string { 50 return "ApolloTracing" 51 } 52 53 func (Tracer) Validate(graphql.ExecutableSchema) error { 54 return nil 55 } 56 57 func (Tracer) InterceptField(ctx context.Context, next graphql.Resolver) (interface{}, error) { 58 td, ok := graphql.GetExtension(ctx, "tracing").(*TracingExtension) 59 if !ok { 60 return next(ctx) 61 } 62 63 start := graphql.Now() 64 65 defer func() { 66 end := graphql.Now() 67 68 rc := graphql.GetOperationContext(ctx) 69 fc := graphql.GetFieldContext(ctx) 70 resolver := &ResolverExecution{ 71 Path: fc.Path(), 72 ParentType: fc.Object, 73 FieldName: fc.Field.Name, 74 ReturnType: fc.Field.Definition.Type.String(), 75 StartOffset: start.Sub(rc.Stats.OperationStart), 76 Duration: end.Sub(start), 77 } 78 79 td.mu.Lock() 80 td.Execution.Resolvers = append(td.Execution.Resolvers, resolver) 81 td.mu.Unlock() 82 }() 83 84 return next(ctx) 85 } 86 87 func (Tracer) InterceptResponse(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { 88 rc := graphql.GetOperationContext(ctx) 89 90 start := rc.Stats.OperationStart 91 92 td := &TracingExtension{ 93 Version: 1, 94 StartTime: start, 95 Parsing: Span{ 96 StartOffset: rc.Stats.Parsing.Start.Sub(start), 97 Duration: rc.Stats.Parsing.End.Sub(rc.Stats.Parsing.Start), 98 }, 99 100 Validation: Span{ 101 StartOffset: rc.Stats.Validation.Start.Sub(start), 102 Duration: rc.Stats.Validation.End.Sub(rc.Stats.Validation.Start), 103 }, 104 } 105 106 graphql.RegisterExtension(ctx, "tracing", td) 107 resp := next(ctx) 108 109 end := graphql.Now() 110 td.EndTime = end 111 td.Duration = end.Sub(start) 112 113 return resp 114 }