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