Files
podman/libpod/container_log.go
jgallucci32 9ac115e691 Fix -f logs to stop when a container exits
Fixes an issue with the previous PR where a container would exit while following logs and the log tail continued to follow. This creates a subroutine which checks the state of the container and instructs the tailLog to stop when it reaches EOF.

Tested the following conditions:
* Tail and follow logs of running container
* Tail and follow logs of stopped container
* Tail and follow logs of running container which exits after some time

Signed-off-by: jgallucci32 <john.gallucci.iv@gmail.com>
2020-06-12 10:14:35 -07:00

95 lines
2.3 KiB
Go

package libpod
import (
"os"
"time"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/logs"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// Log is a runtime function that can read one or more container logs.
func (r *Runtime) Log(containers []*Container, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
for _, ctr := range containers {
if err := ctr.ReadLog(options, logChannel); err != nil {
return err
}
}
return nil
}
// ReadLog reads a containers log based on the input options and returns loglines over a channel.
func (c *Container) ReadLog(options *logs.LogOptions, logChannel chan *logs.LogLine) error {
// TODO Skip sending logs until journald logs can be read
// TODO make this not a magic string
if c.LogDriver() == define.JournaldLogging {
return c.readFromJournal(options, logChannel)
}
return c.readFromLogFile(options, logChannel)
}
func (c *Container) readFromLogFile(options *logs.LogOptions, logChannel chan *logs.LogLine) error {
t, tailLog, err := logs.GetLogFile(c.LogPath(), options)
if err != nil {
// If the log file does not exist, this is not fatal.
if os.IsNotExist(errors.Cause(err)) {
return nil
}
return errors.Wrapf(err, "unable to read log file %s for %s ", c.ID(), c.LogPath())
}
options.WaitGroup.Add(1)
if len(tailLog) > 0 {
for _, nll := range tailLog {
nll.CID = c.ID()
nll.CName = c.Name()
if nll.Since(options.Since) {
logChannel <- nll
}
}
}
go func() {
var partial string
for line := range t.Lines {
nll, err := logs.NewLogLine(line.Text)
if err != nil {
logrus.Error(err)
continue
}
if nll.Partial() {
partial += nll.Msg
continue
} else if !nll.Partial() && len(partial) > 1 {
nll.Msg = partial + nll.Msg
partial = ""
}
nll.CID = c.ID()
nll.CName = c.Name()
if nll.Since(options.Since) {
logChannel <- nll
}
}
options.WaitGroup.Done()
}()
// Check if container is still running or paused
go func() {
if options.Follow {
for {
state, err := c.State()
if err != nil {
logrus.Error(err)
break
}
if state != define.ContainerStateRunning && state != define.ContainerStatePaused {
t.StopAtEOF()
break
}
time.Sleep(1 * time.Second)
}
}
}()
return nil
}