| package client |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "net/http/httputil" |
| |
| "github.com/aws/aws-sdk-go/aws" |
| "github.com/aws/aws-sdk-go/aws/request" |
| ) |
| |
| const logReqMsg = `DEBUG: Request %s/%s Details: |
| ---[ REQUEST POST-SIGN ]----------------------------- |
| %s |
| -----------------------------------------------------` |
| |
| const logReqErrMsg = `DEBUG ERROR: Request %s/%s: |
| ---[ REQUEST DUMP ERROR ]----------------------------- |
| %s |
| ------------------------------------------------------` |
| |
| type logWriter struct { |
| // Logger is what we will use to log the payload of a response. |
| Logger aws.Logger |
| // buf stores the contents of what has been read |
| buf *bytes.Buffer |
| } |
| |
| func (logger *logWriter) Write(b []byte) (int, error) { |
| return logger.buf.Write(b) |
| } |
| |
| type teeReaderCloser struct { |
| // io.Reader will be a tee reader that is used during logging. |
| // This structure will read from a body and write the contents to a logger. |
| io.Reader |
| // Source is used just to close when we are done reading. |
| Source io.ReadCloser |
| } |
| |
| func (reader *teeReaderCloser) Close() error { |
| return reader.Source.Close() |
| } |
| |
| // LogHTTPRequestHandler is a SDK request handler to log the HTTP request sent |
| // to a service. Will include the HTTP request body if the LogLevel of the |
| // request matches LogDebugWithHTTPBody. |
| var LogHTTPRequestHandler = request.NamedHandler{ |
| Name: "awssdk.client.LogRequest", |
| Fn: logRequest, |
| } |
| |
| func logRequest(r *request.Request) { |
| logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) |
| bodySeekable := aws.IsReaderSeekable(r.Body) |
| |
| b, err := httputil.DumpRequestOut(r.HTTPRequest, logBody) |
| if err != nil { |
| r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, |
| r.ClientInfo.ServiceName, r.Operation.Name, err)) |
| return |
| } |
| |
| if logBody { |
| if !bodySeekable { |
| r.SetReaderBody(aws.ReadSeekCloser(r.HTTPRequest.Body)) |
| } |
| // Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's |
| // Body as a NoOpCloser and will not be reset after read by the HTTP |
| // client reader. |
| r.ResetBody() |
| } |
| |
| r.Config.Logger.Log(fmt.Sprintf(logReqMsg, |
| r.ClientInfo.ServiceName, r.Operation.Name, string(b))) |
| } |
| |
| // LogHTTPRequestHeaderHandler is a SDK request handler to log the HTTP request sent |
| // to a service. Will only log the HTTP request's headers. The request payload |
| // will not be read. |
| var LogHTTPRequestHeaderHandler = request.NamedHandler{ |
| Name: "awssdk.client.LogRequestHeader", |
| Fn: logRequestHeader, |
| } |
| |
| func logRequestHeader(r *request.Request) { |
| b, err := httputil.DumpRequestOut(r.HTTPRequest, false) |
| if err != nil { |
| r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, |
| r.ClientInfo.ServiceName, r.Operation.Name, err)) |
| return |
| } |
| |
| r.Config.Logger.Log(fmt.Sprintf(logReqMsg, |
| r.ClientInfo.ServiceName, r.Operation.Name, string(b))) |
| } |
| |
| const logRespMsg = `DEBUG: Response %s/%s Details: |
| ---[ RESPONSE ]-------------------------------------- |
| %s |
| -----------------------------------------------------` |
| |
| const logRespErrMsg = `DEBUG ERROR: Response %s/%s: |
| ---[ RESPONSE DUMP ERROR ]----------------------------- |
| %s |
| -----------------------------------------------------` |
| |
| // LogHTTPResponseHandler is a SDK request handler to log the HTTP response |
| // received from a service. Will include the HTTP response body if the LogLevel |
| // of the request matches LogDebugWithHTTPBody. |
| var LogHTTPResponseHandler = request.NamedHandler{ |
| Name: "awssdk.client.LogResponse", |
| Fn: logResponse, |
| } |
| |
| func logResponse(r *request.Request) { |
| lw := &logWriter{r.Config.Logger, bytes.NewBuffer(nil)} |
| |
| if r.HTTPResponse == nil { |
| lw.Logger.Log(fmt.Sprintf(logRespErrMsg, |
| r.ClientInfo.ServiceName, r.Operation.Name, "request's HTTPResponse is nil")) |
| return |
| } |
| |
| logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) |
| if logBody { |
| r.HTTPResponse.Body = &teeReaderCloser{ |
| Reader: io.TeeReader(r.HTTPResponse.Body, lw), |
| Source: r.HTTPResponse.Body, |
| } |
| } |
| |
| handlerFn := func(req *request.Request) { |
| b, err := httputil.DumpResponse(req.HTTPResponse, false) |
| if err != nil { |
| lw.Logger.Log(fmt.Sprintf(logRespErrMsg, |
| req.ClientInfo.ServiceName, req.Operation.Name, err)) |
| return |
| } |
| |
| lw.Logger.Log(fmt.Sprintf(logRespMsg, |
| req.ClientInfo.ServiceName, req.Operation.Name, string(b))) |
| |
| if logBody { |
| b, err := ioutil.ReadAll(lw.buf) |
| if err != nil { |
| lw.Logger.Log(fmt.Sprintf(logRespErrMsg, |
| req.ClientInfo.ServiceName, req.Operation.Name, err)) |
| return |
| } |
| |
| lw.Logger.Log(string(b)) |
| } |
| } |
| |
| const handlerName = "awsdk.client.LogResponse.ResponseBody" |
| |
| r.Handlers.Unmarshal.SetBackNamed(request.NamedHandler{ |
| Name: handlerName, Fn: handlerFn, |
| }) |
| r.Handlers.UnmarshalError.SetBackNamed(request.NamedHandler{ |
| Name: handlerName, Fn: handlerFn, |
| }) |
| } |
| |
| // LogHTTPResponseHeaderHandler is a SDK request handler to log the HTTP |
| // response received from a service. Will only log the HTTP response's headers. |
| // The response payload will not be read. |
| var LogHTTPResponseHeaderHandler = request.NamedHandler{ |
| Name: "awssdk.client.LogResponseHeader", |
| Fn: logResponseHeader, |
| } |
| |
| func logResponseHeader(r *request.Request) { |
| if r.Config.Logger == nil { |
| return |
| } |
| |
| b, err := httputil.DumpResponse(r.HTTPResponse, false) |
| if err != nil { |
| r.Config.Logger.Log(fmt.Sprintf(logRespErrMsg, |
| r.ClientInfo.ServiceName, r.Operation.Name, err)) |
| return |
| } |
| |
| r.Config.Logger.Log(fmt.Sprintf(logRespMsg, |
| r.ClientInfo.ServiceName, r.Operation.Name, string(b))) |
| } |