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