Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return context.Context from FieldMiddleware #327

Closed
wants to merge 3 commits into from
Closed

Conversation

vvakame
Copy link
Collaborator

@vvakame vvakame commented Sep 1, 2018

I'm trying to use OpenCensus for query tracing.
Problem is gqlgen can't propagate tracing span to next resolver.
We can organize resolver tracing to tree structure after this PR merged.

I think same issue has in OpenTracing at here

example stack with this PR

I have:

  • Added tests covering the bug / feature (see testing)
  • Updated any relevant documentation (see docs)
package gqlopencensus

import (
	"context"
	"fmt"

	"github.com/99designs/gqlgen/graphql"
	"go.opencensus.io/trace"
	"encoding/json"
)

// ResolverMiddleware add tracing for resolver.
func ResolverMiddleware() graphql.FieldMiddleware {
	return func(ctx context.Context, next graphql.Resolver) (context.Context, interface{}, error) {
		rctx := graphql.GetResolverContext(ctx)
		ctx, span := trace.StartSpan(ctx, rctx.Object+"."+rctx.Field.Name)
		defer span.End()

		span.AddAttributes(
			trace.StringAttribute("resolver.object", rctx.Object),
			trace.StringAttribute("resolver.field", rctx.Field.Name),
			trace.StringAttribute("resolver.path", fmt.Sprintf("%+v", rctx.Path())),
		)

		res, err := next(ctx)
		return ctx, res, err
	}
}

// RequestMiddleware add tracing for request.
func RequestMiddleware() graphql.RequestMiddleware {
	return func(ctx context.Context, next func(ctx context.Context) []byte) []byte {
		requestContext := graphql.GetRequestContext(ctx)
		requestName := "nameless-operation"
		if len(requestContext.Doc.Operations) != 0 {
			op := requestContext.Doc.Operations[0]
			if op.Name != "" {
				requestName = op.Name
			}
		}

		ctx, span := trace.StartSpan(ctx, requestName)
		defer span.End()

		var variables string
		b, err := json.Marshal(requestContext.Variables)
		if err != nil {
			variables = "can't marshal to JSON"
		} else {
			variables = string(b)
		}

		span.AddAttributes(
			trace.StringAttribute("request.query", requestContext.RawQuery),
			trace.StringAttribute("request.variables", variables),
		)

		return next(ctx)
	}
}

@vvakame vvakame requested a review from vektah September 1, 2018 05:03
Copy link
Collaborator

@vektah vektah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets try and keep API breaks off master until we are ready to shop the next major release, maybe bring back the next branch?

@mathewbyrne have any thoughts?

n := next
next = func(ctx context.Context) (interface{}, error) {
return ec.directives.{{$directive.Name|ucFirst}}({{$directive.CallArgs}})
res := func() interface{} {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this required?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed to return values 1 to 2. but generated code will return return graphql.Null.
this immidiately execute function is wrapping it.

@@ -38,7 +38,7 @@
Field: field,
}
ctx = graphql.WithResolverContext(ctx, rctx)
resTmp := ec.FieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(ctx context.Context) (interface{}, error) {
ctx, resTmp := ec.FieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(ctx context.Context) (interface{}, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its probably preferable to update ctx rather than shadowing it and disabling a bunch of linter checks?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sees that there is not much value for the this hard work... 🤔

func DefaultResolverMiddleware(ctx context.Context, next Resolver) (res interface{}, err error) {
return next(ctx)
func DefaultResolverMiddleware(ctx context.Context, next Resolver) (context.Context, interface{}, error) {
res, err := next(ctx)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this interface mismatch imply resolvers cant change their childs context either? Should we be thinking a little broader than just directives?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is an interesting point.
Does resolvers and directives need to modify context?
For Resolver, I can not make an idea that use case.
For directive, It doesn't have tree structure, because it doesn't need to modify context. I think directive middleware may be good.

@vektah vektah added the BC break label Sep 3, 2018
@vektah vektah changed the base branch from master to next September 3, 2018 00:47
@vvakame
Copy link
Collaborator Author

vvakame commented Sep 13, 2018

👀

@vetcher
Copy link
Contributor

vetcher commented Sep 24, 2018

May be, somehow, we can add second type for middleware? One with context and the second without?

@vektah vektah mentioned this pull request Sep 25, 2018
2 tasks
@mathewbyrne
Copy link
Contributor

Closing this as I think it's been addressed in #353. @vvakame feel free to reopen if you think the other change hasn't quite covered your use-case.

@mathewbyrne mathewbyrne closed this Oct 2, 2018
@vvakame vvakame deleted the feat-middleware-ctx branch October 15, 2018 07:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants