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

ddtrace/tracer: add WithHTTPClient and increase default HTTP timeout #636

Merged
merged 2 commits into from
Apr 16, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions ddtrace/tracer/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ type config struct {
// propagator propagates span context cross-process
propagator Propagator

// httpRoundTripper defines the http.RoundTripper used by the agent transport.
httpRoundTripper http.RoundTripper
// httpClient specifies the HTTP client to be used by the agent's transport.
httpClient *http.Client

// hostname is automatically assigned when the DD_TRACE_REPORT_HOSTNAME is set to true,
// and is added as a special tag to the root span of traces.
Expand Down Expand Up @@ -243,12 +243,18 @@ func WithSampler(s Sampler) StartOption {
}
}

// WithHTTPRoundTripper allows customizing the underlying HTTP transport for
// emitting spans. This is useful for advanced customization such as emitting
// spans to a unix domain socket. The default should be used in most cases.
// WithHTTPRoundTripper is deprecated. Please consider using WithHTTPClient instead.
gbbr marked this conversation as resolved.
Show resolved Hide resolved
func WithHTTPRoundTripper(r http.RoundTripper) StartOption {
return WithHTTPClient(&http.Client{
Transport: r,
Timeout: defaultHTTPTimeout,
})
}

// WithHTTPClient specifies the HTTP client to use when emitting spans to the agent.
func WithHTTPClient(client *http.Client) StartOption {
return func(c *config) {
c.httpRoundTripper = r
c.httpClient = client
}
}

Expand Down
2 changes: 1 addition & 1 deletion ddtrace/tracer/option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestTracerOptionsDefaults(t *testing.T) {
assert.Equal("tracer.test", c.serviceName)
assert.Equal("localhost:8126", c.agentAddr)
assert.Equal("localhost:8125", c.dogstatsdAddr)
assert.Equal(nil, c.httpRoundTripper)
assert.Nil(nil, c.httpClient)
})

t.Run("analytics", func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/tracer/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func newUnstartedTracer(opts ...StartOption) *tracer {
fn(c)
}
if c.transport == nil {
c.transport = newTransport(c.agentAddr, c.httpRoundTripper)
c.transport = newTransport(c.agentAddr, c.httpClient)
}
if c.propagator == nil {
c.propagator = NewPropagator(nil)
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/tracer/tracer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ func TestTracerPrioritySampler(t *testing.T) {
addr := srv.Listener.Addr().String()

tr, _, flush, stop := startTestTracer(t,
withTransport(newHTTPTransport(addr, defaultRoundTripper)),
withTransport(newHTTPTransport(addr, defaultClient)),
)
defer stop()

Expand Down
30 changes: 14 additions & 16 deletions ddtrace/tracer/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import (
"gopkg.in/DataDog/dd-trace-go.v1/internal/version"
)

var (
var defaultClient = &http.Client{
// We copy the transport to avoid using the default one, as it might be
// augmented with tracing and we don't want these calls to be recorded.
// See https://golang.org/pkg/net/http/#DefaultTransport .
defaultRoundTripper = &http.Transport{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
Expand All @@ -36,14 +36,15 @@ var (
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
)
},
Timeout: defaultHTTPTimeout,
}

const (
defaultHostname = "localhost"
defaultPort = "8126"
defaultAddress = defaultHostname + ":" + defaultPort
defaultHTTPTimeout = time.Second // defines the current timeout before giving up with the send process
defaultHTTPTimeout = 2 * time.Second // defines the current timeout before giving up with the send process
traceCountHeader = "X-Datadog-Trace-Count" // header containing the number of traces in the payload
)

Expand All @@ -64,16 +65,16 @@ type transport interface {
// running on a non-default port, if it's located on another machine, or when
// otherwise needing to customize the transport layer, for instance when using
// a unix domain socket.
func newTransport(addr string, roundTripper http.RoundTripper) transport {
if roundTripper == nil {
roundTripper = defaultRoundTripper
func newTransport(addr string, client *http.Client) transport {
if client == nil {
client = defaultClient
}
return newHTTPTransport(addr, roundTripper)
return newHTTPTransport(addr, client)
}

// newDefaultTransport return a default transport for this tracing client
func newDefaultTransport() transport {
return newHTTPTransport(defaultAddress, defaultRoundTripper)
return newHTTPTransport(defaultAddress, defaultClient)
}

type httpTransport struct {
Expand All @@ -83,7 +84,7 @@ type httpTransport struct {
}

// newHTTPTransport returns an httpTransport for the given endpoint
func newHTTPTransport(addr string, roundTripper http.RoundTripper) *httpTransport {
func newHTTPTransport(addr string, client *http.Client) *httpTransport {
// initialize the default EncoderPool with Encoder headers
defaultHeaders := map[string]string{
"Datadog-Meta-Lang": "go",
Expand All @@ -101,11 +102,8 @@ func newHTTPTransport(addr string, roundTripper http.RoundTripper) *httpTranspor
}
return &httpTransport{
traceURL: fmt.Sprintf("http://%s/v0.4/traces", resolveAddr(addr)),
client: &http.Client{
Transport: roundTripper,
Timeout: defaultHTTPTimeout,
},
headers: defaultHeaders,
client: client,
headers: defaultHeaders,
}
}

Expand Down
28 changes: 23 additions & 5 deletions ddtrace/tracer/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func TestTracesAgentIntegration(t *testing.T) {
}

for _, tc := range testCases {
transport := newHTTPTransport(defaultAddress, defaultRoundTripper)
transport := newHTTPTransport(defaultAddress, defaultClient)
p, err := encode(tc.payload)
assert.NoError(err)
_, err = transport.send(p)
Expand Down Expand Up @@ -196,7 +196,7 @@ func TestTransportResponse(t *testing.T) {
}))
defer ln.Close()
addr := ln.Addr().String()
transport := newHTTPTransport(addr, defaultRoundTripper)
transport := newHTTPTransport(addr, defaultClient)
rc, err := transport.send(newPayload())
if tt.err != "" {
assert.Equal(tt.err, err.Error())
Expand Down Expand Up @@ -230,7 +230,7 @@ func TestTraceCountHeader(t *testing.T) {
assert.Nil(err)
assert.NotEmpty(port, "port should be given, as it's chosen randomly")
for _, tc := range testCases {
transport := newHTTPTransport(host, defaultRoundTripper)
transport := newHTTPTransport(host, defaultClient)
p, err := encode(tc.payload)
assert.NoError(err)
_, err = transport.send(p)
Expand All @@ -246,7 +246,7 @@ type recordingRoundTripper struct {

func (r *recordingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
r.reqs = append(r.reqs, req)
return defaultRoundTripper.RoundTrip(req)
return defaultClient.Transport.RoundTrip(req)
}

func TestCustomTransport(t *testing.T) {
Expand All @@ -263,7 +263,7 @@ func TestCustomTransport(t *testing.T) {
assert.NotEmpty(port, "port should be given, as it's chosen randomly")

customRoundTripper := new(recordingRoundTripper)
transport := newHTTPTransport(host, customRoundTripper)
transport := newHTTPTransport(host, &http.Client{Transport: customRoundTripper})
p, err := encode(getTestTrace(1, 1))
assert.NoError(err)
_, err = transport.send(p)
Expand All @@ -273,6 +273,24 @@ func TestCustomTransport(t *testing.T) {
assert.Len(customRoundTripper.reqs, 1)
}

func TestWithHTTPClient(t *testing.T) {
assert := assert.New(t)
srv := mockDatadogAPINewServer(t)
defer srv.Close()

u, err := url.Parse(srv.URL)
assert.NoError(err)
rt := new(recordingRoundTripper)
trc := newTracer(WithAgentAddr(u.Host), WithHTTPClient(&http.Client{Transport: rt}))
defer trc.Stop()

p, err := encode(getTestTrace(1, 1))
assert.NoError(err)
_, err = trc.config.transport.send(p)
assert.NoError(err)
assert.Len(rt.reqs, 1)
}

// TestTransportHTTPRace defines a regression tests where the request body was being
// read even after http.Client.Do returns. See golang/go#33244
func TestTransportHTTPRace(t *testing.T) {
Expand Down