forked from tcnksm/go-httpstat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
httpstat.go
133 lines (117 loc) · 3.66 KB
/
httpstat.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Package httpstat traces HTTP latency infomation (DNSLookup, TCP Connection and so on) on any golang HTTP request.
// It uses `httptrace` package. Just create `go-httpstat` powered `context.Context` and give it your `http.Request` (no big code modification is required).
package httpstat
import (
"bytes"
"context"
"fmt"
"io"
"strings"
"time"
)
// Result stores httpstat info.
type Result struct {
// The following are duration for each phase
DNSLookup time.Duration
TCPConnection time.Duration
TLSHandshake time.Duration
ServerProcessing time.Duration
contentTransfer time.Duration
// The followings are timeline of request
NameLookup time.Duration
Connect time.Duration
Pretransfer time.Duration
StartTransfer time.Duration
total time.Duration
t0 time.Time
t1 time.Time
t2 time.Time
t3 time.Time
t4 time.Time
t5 time.Time // need to be provided from outside
dnsStart time.Time
dnsDone time.Time
tcpStart time.Time
tcpDone time.Time
tlsStart time.Time
tlsDone time.Time
serverStart time.Time
serverDone time.Time
transferStart time.Time
trasferDone time.Time // need to be provided from outside
// isTLS is true when connection seems to use TLS
isTLS bool
// isReused is true when connection is reused (keep-alive)
isReused bool
}
func (r *Result) durations() map[string]time.Duration {
return map[string]time.Duration{
"DNSLookup": r.DNSLookup,
"TCPConnection": r.TCPConnection,
"TLSHandshake": r.TLSHandshake,
"ServerProcessing": r.ServerProcessing,
"ContentTransfer": r.contentTransfer,
"NameLookup": r.NameLookup,
"Connect": r.Connect,
"Pretransfer": r.Connect,
"StartTransfer": r.StartTransfer,
"Total": r.total,
}
}
// Format formats stats result.
func (r Result) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
var buf bytes.Buffer
fmt.Fprintf(&buf, "DNS lookup: %4d ms\n",
int(r.DNSLookup/time.Millisecond))
fmt.Fprintf(&buf, "TCP connection: %4d ms\n",
int(r.TCPConnection/time.Millisecond))
fmt.Fprintf(&buf, "TLS handshake: %4d ms\n",
int(r.TLSHandshake/time.Millisecond))
fmt.Fprintf(&buf, "Server processing: %4d ms\n",
int(r.ServerProcessing/time.Millisecond))
if r.total > 0 {
fmt.Fprintf(&buf, "Content transfer: %4d ms\n\n",
int(r.contentTransfer/time.Millisecond))
} else {
fmt.Fprintf(&buf, "Content transfer: %4s ms\n\n", "-")
}
fmt.Fprintf(&buf, "Name Lookup: %4d ms\n",
int(r.NameLookup/time.Millisecond))
fmt.Fprintf(&buf, "Connect: %4d ms\n",
int(r.Connect/time.Millisecond))
fmt.Fprintf(&buf, "Pre Transfer: %4d ms\n",
int(r.Pretransfer/time.Millisecond))
fmt.Fprintf(&buf, "Start Transfer: %4d ms\n",
int(r.StartTransfer/time.Millisecond))
if r.total > 0 {
fmt.Fprintf(&buf, "Total: %4d ms\n",
int(r.total/time.Millisecond))
} else {
fmt.Fprintf(&buf, "Total: %4s ms\n", "-")
}
io.WriteString(s, buf.String())
return
}
fallthrough
case 's', 'q':
d := r.durations()
list := make([]string, 0, len(d))
for k, v := range d {
// Handle when End function is not called
if (k == "ContentTransfer" || k == "Total") && r.t5.IsZero() {
list = append(list, fmt.Sprintf("%s: - ms", k))
continue
}
list = append(list, fmt.Sprintf("%s: %d ms", k, v/time.Millisecond))
}
io.WriteString(s, strings.Join(list, ", "))
}
}
// WithHTTPStat is a wrapper of httptrace.WithClientTrace. It records the
// time of each httptrace hooks.
func WithHTTPStat(ctx context.Context, r *Result) context.Context {
return withClientTrace(ctx, r)
}