logs.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package daemon
  2. import (
  3. "errors"
  4. "io"
  5. "strconv"
  6. "time"
  7. "golang.org/x/net/context"
  8. "github.com/Sirupsen/logrus"
  9. "github.com/docker/docker/api/types/backend"
  10. containertypes "github.com/docker/docker/api/types/container"
  11. timetypes "github.com/docker/docker/api/types/time"
  12. "github.com/docker/docker/container"
  13. "github.com/docker/docker/daemon/logger"
  14. "github.com/docker/docker/pkg/ioutils"
  15. "github.com/docker/docker/pkg/stdcopy"
  16. )
  17. // ContainerLogs hooks up a container's stdout and stderr streams
  18. // configured with the given struct.
  19. func (daemon *Daemon) ContainerLogs(ctx context.Context, containerName string, config *backend.ContainerLogsConfig, started chan struct{}) error {
  20. if !(config.ShowStdout || config.ShowStderr) {
  21. return errors.New("You must choose at least one stream")
  22. }
  23. container, err := daemon.GetContainer(containerName)
  24. if err != nil {
  25. return err
  26. }
  27. if container.HostConfig.LogConfig.Type == "none" {
  28. return logger.ErrReadLogsNotSupported
  29. }
  30. cLog, err := daemon.getLogger(container)
  31. if err != nil {
  32. return err
  33. }
  34. logReader, ok := cLog.(logger.LogReader)
  35. if !ok {
  36. return logger.ErrReadLogsNotSupported
  37. }
  38. follow := config.Follow && container.IsRunning()
  39. tailLines, err := strconv.Atoi(config.Tail)
  40. if err != nil {
  41. tailLines = -1
  42. }
  43. logrus.Debug("logs: begin stream")
  44. var since time.Time
  45. if config.Since != "" {
  46. s, n, err := timetypes.ParseTimestamps(config.Since, 0)
  47. if err != nil {
  48. return err
  49. }
  50. since = time.Unix(s, n)
  51. }
  52. readConfig := logger.ReadConfig{
  53. Since: since,
  54. Tail: tailLines,
  55. Follow: follow,
  56. }
  57. logs := logReader.ReadLogs(readConfig)
  58. // Close logWatcher on exit
  59. defer func() {
  60. logs.Close()
  61. if cLog != container.LogDriver {
  62. // Since the logger isn't cached in the container, which
  63. // occurs if it is running, it must get explicitly closed
  64. // here to avoid leaking it and any file handles it has.
  65. if err := cLog.Close(); err != nil {
  66. logrus.Errorf("Error closing logger: %v", err)
  67. }
  68. }
  69. }()
  70. wf := ioutils.NewWriteFlusher(config.OutStream)
  71. defer wf.Close()
  72. close(started)
  73. wf.Flush()
  74. var outStream io.Writer
  75. outStream = wf
  76. errStream := outStream
  77. if !container.Config.Tty {
  78. errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
  79. outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
  80. }
  81. for {
  82. select {
  83. case err := <-logs.Err:
  84. logrus.Errorf("Error streaming logs: %v", err)
  85. return nil
  86. case <-ctx.Done():
  87. logrus.Debugf("logs: end stream, ctx is done: %v", ctx.Err())
  88. return nil
  89. case msg, ok := <-logs.Msg:
  90. if !ok {
  91. logrus.Debug("logs: end stream")
  92. return nil
  93. }
  94. logLine := msg.Line
  95. if config.Details {
  96. logLine = append([]byte(msg.Attrs.String()+" "), logLine...)
  97. }
  98. if config.Timestamps {
  99. logLine = append([]byte(msg.Timestamp.Format(logger.TimeFormat)+" "), logLine...)
  100. }
  101. if msg.Source == "stdout" && config.ShowStdout {
  102. outStream.Write(logLine)
  103. }
  104. if msg.Source == "stderr" && config.ShowStderr {
  105. errStream.Write(logLine)
  106. }
  107. }
  108. }
  109. }
  110. func (daemon *Daemon) getLogger(container *container.Container) (logger.Logger, error) {
  111. if container.LogDriver != nil && container.IsRunning() {
  112. return container.LogDriver, nil
  113. }
  114. return container.StartLogger()
  115. }
  116. // mergeLogConfig merges the daemon log config to the container's log config if the container's log driver is not specified.
  117. func (daemon *Daemon) mergeAndVerifyLogConfig(cfg *containertypes.LogConfig) error {
  118. if cfg.Type == "" {
  119. cfg.Type = daemon.defaultLogConfig.Type
  120. }
  121. if cfg.Config == nil {
  122. cfg.Config = make(map[string]string)
  123. }
  124. if cfg.Type == daemon.defaultLogConfig.Type {
  125. for k, v := range daemon.defaultLogConfig.Config {
  126. if _, ok := cfg.Config[k]; !ok {
  127. cfg.Config[k] = v
  128. }
  129. }
  130. }
  131. return logger.ValidateLogOpts(cfg.Type, cfg.Config)
  132. }