ContainerLogs copies the container's log channel to the channel provided in the config. If ContainerLogs returns an error, no messages have been copied. and the channel will be closed without data. if it returns nil, the config channel will be active and return log messages until it runs out or the
(ctx context.Context, containerName string, config *backend.ContainerLogsOptions)
| 23 | // if it returns nil, the config channel will be active and return log |
| 24 | // messages until it runs out or the context is canceled. |
| 25 | func (daemon *Daemon) ContainerLogs(ctx context.Context, containerName string, config *backend.ContainerLogsOptions) (messages <-chan *backend.LogMessage, isTTY bool, retErr error) { |
| 26 | ctx, span := tracing.StartSpan(ctx, "daemon.ContainerLogs") |
| 27 | defer func() { |
| 28 | span.SetStatus(retErr) |
| 29 | span.End() |
| 30 | }() |
| 31 | |
| 32 | lg := log.G(ctx).WithFields(log.Fields{ |
| 33 | "module": "daemon", |
| 34 | "method": "(*Daemon).ContainerLogs", |
| 35 | "container": containerName, |
| 36 | }) |
| 37 | |
| 38 | if !config.ShowStdout && !config.ShowStderr { |
| 39 | return nil, false, errdefs.InvalidParameter(errors.New("You must choose at least one stream")) |
| 40 | } |
| 41 | ctr, err := daemon.GetContainer(containerName) |
| 42 | if err != nil { |
| 43 | return nil, false, err |
| 44 | } |
| 45 | |
| 46 | if ctr.State.RemovalInProgress || ctr.State.Dead { |
| 47 | return nil, false, errdefs.Conflict(errors.New("can not get logs from container which is dead or marked for removal")) |
| 48 | } |
| 49 | |
| 50 | if ctr.HostConfig.LogConfig.Type == "none" { |
| 51 | return nil, false, logger.ErrReadLogsNotSupported{} |
| 52 | } |
| 53 | |
| 54 | cLog, cLogCreated, err := daemon.getLogger(ctr) |
| 55 | if err != nil { |
| 56 | return nil, false, err |
| 57 | } |
| 58 | if cLogCreated { |
| 59 | defer func() { |
| 60 | if retErr != nil { |
| 61 | if err = cLog.Close(); err != nil { |
| 62 | log.G(ctx).Errorf("Error closing logger: %v", err) |
| 63 | } |
| 64 | } |
| 65 | }() |
| 66 | } |
| 67 | |
| 68 | logReader, ok := cLog.(logger.LogReader) |
| 69 | if !ok { |
| 70 | return nil, false, logger.ErrReadLogsNotSupported{} |
| 71 | } |
| 72 | |
| 73 | tailLines, err := strconv.Atoi(config.Tail) |
| 74 | if err != nil { |
| 75 | tailLines = -1 |
| 76 | } |
| 77 | |
| 78 | follow := config.Follow && !cLogCreated |
| 79 | logs := logReader.ReadLogs(ctx, logger.ReadConfig{ |
| 80 | Since: config.Since, |
| 81 | Until: config.Until, |
| 82 | Tail: tailLines, |
nothing calls this directly
no test coverage detected