github.com/newrelic/go-agent@v3.26.0+incompatible/_integrations/nrb3/nrb3.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package nrb3 5 6 import ( 7 "net/http" 8 "time" 9 10 newrelic "github.com/newrelic/go-agent" 11 "github.com/newrelic/go-agent/internal" 12 ) 13 14 func init() { internal.TrackUsage("integration", "b3") } 15 16 // NewRoundTripper creates an `http.RoundTripper` to instrument external 17 // requests. The RoundTripper returned creates an external segment and adds B3 18 // tracing headers to each request if and only if a `newrelic.Transaction` 19 // (https://godoc.org/github.com/newrelic/go-agent#Transaction) is found in the 20 // `http.Request`'s context. It then delegates to the original RoundTripper 21 // provided (or http.DefaultTransport if none is provided). 22 func NewRoundTripper(original http.RoundTripper) http.RoundTripper { 23 if nil == original { 24 original = http.DefaultTransport 25 } 26 return &b3Transport{ 27 idGen: internal.NewTraceIDGenerator(int64(time.Now().UnixNano())), 28 original: original, 29 } 30 } 31 32 // cloneRequest mimics implementation of 33 // https://godoc.org/github.com/google/go-github/github#BasicAuthTransport.RoundTrip 34 func cloneRequest(r *http.Request) *http.Request { 35 // shallow copy of the struct 36 r2 := new(http.Request) 37 *r2 = *r 38 // deep copy of the Header 39 r2.Header = make(http.Header, len(r.Header)) 40 for k, s := range r.Header { 41 r2.Header[k] = append([]string(nil), s...) 42 } 43 return r2 44 } 45 46 type b3Transport struct { 47 idGen *internal.TraceIDGenerator 48 original http.RoundTripper 49 } 50 51 func txnSampled(txn newrelic.Transaction) string { 52 if txn.IsSampled() { 53 return "1" 54 } 55 return "0" 56 } 57 58 func addHeader(request *http.Request, key, val string) { 59 if val != "" { 60 request.Header.Add(key, val) 61 } 62 } 63 64 func (t *b3Transport) RoundTrip(request *http.Request) (*http.Response, error) { 65 if txn := newrelic.FromContext(request.Context()); nil != txn { 66 // The specification of http.RoundTripper requires that the request is never modified. 67 request = cloneRequest(request) 68 segment := &newrelic.ExternalSegment{ 69 StartTime: newrelic.StartSegmentNow(txn), 70 Request: request, 71 } 72 defer segment.End() 73 74 md := txn.GetTraceMetadata() 75 addHeader(request, "X-B3-TraceId", md.TraceID) 76 addHeader(request, "X-B3-SpanId", t.idGen.GenerateTraceID()) 77 addHeader(request, "X-B3-ParentSpanId", md.SpanID) 78 addHeader(request, "X-B3-Sampled", txnSampled(txn)) 79 } 80 81 return t.original.RoundTrip(request) 82 }