-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathlogger.go
122 lines (102 loc) · 3.33 KB
/
logger.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
package transport
import (
"net/http"
"net/http/httputil"
"time"
"github.com/ankorstore/yokai/log"
"github.com/rs/zerolog"
)
// LoggerTransport is a wrapper around [http.RoundTripper] with some [LoggerTransportConfig] configuration.
type LoggerTransport struct {
transport http.RoundTripper
config *LoggerTransportConfig
}
// LoggerTransportConfig is the configuration of the [LoggerTransport].
type LoggerTransportConfig struct {
LogRequest bool
LogResponse bool
LogRequestBody bool
LogResponseBody bool
LogRequestLevel zerolog.Level
LogResponseLevel zerolog.Level
LogResponseLevelFromResponseCode bool
}
// NewLoggerTransport returns a [LoggerTransport] instance with default [LoggerTransportConfig] configuration.
func NewLoggerTransport(base http.RoundTripper) *LoggerTransport {
return NewLoggerTransportWithConfig(
base,
&LoggerTransportConfig{
LogRequest: false,
LogResponse: false,
LogRequestBody: false,
LogResponseBody: false,
LogRequestLevel: zerolog.InfoLevel,
LogResponseLevel: zerolog.InfoLevel,
LogResponseLevelFromResponseCode: false,
},
)
}
// NewLoggerTransportWithConfig returns a [LoggerTransport] instance for a provided [LoggerTransportConfig] configuration.
func NewLoggerTransportWithConfig(base http.RoundTripper, config *LoggerTransportConfig) *LoggerTransport {
if base == nil {
base = NewBaseTransport()
}
return &LoggerTransport{
transport: base,
config: config,
}
}
// Base returns the wrapped [http.RoundTripper].
func (t *LoggerTransport) Base() http.RoundTripper {
return t.transport
}
// RoundTrip performs a request / response round trip, based on the wrapped [http.RoundTripper].
//
//nolint:cyclop
func (t *LoggerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
logger := log.CtxLogger(req.Context())
if t.config.LogRequest {
reqEvt := logger.WithLevel(t.config.LogRequestLevel)
reqDump, err := httputil.DumpRequestOut(req, t.config.LogRequestBody)
if err == nil {
reqEvt.Bytes("request", reqDump)
}
reqEvt.
Str("method", req.Method).
Str("url", req.URL.String()).
Msg("http client request")
}
start := time.Now()
resp, err := t.transport.RoundTrip(req)
latency := time.Since(start).String()
if err != nil {
logger.Error().Err(err).Str("latency", latency).Msg("http client failure")
return resp, err
}
if t.config.LogResponse {
var respEvt *zerolog.Event
if t.config.LogResponseLevelFromResponseCode {
switch {
case resp.StatusCode >= http.StatusBadRequest && resp.StatusCode < http.StatusInternalServerError:
respEvt = logger.Warn()
case resp.StatusCode >= http.StatusInternalServerError:
respEvt = logger.Error()
default:
respEvt = logger.WithLevel(t.config.LogResponseLevel)
}
} else {
respEvt = logger.WithLevel(t.config.LogResponseLevel)
}
respDump, err := httputil.DumpResponse(resp, t.config.LogResponseBody)
if err == nil {
respEvt.Bytes("response", respDump)
}
respEvt.
Str("method", resp.Request.Method).
Str("url", resp.Request.URL.String()).
Int("code", resp.StatusCode).
Str("latency", latency).
Msg("http client response")
}
return resp, err
}