github.com/weaveworks/common@v0.0.0-20230728070032-dd9e68f319d5/http/client/client.go (about)

     1  package client
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"strconv"
     8  
     9  	"github.com/prometheus/client_golang/prometheus"
    10  
    11  	"github.com/weaveworks/common/instrument"
    12  )
    13  
    14  // Requester executes an HTTP request.
    15  type Requester interface {
    16  	Do(req *http.Request) (*http.Response, error)
    17  }
    18  
    19  // TimedClient instruments a request. It implements Requester.
    20  type TimedClient struct {
    21  	client    Requester
    22  	collector instrument.Collector
    23  }
    24  
    25  type contextKey int
    26  
    27  // OperationNameContextKey specifies the operation name location within the context
    28  // for instrumentation.
    29  const OperationNameContextKey contextKey = 0
    30  
    31  // NewTimedClient creates a Requester that instruments requests on `client`.
    32  func NewTimedClient(client Requester, collector instrument.Collector) *TimedClient {
    33  	return &TimedClient{
    34  		client:    client,
    35  		collector: collector,
    36  	}
    37  }
    38  
    39  // Do executes the request.
    40  func (c TimedClient) Do(r *http.Request) (*http.Response, error) {
    41  	return TimeRequest(r.Context(), c.operationName(r), c.collector, c.client, r)
    42  }
    43  
    44  func (c TimedClient) operationName(r *http.Request) string {
    45  	operation, _ := r.Context().Value(OperationNameContextKey).(string)
    46  	if operation == "" {
    47  		operation = r.URL.Path
    48  	}
    49  	return operation
    50  }
    51  
    52  // TimeRequest performs an HTTP client request and records the duration in a histogram.
    53  func TimeRequest(ctx context.Context, operation string, coll instrument.Collector, client Requester, request *http.Request) (*http.Response, error) {
    54  	var response *http.Response
    55  	doRequest := func(_ context.Context) error {
    56  		var err error
    57  		response, err = client.Do(request)
    58  		return err
    59  	}
    60  	toStatusCode := func(err error) string {
    61  		if err == nil {
    62  			return strconv.Itoa(response.StatusCode)
    63  		}
    64  		return "error"
    65  	}
    66  	err := instrument.CollectedRequest(ctx, fmt.Sprintf("%s %s", request.Method, operation),
    67  		coll, toStatusCode, doRequest)
    68  	return response, err
    69  }
    70  
    71  // TimeRequestHistogram performs an HTTP client request and records the duration in a histogram.
    72  // Deprecated: try to use TimeRequest() to avoid creation of a collector on every request
    73  func TimeRequestHistogram(ctx context.Context, operation string, metric *prometheus.HistogramVec, client Requester, request *http.Request) (*http.Response, error) {
    74  	coll := instrument.NewHistogramCollector(metric)
    75  	return TimeRequest(ctx, operation, coll, client, request)
    76  }