-
Notifications
You must be signed in to change notification settings - Fork 45
/
http.go
160 lines (132 loc) · 4.95 KB
/
http.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package zapdriver
// "Broker: Request timed out"
// https://console.cloud.google.com/logs/viewer?project=bnl-blendle&minLogLevel=
// 0&expandAll=false×tamp=2018-05-23T22:21:56.142000000Z&customFacets=&limi
// tCustomFacetWidth=true&dateRangeEnd=2018-05-23T22:21:52.545Z&interval=PT1H&re
// source=container%2Fcluster_name%2Fblendle-2%2Fnamespace_id%2Fstream-
// composition-analytic-events-
// backfill&scrollTimestamp=2018-05-23T05:29:33.000000000Z&logName=projects
// %2Fbnl-blendle%2Flogs%2Fstream-composition-analytic-events-
// pipe-1&dateRangeUnbound=backwardInTime
import (
"bytes"
"io"
"net/http"
"strconv"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// HTTP adds the correct Stackdriver "HTTP" field.
//
// see: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#HttpRequest
func HTTP(req *HTTPPayload) zap.Field {
return zap.Object("httpRequest", req)
}
// HTTPPayload is the complete payload that can be interpreted by
// Stackdriver as a HTTP request.
type HTTPPayload struct {
// The request method. Examples: "GET", "HEAD", "PUT", "POST".
RequestMethod string `json:"requestMethod"`
// The scheme (http, https), the host name, the path and the query portion of
// the URL that was requested.
//
// Example: "http://example.com/some/info?color=red".
RequestURL string `json:"requestUrl"`
// The size of the HTTP request message in bytes, including the request
// headers and the request body.
RequestSize string `json:"requestSize"`
// The response code indicating the status of response.
//
// Examples: 200, 404.
Status int `json:"status"`
// The size of the HTTP response message sent back to the client, in bytes,
// including the response headers and the response body.
ResponseSize string `json:"responseSize"`
// The user agent sent by the client.
//
// Example: "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; Q312461; .NET CLR 1.0.3705)".
UserAgent string `json:"userAgent"`
// The IP address (IPv4 or IPv6) of the client that issued the HTTP request.
//
// Examples: "192.168.1.1", "FE80::0202:B3FF:FE1E:8329".
RemoteIP string `json:"remoteIp"`
// The IP address (IPv4 or IPv6) of the origin server that the request was
// sent to.
ServerIP string `json:"serverIp"`
// The referrer URL of the request, as defined in HTTP/1.1 Header Field
// Definitions.
Referer string `json:"referer"`
// The request processing latency on the server, from the time the request was
// received until the response was sent.
//
// A duration in seconds with up to nine fractional digits, terminated by 's'.
//
// Example: "3.5s".
Latency string `json:"latency"`
// Whether or not a cache lookup was attempted.
CacheLookup bool `json:"cacheLookup"`
// Whether or not an entity was served from cache (with or without
// validation).
CacheHit bool `json:"cacheHit"`
// Whether or not the response was validated with the origin server before
// being served from cache. This field is only meaningful if cacheHit is True.
CacheValidatedWithOriginServer bool `json:"cacheValidatedWithOriginServer"`
// The number of HTTP response bytes inserted into cache. Set only when a
// cache fill was attempted.
CacheFillBytes string `json:"cacheFillBytes"`
// Protocol used for the request.
//
// Examples: "HTTP/1.1", "HTTP/2", "websocket"
Protocol string `json:"protocol"`
}
// NewHTTP returns a new HTTPPayload struct, based on the passed
// in http.Request and http.Response objects.
func NewHTTP(req *http.Request, res *http.Response) *HTTPPayload {
if req == nil {
req = &http.Request{}
}
if res == nil {
res = &http.Response{}
}
sdreq := &HTTPPayload{
RequestMethod: req.Method,
Status: res.StatusCode,
UserAgent: req.UserAgent(),
RemoteIP: req.RemoteAddr,
Referer: req.Referer(),
Protocol: req.Proto,
}
if req.URL != nil {
sdreq.RequestURL = req.URL.String()
}
buf := &bytes.Buffer{}
if req.Body != nil {
n, _ := io.Copy(buf, req.Body) // nolint: gas
sdreq.RequestSize = strconv.FormatInt(n, 10)
}
if res.Body != nil {
buf.Reset()
n, _ := io.Copy(buf, res.Body) // nolint: gas
sdreq.ResponseSize = strconv.FormatInt(n, 10)
}
return sdreq
}
// MarshalLogObject implements zapcore.ObjectMarshaller interface.
func (req HTTPPayload) MarshalLogObject(enc zapcore.ObjectEncoder) error {
enc.AddString("requestMethod", req.RequestMethod)
enc.AddString("requestUrl", req.RequestURL)
enc.AddString("requestSize", req.RequestSize)
enc.AddInt("status", req.Status)
enc.AddString("responseSize", req.ResponseSize)
enc.AddString("userAgent", req.UserAgent)
enc.AddString("remoteIp", req.RemoteIP)
enc.AddString("serverIp", req.ServerIP)
enc.AddString("referer", req.Referer)
enc.AddString("latency", req.Latency)
enc.AddBool("cacheLookup", req.CacheLookup)
enc.AddBool("cacheHit", req.CacheHit)
enc.AddBool("cacheValidatedWithOriginServer", req.CacheValidatedWithOriginServer)
enc.AddString("cacheFillBytes", req.CacheFillBytes)
enc.AddString("protocol", req.Protocol)
return nil
}