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  }