-
Notifications
You must be signed in to change notification settings - Fork 438
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
contrib/graph-gophers/graphql-go: add graphql tracing support #287
Conversation
) | ||
|
||
const ( | ||
graphqlFieldTag = "graphql.field" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use the tag*
prefix here:
const (
tagGraphqlField = "graphql.field"
tagGraphqlQuery = "graphql.query"
tagGraphqlType = "graphql.type"
)
I find this is more Go idiomatic, we can see this in the wild too, like in the http
package constants.
return ctx, func(err *errors.QueryError) { | ||
// this is necessary otherwise the span gets marked as an error | ||
if err != nil { | ||
span.Finish(tracer.WithError(err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can leave only this line. The error will not be applied if it is nil
(see here). Have you experienced otherwise?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's weird: https://play.golang.org/p/8JiXCGldIxi
type e struct{}
func (*e) Error() string { return "this works" }
func main() {
var err error = (*e)(nil)
fmt.Println(err, err == nil, err.Error())
}
It happens because an interface nil
is different from a pointer nil
. In this example calling the Error
method actually still works though, and I guess that's why it's allowed.
I don't know if there's a better way to handle this upstream. We could do it with reflection, but that's probably not worth the computational overhead.
I can update the comment to make it more clear why its necessary if that would be helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. This could even cause us problems if people have the same expectations as me. It could result in unexpected errors where there are in fact none. There should be an issue on https://github.com/golang/go about this if there is not. I understand how this behavior is correct but can be misleading for errors. Possibly something for Go 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed its confusing. There's a FAQ entry and a proposal golang/go#22729
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Maybe we can mention this issue in the comment:
// must explicitly check for nil, see issue golang/go#22729
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the FAQ this looks like a beginner mistake, so I'm not sure if there is anything to do here at all, other than perhaps not make people think that it's ok to pass "nil" errors to tracer.WithError
.
} | ||
|
||
// New creates a new Tracer. | ||
func New(opts ...Option) trace.Tracer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if it wouldn't be better to call this NewTracer
. The package is used as graphqltrace
which is an action, so in the context of graphqltrace.New
might indicate an action too, not a constructor. Maybe graphqltrace.NewTracer
would be better and more in line with the graphql.Tracer
function. Not feeling too strongly on it though.
type Option func(*config) | ||
|
||
func defaults(cfg *config) { | ||
cfg.serviceName = "graphql.server" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is something outside the scope of this task, but I'm just letting you know that we have #273, which indicates that this is in some cases unexpected behavior. For example, if someone starts the tracer using:
tracer.Start(tracer.WithServiceName("my-service"))
And then later instruments a service, that should be the default service name. One solution could be to add an extra internal
package at the root level to share configuration settings between the tracer package and integrations. It should be thread-safe.
type Query { | ||
hello: String! | ||
} | ||
` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be nicer to have tracer := graphqltrace.NewTracer()
? Just so we can isolate this for the sake of the example.
) | ||
|
||
return ctx, func(errs []*errors.QueryError) { | ||
var err error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could simplify this, one idea is:
switch n := len(errs); n {
case 0:
// err = nil
case 1:
err = errs[0]
default:
err = fmt.Errorf("%s (and %d more errors)", errs[0], n-1)
}
The fmt
package formatter %s
will correctly call the error
interface's Error
method to obtain the string.
Thanks. I'm not sure I like that proposal. There's a lot to consider for
sure.
…On Tue, Jul 31, 2018 at 2:47 PM Caleb Doxsey ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In contrib/graph-gophers/graphql-go/graphql.go
<#287 (comment)>:
> + }
+ span.Finish(tracer.WithError(err))
+ }
+}
+
+// TraceField traces a GraphQL field access.
+func (t *Tracer) TraceField(ctx context.Context, label string, typeName string, fieldName string, trivial bool, args map[string]interface{}) (context.Context, trace.TraceFieldFinishFunc) {
+ span, ctx := tracer.StartSpanFromContext(ctx, "graphql.field",
+ tracer.ServiceName(t.cfg.serviceName),
+ tracer.Tag(graphqlFieldTag, fieldName),
+ tracer.Tag(graphqlTypeTag, typeName),
+ )
+ return ctx, func(err *errors.QueryError) {
+ // this is necessary otherwise the span gets marked as an error
+ if err != nil {
+ span.Finish(tracer.WithError(err))
Indeed its confusing. There's a FAQ entry and a proposal golang/go#22729
<golang/go#22729>
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#287 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AGYGlF-oTYCccUe9JXmKh8DfXsdjdsXXks5uMEPNgaJpZM4VnHTe>
.
|
@gbbr thanks for the review. I made the changes you requested. For the default service name, it's a little tricky, because we set a service name by default based on the application name. I think we need some way to distinguish between a user having set the service name and us providing the default. Maybe a Anyway that's probably best tackled in another PR since all the existing integrations will need to be updated in some way. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right about the service name. My suggestion would also technically cause a breaking change. Let's keep it in #273
) | ||
|
||
// A Tracer implements the graphql-go/trace.Tracer interface by sending traces | ||
// to the Datadog tracer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we do the ole var _ Tracer = (*trace.Tracer)(nil)
package-level constant to assert and show that this interface is implemented? Any thoughts on that? I've been using it everywhere in this package - unsure if good or bad.
@@ -0,0 +1,17 @@ | |||
package graphql // import "gopkg.in/DataDog/dd-trace-go.v1/contrib/graph-gophers/graphql-go" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot about the package level documentation. Can you please add that and give it a shot on godoc
? It might be more intuitive to use the file graphql.go
. We might want to move the canonical import path comment there too as it might be the file where people will look intuitively to find these headers.
…ing handling for TraceQuery, update comment on nil error check, rename Tracer to NewTracer
…package level documentation
Fixes: #286
This adds GraphQL server tracing support.