// Like StartSpan, but begins a root span for a new trace if no trace is active // in the supplied context and tracing is enabled for the process. func Trace( parent context.Context, desc string) (ctx context.Context, report ReportFunc) { // If tracing is disabled, this is a no-op. if !*fEnabled { ctx = parent report = func(err error) {} return } // Is this context already being traced? If so, simply add a span. if parent.Value(traceStateKey) != nil { ctx, report = StartSpan(parent, desc) return } // Set up a new trace state. ts := new(traceState) baseReport := ts.CreateSpan(desc) // Log when finished. report = func(err error) { baseReport(err) ts.Log() } // Set up the context. ctx = context.WithValue(parent, traceStateKey, ts) return }
// Begin a span within the current trace. Return a new context that should be // used for operations that logically occur within the span, and a report // function that must be called with the outcome of the logical operation // represented by the span. // // If no trace is active, no span will be created but ctx and report will still // be valid. func StartSpan( parent context.Context, desc string) (ctx context.Context, report ReportFunc) { // Look for the trace state. val := parent.Value(traceStateKey) if val == nil { // Nothing to do. ctx = parent report = func(err error) {} return } ts := val.(*traceState) // Set up the report function. report = ts.CreateSpan(desc) // For now we don't do anything interesting with the context. In the future, // we may use it to record span hierarchy. ctx = parent return }
// Do sends an HTTP request with the provided http.Client and returns an HTTP response. // If the client is nil, http.DefaultClient is used. // If the context is canceled or times out, ctx.Err() will be returned. func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) { if client == nil { client = http.DefaultClient } // Request cancelation changed in Go 1.5, see cancelreq.go and cancelreq_go14.go. cancel := canceler(client, req) type responseAndError struct { resp *http.Response err error } result := make(chan responseAndError, 1) go func() { resp, err := client.Do(req) testHookDoReturned() result <- responseAndError{resp, err} }() var resp *http.Response select { case <-ctx.Done(): testHookContextDoneBeforeHeaders() cancel() // Clean up after the goroutine calling client.Do: go func() { if r := <-result; r.resp != nil { testHookDidBodyClose() r.resp.Body.Close() } }() return nil, ctx.Err() case r := <-result: var err error resp, err = r.resp, r.err if err != nil { return resp, err } } c := make(chan struct{}) go func() { select { case <-ctx.Done(): cancel() case <-c: // The response's Body is closed. } }() resp.Body = ¬ifyingReader{resp.Body, c} return resp, nil }