(ctr *container.Container, cfg *stream.AttachConfig, enableLogs, doStream bool)
| 124 | } |
| 125 | |
| 126 | func (daemon *Daemon) containerAttach(ctr *container.Container, cfg *stream.AttachConfig, enableLogs, doStream bool) error { |
| 127 | if enableLogs { |
| 128 | logDriver, logCreated, err := daemon.getLogger(ctr) |
| 129 | if err != nil { |
| 130 | return err |
| 131 | } |
| 132 | if logCreated { |
| 133 | defer func() { |
| 134 | if err = logDriver.Close(); err != nil { |
| 135 | log.G(context.TODO()).WithFields(log.Fields{ |
| 136 | "error": err, |
| 137 | "container": ctr.ID, |
| 138 | }).Error("Error closing logger") |
| 139 | } |
| 140 | }() |
| 141 | } |
| 142 | cLog, ok := logDriver.(logger.LogReader) |
| 143 | if !ok { |
| 144 | return logger.ErrReadLogsNotSupported{} |
| 145 | } |
| 146 | logWatcher := cLog.ReadLogs(context.TODO(), logger.ReadConfig{Tail: -1}) |
| 147 | defer logWatcher.ConsumerGone() |
| 148 | |
| 149 | LogLoop: |
| 150 | for { |
| 151 | select { |
| 152 | case msg, ok := <-logWatcher.Msg: |
| 153 | if !ok { |
| 154 | break LogLoop |
| 155 | } |
| 156 | if msg.Source == "stdout" && cfg.Stdout != nil { |
| 157 | cfg.Stdout.Write(msg.Line) |
| 158 | } |
| 159 | if msg.Source == "stderr" && cfg.Stderr != nil { |
| 160 | cfg.Stderr.Write(msg.Line) |
| 161 | } |
| 162 | case err := <-logWatcher.Err: |
| 163 | log.G(context.TODO()).WithFields(log.Fields{ |
| 164 | "error": err, |
| 165 | "container": ctr.ID, |
| 166 | }).Error("Error streaming logs") |
| 167 | break LogLoop |
| 168 | } |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | daemon.LogContainerEvent(ctr, events.ActionAttach) |
| 173 | |
| 174 | if !doStream { |
| 175 | return nil |
| 176 | } |
| 177 | |
| 178 | if cfg.Stdin != nil { |
| 179 | r, w := io.Pipe() |
| 180 | go func(stdin io.ReadCloser) { |
| 181 | io.Copy(w, stdin) |
| 182 | log.G(context.TODO()).WithFields(log.Fields{ |
| 183 | "container": ctr.ID, |
no test coverage detected