1package client 2 3import ( 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 14const logReqMsg = `DEBUG: Request %s/%s Details: 15---[ REQUEST POST-SIGN ]----------------------------- 16%s 17-----------------------------------------------------` 18 19const logReqErrMsg = `DEBUG ERROR: Request %s/%s: 20---[ REQUEST DUMP ERROR ]----------------------------- 21%s 22------------------------------------------------------` 23 24type 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 31func (logger *logWriter) Write(b []byte) (int, error) { 32 return logger.buf.Write(b) 33} 34 35type 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 43func (reader *teeReaderCloser) Close() error { 44 return reader.Source.Close() 45} 46 47// LogHTTPRequestHandler is a SDK request handler to log the HTTP request sent 48// to a service. Will include the HTTP request body if the LogLevel of the 49// request matches LogDebugWithHTTPBody. 50var LogHTTPRequestHandler = request.NamedHandler{ 51 Name: "awssdk.client.LogRequest", 52 Fn: logRequest, 53} 54 55func logRequest(r *request.Request) { 56 logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) 57 bodySeekable := aws.IsReaderSeekable(r.Body) 58 59 b, err := httputil.DumpRequestOut(r.HTTPRequest, logBody) 60 if err != nil { 61 r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, 62 r.ClientInfo.ServiceName, r.Operation.Name, err)) 63 return 64 } 65 66 if logBody { 67 if !bodySeekable { 68 r.SetReaderBody(aws.ReadSeekCloser(r.HTTPRequest.Body)) 69 } 70 // Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's 71 // Body as a NoOpCloser and will not be reset after read by the HTTP 72 // client reader. 73 r.ResetBody() 74 } 75 76 r.Config.Logger.Log(fmt.Sprintf(logReqMsg, 77 r.ClientInfo.ServiceName, r.Operation.Name, string(b))) 78} 79 80// LogHTTPRequestHeaderHandler is a SDK request handler to log the HTTP request sent 81// to a service. Will only log the HTTP request's headers. The request payload 82// will not be read. 83var LogHTTPRequestHeaderHandler = request.NamedHandler{ 84 Name: "awssdk.client.LogRequestHeader", 85 Fn: logRequestHeader, 86} 87 88func logRequestHeader(r *request.Request) { 89 b, err := httputil.DumpRequestOut(r.HTTPRequest, false) 90 if err != nil { 91 r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, 92 r.ClientInfo.ServiceName, r.Operation.Name, err)) 93 return 94 } 95 96 r.Config.Logger.Log(fmt.Sprintf(logReqMsg, 97 r.ClientInfo.ServiceName, r.Operation.Name, string(b))) 98} 99 100const logRespMsg = `DEBUG: Response %s/%s Details: 101---[ RESPONSE ]-------------------------------------- 102%s 103-----------------------------------------------------` 104 105const logRespErrMsg = `DEBUG ERROR: Response %s/%s: 106---[ RESPONSE DUMP ERROR ]----------------------------- 107%s 108-----------------------------------------------------` 109 110// LogHTTPResponseHandler is a SDK request handler to log the HTTP response 111// received from a service. Will include the HTTP response body if the LogLevel 112// of the request matches LogDebugWithHTTPBody. 113var LogHTTPResponseHandler = request.NamedHandler{ 114 Name: "awssdk.client.LogResponse", 115 Fn: logResponse, 116} 117 118func logResponse(r *request.Request) { 119 lw := &logWriter{r.Config.Logger, bytes.NewBuffer(nil)} 120 121 if r.HTTPResponse == nil { 122 lw.Logger.Log(fmt.Sprintf(logRespErrMsg, 123 r.ClientInfo.ServiceName, r.Operation.Name, "request's HTTPResponse is nil")) 124 return 125 } 126 127 logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) 128 if logBody { 129 r.HTTPResponse.Body = &teeReaderCloser{ 130 Reader: io.TeeReader(r.HTTPResponse.Body, lw), 131 Source: r.HTTPResponse.Body, 132 } 133 } 134 135 handlerFn := func(req *request.Request) { 136 b, err := httputil.DumpResponse(req.HTTPResponse, false) 137 if err != nil { 138 lw.Logger.Log(fmt.Sprintf(logRespErrMsg, 139 req.ClientInfo.ServiceName, req.Operation.Name, err)) 140 return 141 } 142 143 lw.Logger.Log(fmt.Sprintf(logRespMsg, 144 req.ClientInfo.ServiceName, req.Operation.Name, string(b))) 145 146 if logBody { 147 b, err := ioutil.ReadAll(lw.buf) 148 if err != nil { 149 lw.Logger.Log(fmt.Sprintf(logRespErrMsg, 150 req.ClientInfo.ServiceName, req.Operation.Name, err)) 151 return 152 } 153 154 lw.Logger.Log(string(b)) 155 } 156 } 157 158 const handlerName = "awsdk.client.LogResponse.ResponseBody" 159 160 r.Handlers.Unmarshal.SetBackNamed(request.NamedHandler{ 161 Name: handlerName, Fn: handlerFn, 162 }) 163 r.Handlers.UnmarshalError.SetBackNamed(request.NamedHandler{ 164 Name: handlerName, Fn: handlerFn, 165 }) 166} 167 168// LogHTTPResponseHeaderHandler is a SDK request handler to log the HTTP 169// response received from a service. Will only log the HTTP response's headers. 170// The response payload will not be read. 171var LogHTTPResponseHeaderHandler = request.NamedHandler{ 172 Name: "awssdk.client.LogResponseHeader", 173 Fn: logResponseHeader, 174} 175 176func logResponseHeader(r *request.Request) { 177 if r.Config.Logger == nil { 178 return 179 } 180 181 b, err := httputil.DumpResponse(r.HTTPResponse, false) 182 if err != nil { 183 r.Config.Logger.Log(fmt.Sprintf(logRespErrMsg, 184 r.ClientInfo.ServiceName, r.Operation.Name, err)) 185 return 186 } 187 188 r.Config.Logger.Log(fmt.Sprintf(logRespMsg, 189 r.ClientInfo.ServiceName, r.Operation.Name, string(b))) 190} 191