write_log_stream.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package httputils // import "github.com/docker/docker/api/server/httputils"
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "net/url"
  7. "sort"
  8. "github.com/docker/docker/api/types/backend"
  9. "github.com/docker/docker/api/types/container"
  10. "github.com/docker/docker/pkg/ioutils"
  11. "github.com/docker/docker/pkg/jsonmessage"
  12. "github.com/docker/docker/pkg/stdcopy"
  13. )
  14. // WriteLogStream writes an encoded byte stream of log messages from the
  15. // messages channel, multiplexing them with a stdcopy.Writer if mux is true
  16. func WriteLogStream(_ context.Context, w io.Writer, msgs <-chan *backend.LogMessage, config *container.LogsOptions, mux bool) {
  17. wf := ioutils.NewWriteFlusher(w)
  18. defer wf.Close()
  19. wf.Flush()
  20. outStream := io.Writer(wf)
  21. errStream := outStream
  22. sysErrStream := errStream
  23. if mux {
  24. sysErrStream = stdcopy.NewStdWriter(outStream, stdcopy.Systemerr)
  25. errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
  26. outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
  27. }
  28. for {
  29. msg, ok := <-msgs
  30. if !ok {
  31. return
  32. }
  33. // check if the message contains an error. if so, write that error
  34. // and exit
  35. if msg.Err != nil {
  36. fmt.Fprintf(sysErrStream, "Error grabbing logs: %v\n", msg.Err)
  37. continue
  38. }
  39. logLine := msg.Line
  40. if config.Details {
  41. logLine = append(attrsByteSlice(msg.Attrs), ' ')
  42. logLine = append(logLine, msg.Line...)
  43. }
  44. if config.Timestamps {
  45. logLine = append([]byte(msg.Timestamp.Format(jsonmessage.RFC3339NanoFixed)+" "), logLine...)
  46. }
  47. if msg.Source == "stdout" && config.ShowStdout {
  48. _, _ = outStream.Write(logLine)
  49. }
  50. if msg.Source == "stderr" && config.ShowStderr {
  51. _, _ = errStream.Write(logLine)
  52. }
  53. }
  54. }
  55. type byKey []backend.LogAttr
  56. func (b byKey) Len() int { return len(b) }
  57. func (b byKey) Less(i, j int) bool { return b[i].Key < b[j].Key }
  58. func (b byKey) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
  59. func attrsByteSlice(a []backend.LogAttr) []byte {
  60. // Note this sorts "a" in-place. That is fine here - nothing else is
  61. // going to use Attrs or care about the order.
  62. sort.Sort(byKey(a))
  63. var ret []byte
  64. for i, pair := range a {
  65. k, v := url.QueryEscape(pair.Key), url.QueryEscape(pair.Value)
  66. ret = append(ret, []byte(k)...)
  67. ret = append(ret, '=')
  68. ret = append(ret, []byte(v)...)
  69. if i != len(a)-1 {
  70. ret = append(ret, ',')
  71. }
  72. }
  73. return ret
  74. }