Пример #1
0
// 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
}
Пример #2
0
// 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
}
Пример #3
0
// 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 = &notifyingReader{resp.Body, c}

	return resp, nil
}