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  }