/
instrumented_round_tripper.go
95 lines (77 loc) · 2.75 KB
/
instrumented_round_tripper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package instrumented_round_tripper
import (
"log"
"net/http"
"time"
"github.com/cloudfoundry/dropsonde/emitter"
"github.com/cloudfoundry/dropsonde/factories"
"github.com/cloudfoundry/sonde-go/events"
"github.com/gogo/protobuf/proto"
uuid "github.com/nu7hatch/gouuid"
)
type instrumentedRoundTripper struct {
roundTripper http.RoundTripper
emitter emitter.EventEmitter
}
type instrumentedCancelableRoundTripper struct {
instrumentedRoundTripper *instrumentedRoundTripper
}
/*
Helper for creating an InstrumentedRoundTripper which will delegate to the given RoundTripper
*/
func InstrumentedRoundTripper(roundTripper http.RoundTripper, emitter emitter.EventEmitter) http.RoundTripper {
irt := &instrumentedRoundTripper{roundTripper, emitter}
_, ok := roundTripper.(canceler)
if ok {
return &instrumentedCancelableRoundTripper{
instrumentedRoundTripper: irt,
}
}
return irt
}
/*
Wraps the RoundTrip function of the given RoundTripper.
Will provide accounting metrics for the http.Request / http.Response life-cycle
Callers of RoundTrip are responsible for setting the ‘X-CF-RequestID’ field in the request header if they have one.
Callers are also responsible for setting the ‘X-CF-ApplicationID’ and ‘X-CF-InstanceIndex’ fields in the request header if they are known.
*/
func (irt *instrumentedRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
requestId, err := GenerateUuid()
if err != nil {
log.Printf("failed to generated request ID: %v\n", err)
requestId = &uuid.UUID{}
}
startTime := time.Now()
parentRequestId := req.Header.Get("X-CF-RequestID")
req.Header.Set("X-CF-RequestID", requestId.String())
resp, roundTripErr := irt.roundTripper.RoundTrip(req)
var statusCode int
var contentLength int64
if roundTripErr == nil {
statusCode = resp.StatusCode
contentLength = resp.ContentLength
}
httpStartStop := factories.NewHttpStartStop(req, statusCode, contentLength, events.PeerType_Client, requestId)
if parentRequestId != "" {
if id, err := uuid.ParseHex(parentRequestId); err == nil {
httpStartStop.ParentRequestId = factories.NewUUID(id)
}
}
httpStartStop.StartTimestamp = proto.Int64(startTime.UnixNano())
err = irt.emitter.Emit(httpStartStop)
if err != nil {
log.Printf("failed to emit startstop event: %v\n", err)
}
return resp, roundTripErr
}
func (icrt *instrumentedCancelableRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
return icrt.instrumentedRoundTripper.RoundTrip(req)
}
func (icrt *instrumentedCancelableRoundTripper) CancelRequest(req *http.Request) {
cancelableTransport := icrt.instrumentedRoundTripper.roundTripper.(canceler)
cancelableTransport.CancelRequest(req)
}
var GenerateUuid = uuid.NewV4
type canceler interface {
CancelRequest(*http.Request)
}