...

Source file src/github.com/99designs/gqlgen/graphql/handler/apollotracing/tracer.go

Documentation: github.com/99designs/gqlgen/graphql/handler/apollotracing

     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  }
   120  

View as plain text