/
objects.go
124 lines (111 loc) · 3.01 KB
/
objects.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
package httplog
import (
"bytes"
"encoding/json"
"io"
"net/http"
"strings"
"go.uber.org/zap/zapcore"
)
// Request represents an HTTP request, it implements a zapcore.ObjectMarshaller
// on it.
type Request struct {
Headers Headers
Body []byte
}
// NewRequest creates a Request object from an http.Request.
func NewRequest(r *http.Request) (*Request, error) {
b, err := dumpRequestBody(r)
if err != nil {
return nil, err
}
return &Request{
Headers: Headers(r.Header),
Body: b,
}, nil
}
// MarshalLogObject adds the properties of the request in the log.
func (r *Request) MarshalLogObject(enc zapcore.ObjectEncoder) error {
_ = enc.AddObject("headers", r.Headers)
if ct := r.Headers.Get("Content-Type"); ct != "" {
marshalBody(ct, r.Body, enc)
} else {
enc.AddBinary("body", r.Body)
}
return nil
}
// Response represents an HTTP response, it implements a
// zapcore.ObjectMarshaller on it.
type Response struct {
Headers Headers
Body []byte
}
// MarshalLogObject adds the properties of the response in the log.
func (r *Response) MarshalLogObject(enc zapcore.ObjectEncoder) error {
_ = enc.AddObject("headers", r.Headers)
if ct := r.Headers.Get("Content-Type"); ct != "" {
marshalBody(ct, r.Body, enc)
} else {
enc.AddBinary("body", r.Body)
}
return nil
}
// Headers represents key-value pairs in the HTTP headers.
type Headers http.Header
// Get gets the first value associated with the given key. If there are no
// values associated with the key, Get returns "".
func (h Headers) Get(key string) string {
return http.Header(h).Get(key)
}
// Values returns all values associated with the given key.
func (h Headers) Values(key string) []string {
return http.Header(h).Values(key)
}
// MarshalLogObject adds the headers in the log.
func (h Headers) MarshalLogObject(enc zapcore.ObjectEncoder) error {
for k, v := range h {
_ = enc.AddArray(k, zapcore.ArrayMarshalerFunc(func(enc zapcore.ArrayEncoder) error {
for i := range v {
enc.AppendString(v[i])
}
return nil
}))
}
return nil
}
func marshalBody(contentType string, body []byte, enc zapcore.ObjectEncoder) {
ct := strings.SplitN(contentType, ";", 2)
switch {
case strings.HasSuffix(ct[0], "json"):
m := make(map[string]interface{})
if err := json.Unmarshal(body, &m); err == nil {
if err := enc.AddReflected("json", m); err == nil {
return
}
}
// fallback to string
enc.AddString("text", string(body))
case strings.HasPrefix(ct[0], "text"):
enc.AddString("text", string(body))
case strings.HasSuffix(ct[0], "x-www-form-urlencoded"):
enc.AddString("text", string(body))
case strings.HasSuffix(ct[0], "xml"):
enc.AddString("text", string(body))
default:
enc.AddBinary("body", body)
}
}
func dumpRequestBody(r *http.Request) ([]byte, error) {
if r.Body == nil || r.Body == http.NoBody {
return nil, nil
}
var buf bytes.Buffer
if _, err := buf.ReadFrom(r.Body); err != nil {
return nil, err
}
if err := r.Body.Close(); err != nil {
return nil, err
}
r.Body = io.NopCloser(&buf)
return buf.Bytes(), nil
}