logger.go 3.2 KB

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