Skip to content
This repository has been archived by the owner on Jul 31, 2023. It is now read-only.

adds ClientTracer option to allow usage of httptrace package with ochttp #848

Merged
merged 3 commits into from
Aug 3, 2018

Conversation

basvanbeek
Copy link
Member

This adds the option to use the httptrace package with the ochttp RountTripper for more granular annotation of http requests.

This solves #309.

// ClientTrace implements the annotation of all available hooks for
// httptrace.ClientTrace.
type ClientTrace struct {
*trace.Span
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't use embedding here. There's no need to import the whole API surface of Span.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, this doesn't need to be exported does it?

Copy link
Member Author

Choose a reason for hiding this comment

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

We can lose the embedding but we should keep the export as it allows people to write their own func(*trace.Span) *httptrace.ClientTrace for selecting the set of hooks they want annotated by this ClientTrace.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand why this needs to be public. If they want to use only a subset of the hooks, they can simply set the corresponding fields to nil in the returned ClientTrace. Likewise, if they want to use their own ClientTrace and copy across some of the fields, they can do this too by calling methods on ClientTrace directly.

// ClientTracer may be set to a function allowing OpenCensus to annotate the
// current span with HTTP request event information emitted by the httptrace
// package.
ClientTracer func(*trace.Span) *httptrace.ClientTrace
Copy link
Contributor

Choose a reason for hiding this comment

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

Call it NewClientTrace, like sync.Pool's New.

Also might want to pass in the http.Request in case they want to specialize based on some attribute of the request.

Copy link
Member Author

Choose a reason for hiding this comment

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

ok

*trace.Span
}

// ClientTracer returns a httptrace.ClientTrace instance which instruments all
Copy link
Contributor

Choose a reason for hiding this comment

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

I would mention that it adds annotations specifically. Also the word "instance" is a little odd, why not just "returns a httptrace.ClientTrace"?

Second, I would also delegate to the contextual httptrace.ClientTrace (if present) so this doesn't break any other instrumentation they might already have.

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed the word instance as requested.

Not sure I follow the second remark; the clientTracer uses http.WithClientTrace() to attach itself. This function should take care of playing nice with other instrumentation if I read the description right.


// ClientTracer returns a httptrace.ClientTrace instance which instruments all
// emitted httptrace events on the provided Span.
func ClientTracer(s *trace.Span) *httptrace.ClientTrace {
Copy link
Contributor

Choose a reason for hiding this comment

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

The name here doesn't add much. Maybe NewSpanAnnotator?

Copy link
Member Author

Choose a reason for hiding this comment

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

ok

// ClientTracer may be set to a function allowing OpenCensus to annotate the
// current span with HTTP request event information emitted by the httptrace
// package.
ClientTracer func(*http.Request, *trace.Span) *httptrace.ClientTrace
Copy link
Contributor

Choose a reason for hiding this comment

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

My point with the name is that this is not the ClientTrace itself - it is a constructor function for the ClientTrace - so I think the name "NewClientTrace" is more appropriate.

Copy link
Member Author

Choose a reason for hiding this comment

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

NewClientTrace doesn't sit well with me as a property of the Transport struct but I'll admit I see your point on ClientTracer being not great either. I'll use NewClientTrace.

// ClientTrace implements the annotation of all available hooks for
// httptrace.ClientTrace.
type ClientTrace struct {
*trace.Span
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand why this needs to be public. If they want to use only a subset of the hooks, they can simply set the corresponding fields to nil in the returned ClientTrace. Likewise, if they want to use their own ClientTrace and copy across some of the fields, they can do this too by calling methods on ClientTrace directly.

Copy link
Contributor

@semistrict semistrict left a comment

Choose a reason for hiding this comment

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

I think we should call the field NewClientTrace instead of ClientTracer


// SpanAnnotator implements the annotation of all available hooks for
// httptrace.ClientTrace.
type SpanAnnotator struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand why this needs to be public. If they want to use only a subset of the hooks, they can simply set the corresponding fields to nil in the returned ClientTrace. Likewise, if they want to use their own ClientTrace and copy across some of the fields, they can do this too by calling methods on ClientTrace directly.

Copy link
Member Author

Choose a reason for hiding this comment

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

I guess it is a matter of opinion on what is the friendlier method for consumers.

Your preference is eliminating the hooks not desired:

func NewSpanAnnotatorWrapper(req *http.Request, s *trace.Span) *httptrace.ClientTrace {
    clientTrace := ochttp.NewSpanAnnotator(req, s)

    // deregister unwanted hooks
    clientTrace.GetConn = nil 
    clientTrace.Got100Continue = nil
    ...
    return clientTrace
}

...

transport := ochttp.Transport{
    NewClientTrace: NewSpanAnnotatorWrapper,
}

Mine is choosing the hooks that are:

func NewSpanAnnotator(req *http.Request, s *trace.Span) *httptrace.ClientTrace {
    spanAnnotator := ochttp.SpanAnnotator{s}

    // register wanted hooks
    return &httptrace.ClientTrace{
        GetConn:              spanAnnotator.PutIdleConn,
        GotFirstResponseByte: spanAnnotator.GotFirstResponseByte,
        DNSStart:             spanAnnotator.DNSStart,
        ...
    }
}

...

transport := ochttp.Transport{
    ClientTracer: NewSpanAnnotator,
}

Which reminds me why I wanted SpanAnnotator to embed *trace.Span or otherwise have it be an exported property. Bear in mind that the actual resulting *httptrace.ClientTrace or SpanAnnotator is created and contained within ochttp's traceTransport.RoundTrip().

Copy link
Contributor

@semistrict semistrict Aug 2, 2018

Choose a reason for hiding this comment

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

I still don't see why the type needs to be public. You could do this :

t := &ochttp.Transport {
    ClientTracer: func(r *http.Request, s *trace.Span) *httptrace.ClientTrace {
 ann := ochttp.NewSpanAnnotator(r,s)          
 return &httptrace.ClientTrace {
               GetConn: ann.GetConn
          }
    }
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Adding a type adds complexity and distraction to the godoc

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I seemed to have forgotten because the function outputs *httptrace.ClientTrace I can always wrap it with a new one. Good thing vacation is coming up for some fresh eyes.

Will not only unexport SpanAnnotator but all its methods can be unexported too.

@semistrict semistrict merged commit db4ae14 into census-instrumentation:master Aug 3, 2018
// NewClientTrace may be set to a function allowing the current *trace.Span
// to be annotated with HTTP request event information emitted by the
// httptrace package.
NewClientTrace func(*http.Request, *trace.Span) *httptrace.ClientTrace
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't often call fields after New* in Go. I'd rather wished this was named ClientTrace. Can we make this modification given there are no users of this yet?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not a huge deal if we are not doing it. I also don't think sync.Pool.New is well representative of the overall Go style in these cases.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do you have an example from the standard library of something that you think is representative? I couldn't think of anything except that.

I think it's fine to make changes since we haven't created a release yet.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants