github.com/instana/go-sensor@v1.62.2-0.20240520081010-4919868049e1/docs/roundtripper.md (about)

     1  ## Tracing HTTP Outgoing Requests
     2  
     3  The tracer is able to collect data from the Go standard library for outgoing HTTP requests.
     4  That is, when one creates an [http.Client](https://pkg.go.dev/net/http@go1.21.3#Client) instance to make calls to HTTP servers.
     5  
     6  To achieve this data collection we provide a [RoundTripper](https://pkg.go.dev/net/http@go1.21.3#RoundTripper) wrapper to be used as the http.Client Transport.
     7  Additionally, as HTTP outgoing requests are exit spans, the HTTP request must be attached to a context containing a entry span.
     8  
     9  ### Usage
    10  
    11  Let's assume the following code snippet as an example to be traced:
    12  
    13  ```go
    14  client := &http.Client{}
    15  
    16  req, err := http.NewRequest(http.MethodGet, "https://www.instana.com", nil)
    17  
    18  _, err = client.Do(req)
    19  
    20  ```
    21  
    22  The first thing we need is to add the collector to the project:
    23  
    24  ```go
    25  col := instana.InitCollector(&instana.Options{
    26    Service: "my-http-client",
    27  })
    28  ```
    29  
    30  Then, we need to wrap the current Transport (if any) with `instana.RoundTripper`.
    31  If no Transport is provided, simply pass `nil` as the second argument.
    32  The `instana.RoundTripper` wrapper will intercept the relevant information from the HTTP request, such as URL, methods and so on. It will then collect them and send to the Agent periodically.
    33  
    34  ```go
    35  // Wrap the original http.Client transport with instana.RoundTripper().
    36  // The http.DefaultTransport will be used if there was no transport provided.
    37  client := &http.Client{
    38    Transport: instana.RoundTripper(col, nil),
    39  }
    40  ```
    41  
    42  Usually, your application will have an entry span already, received via HTTP or other type of incoming request.
    43  This span should be contained into the context, which needs to be passed ahead to your client HTTP request.
    44  Also, make sure to finish the span in order to send it to the Agent.
    45  
    46  ```go
    47  // Inject the parent span into request context
    48  ctx := instana.ContextWithSpan(context.Background(), entrySpan)
    49  
    50  // Use your instrumented http.Client to propagate tracing context with the request
    51  _, err = client.Do(req.WithContext(ctx))
    52  ```
    53  
    54  If you do not have an entry span as explained above or you are not sure, it's possible to manually create an entry span and attach it to a context to be passed forward to your HTTP client:
    55  
    56  ```go
    57  // Every call should start with an entry span:
    58  // https://www.ibm.com/docs/en/instana-observability/current?topic=tracing-best-practices#start-new-traces-with-entry-spans
    59  // Normally this would be your HTTP/GRPC/message queue request span, but here we need to create it explicitly,
    60  // since an HTTP client call is an exit span. And all exit spans must have a parent entry span.
    61  entrySpan := col.Tracer().StartSpan("client-call")
    62  entrySpan.SetTag(string(ext.SpanKind), "entry")
    63  
    64  ...
    65  
    66  // Inject the parent span into request context
    67  ctx := instana.ContextWithSpan(context.Background(), entrySpan)
    68  
    69  // Use your instrumented http.Client to propagate tracing context with the request
    70  _, err = client.Do(req.WithContext(ctx))
    71  
    72  ...
    73  
    74  // Remember to always finish spans that were created manually to make sure it's propagated to the Agent.
    75  // In this case, we want to make sure that the entry span is finished after the HTTP request is completed.
    76  // Optionally, we could use defer right after the span is created.
    77  entrySpan.Finish()
    78  ```
    79  You can learn more about manually instrumenting your code [here]().
    80  
    81  #### Complete Example
    82  
    83  ```go
    84  package main
    85  
    86  import (
    87    "context"
    88    "log"
    89    "net/http"
    90  
    91    instana "github.com/instana/go-sensor"
    92    "github.com/opentracing/opentracing-go/ext"
    93  )
    94  
    95  func main() {
    96    col := instana.InitCollector(&instana.Options{
    97      Service: "my-http-client",
    98    })
    99  
   100    client := &http.Client{
   101      Transport: instana.RoundTripper(col, nil),
   102    }
   103  
   104    entrySpan := col.Tracer().StartSpan("client-call")
   105    entrySpan.SetTag(string(ext.SpanKind), "entry")
   106  
   107    req, err := http.NewRequest(http.MethodGet, "https://www.instana.com", nil)
   108    if err != nil {
   109      log.Fatalf("failed to create request: %s", err)
   110    }
   111  
   112    ctx := instana.ContextWithSpan(context.Background(), entrySpan)
   113  
   114    _, err = client.Do(req.WithContext(ctx))
   115    if err != nil {
   116      log.Fatalf("failed to GET https://www.instana.com: %s", err)
   117    }
   118  
   119    entrySpan.Finish()
   120  }
   121  ```
   122  
   123  -----
   124  [README](../README.md) |
   125  [Tracer Options](options.md) |
   126  [Tracing SQL Driver Databases](sql.md) |
   127  [Tracing Other Go Packages](other_packages.md) |
   128  [Instrumenting Code Manually](manual_instrumentation.md)