]>
Commit | Line | Data |
---|---|---|
9b12e4fe JC |
1 | package client |
2 | ||
3 | import ( | |
4 | "bytes" | |
5 | "fmt" | |
6 | "io" | |
7 | "io/ioutil" | |
8 | "net/http/httputil" | |
9 | ||
10 | "github.com/aws/aws-sdk-go/aws" | |
11 | "github.com/aws/aws-sdk-go/aws/request" | |
12 | ) | |
13 | ||
14 | const logReqMsg = `DEBUG: Request %s/%s Details: | |
15 | ---[ REQUEST POST-SIGN ]----------------------------- | |
16 | %s | |
17 | -----------------------------------------------------` | |
18 | ||
19 | const logReqErrMsg = `DEBUG ERROR: Request %s/%s: | |
20 | ---[ REQUEST DUMP ERROR ]----------------------------- | |
21 | %s | |
22 | ------------------------------------------------------` | |
23 | ||
24 | type logWriter struct { | |
25 | // Logger is what we will use to log the payload of a response. | |
26 | Logger aws.Logger | |
27 | // buf stores the contents of what has been read | |
28 | buf *bytes.Buffer | |
29 | } | |
30 | ||
31 | func (logger *logWriter) Write(b []byte) (int, error) { | |
32 | return logger.buf.Write(b) | |
33 | } | |
34 | ||
35 | type teeReaderCloser struct { | |
36 | // io.Reader will be a tee reader that is used during logging. | |
37 | // This structure will read from a body and write the contents to a logger. | |
38 | io.Reader | |
39 | // Source is used just to close when we are done reading. | |
40 | Source io.ReadCloser | |
41 | } | |
42 | ||
43 | func (reader *teeReaderCloser) Close() error { | |
44 | return reader.Source.Close() | |
45 | } | |
46 | ||
47 | func logRequest(r *request.Request) { | |
48 | logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) | |
49 | dumpedBody, err := httputil.DumpRequestOut(r.HTTPRequest, logBody) | |
50 | if err != nil { | |
51 | r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, r.ClientInfo.ServiceName, r.Operation.Name, err)) | |
52 | return | |
53 | } | |
54 | ||
55 | if logBody { | |
56 | // Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's | |
57 | // Body as a NoOpCloser and will not be reset after read by the HTTP | |
58 | // client reader. | |
59 | r.ResetBody() | |
60 | } | |
61 | ||
62 | r.Config.Logger.Log(fmt.Sprintf(logReqMsg, r.ClientInfo.ServiceName, r.Operation.Name, string(dumpedBody))) | |
63 | } | |
64 | ||
65 | const logRespMsg = `DEBUG: Response %s/%s Details: | |
66 | ---[ RESPONSE ]-------------------------------------- | |
67 | %s | |
68 | -----------------------------------------------------` | |
69 | ||
70 | const logRespErrMsg = `DEBUG ERROR: Response %s/%s: | |
71 | ---[ RESPONSE DUMP ERROR ]----------------------------- | |
72 | %s | |
73 | -----------------------------------------------------` | |
74 | ||
75 | func logResponse(r *request.Request) { | |
76 | lw := &logWriter{r.Config.Logger, bytes.NewBuffer(nil)} | |
77 | r.HTTPResponse.Body = &teeReaderCloser{ | |
78 | Reader: io.TeeReader(r.HTTPResponse.Body, lw), | |
79 | Source: r.HTTPResponse.Body, | |
80 | } | |
81 | ||
82 | handlerFn := func(req *request.Request) { | |
83 | body, err := httputil.DumpResponse(req.HTTPResponse, false) | |
84 | if err != nil { | |
85 | lw.Logger.Log(fmt.Sprintf(logRespErrMsg, req.ClientInfo.ServiceName, req.Operation.Name, err)) | |
86 | return | |
87 | } | |
88 | ||
89 | b, err := ioutil.ReadAll(lw.buf) | |
90 | if err != nil { | |
91 | lw.Logger.Log(fmt.Sprintf(logRespErrMsg, req.ClientInfo.ServiceName, req.Operation.Name, err)) | |
92 | return | |
93 | } | |
94 | lw.Logger.Log(fmt.Sprintf(logRespMsg, req.ClientInfo.ServiceName, req.Operation.Name, string(body))) | |
95 | if req.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) { | |
96 | lw.Logger.Log(string(b)) | |
97 | } | |
98 | } | |
99 | ||
100 | const handlerName = "awsdk.client.LogResponse.ResponseBody" | |
101 | ||
102 | r.Handlers.Unmarshal.SetBackNamed(request.NamedHandler{ | |
103 | Name: handlerName, Fn: handlerFn, | |
104 | }) | |
105 | r.Handlers.UnmarshalError.SetBackNamed(request.NamedHandler{ | |
106 | Name: handlerName, Fn: handlerFn, | |
107 | }) | |
108 | } |