write_log_stream.go 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. package httputils
  2. import (
  3. "fmt"
  4. "io"
  5. "net/url"
  6. "sort"
  7. "golang.org/x/net/context"
  8. "github.com/docker/docker/api/types"
  9. "github.com/docker/docker/api/types/backend"
  10. "github.com/docker/docker/pkg/ioutils"
  11. "github.com/docker/docker/pkg/jsonlog"
  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 *types.ContainerLogsOptions, 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. // TODO(dperny) the format is defined in
  46. // daemon/logger/logger.go as logger.TimeFormat. importing
  47. // logger is verboten (not part of backend) so idk if just
  48. // importing the same thing from jsonlog is good enough
  49. logLine = append([]byte(msg.Timestamp.Format(jsonlog.RFC3339NanoFixed)+" "), logLine...)
  50. }
  51. if msg.Source == "stdout" && config.ShowStdout {
  52. outStream.Write(logLine)
  53. }
  54. if msg.Source == "stderr" && config.ShowStderr {
  55. errStream.Write(logLine)
  56. }
  57. }
  58. }
  59. type byKey []backend.LogAttr
  60. func (b byKey) Len() int { return len(b) }
  61. func (b byKey) Less(i, j int) bool { return b[i].Key < b[j].Key }
  62. func (b byKey) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
  63. func attrsByteSlice(a []backend.LogAttr) []byte {
  64. // Note this sorts "a" in-place. That is fine here - nothing else is
  65. // going to use Attrs or care about the order.
  66. sort.Sort(byKey(a))
  67. var ret []byte
  68. for i, pair := range a {
  69. k, v := url.QueryEscape(pair.Key), url.QueryEscape(pair.Value)
  70. ret = append(ret, []byte(k)...)
  71. ret = append(ret, '=')
  72. ret = append(ret, []byte(v)...)
  73. if i != len(a)-1 {
  74. ret = append(ret, ',')
  75. }
  76. }
  77. return ret
  78. }