package http import ( "errors" "fmt" "net/http" "os" "strconv" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) const defaultResourceName = "http.request" type roundTripper struct { base http.RoundTripper cfg *roundTripperConfig } func (rt *roundTripper) RoundTrip(req *http.Request) (res *http.Response, err error) { span, _ := tracer.StartSpanFromContext(req.Context(), "http.request", tracer.SpanType(ext.SpanTypeHTTP), tracer.ResourceName(defaultResourceName), tracer.Tag(ext.HTTPMethod, req.Method), tracer.Tag(ext.HTTPURL, req.URL.Path), ) defer func() { if rt.cfg.after != nil { rt.cfg.after(res, span) } span.Finish(tracer.WithError(err)) }() if rt.cfg.before != nil { rt.cfg.before(req, span) } // inject the span context into the http request err = tracer.Inject(span.Context(), tracer.HTTPHeadersCarrier(req.Header)) if err != nil { // this should never happen fmt.Fprintf(os.Stderr, "failed to inject http headers for round tripper: %v\n", err) } res, err = rt.base.RoundTrip(req) if err != nil { span.SetTag("http.errors", err.Error()) } else { span.SetTag(ext.HTTPCode, strconv.Itoa(res.StatusCode)) // treat 5XX as errors if res.StatusCode/100 == 5 { span.SetTag("http.errors", res.Status) err = errors.New(res.Status) } } return res, err } // WrapRoundTripper returns a new RoundTripper which traces all requests sent // over the transport. func WrapRoundTripper(rt http.RoundTripper, opts ...RoundTripperOption) http.RoundTripper { cfg := new(roundTripperConfig) for _, opt := range opts { opt(cfg) } return &roundTripper{ base: rt, cfg: cfg, } }