Files
podman/libpod/container_log.go
Valentin Rothberg f4ba433b18 logs: k8s-file: fix spurious error logs
Fix the suprious "Error: nil" messages.  Also add some more context to
logged error messages which makes error sources more obvious.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2021-06-21 14:11:27 +02:00

135 lines
3.9 KiB
Go

package libpod
import (
"context"
"fmt"
"os"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/events"
"github.com/containers/podman/v3/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(ctx context.Context, containers []*Container, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
for _, ctr := range containers {
if err := ctr.ReadLog(ctx, options, logChannel); err != nil {
return err
}
}
return nil
}
// ReadLog reads a containers log based on the input options and returns log lines over a channel.
func (c *Container) ReadLog(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
switch c.LogDriver() {
case define.NoLogging:
return errors.Wrapf(define.ErrNoLogs, "this container is using the 'none' log driver, cannot read logs")
case define.JournaldLogging:
return c.readFromJournal(ctx, options, logChannel)
case define.JSONLogging:
// TODO provide a separate implementation of this when Conmon
// has support.
fallthrough
case define.KubernetesLogging, "":
return c.readFromLogFile(ctx, options, logChannel)
default:
return errors.Wrapf(define.ErrInternal, "unrecognized log driver %q, cannot read logs", c.LogDriver())
}
}
func (c *Container) readFromLogFile(ctx context.Context, 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() {
defer options.WaitGroup.Done()
var partial string
for line := range t.Lines {
select {
case <-ctx.Done():
// the consumer has cancelled
return
default:
// fallthrough
}
nll, err := logs.NewLogLine(line.Text)
if err != nil {
logrus.Errorf("Error getting new log line: %v", err)
continue
}
if nll.Partial() {
partial += nll.Msg
continue
} else if !nll.Partial() && len(partial) > 0 {
nll.Msg = partial + nll.Msg
partial = ""
}
nll.CID = c.ID()
nll.CName = c.Name()
if nll.Since(options.Since) {
logChannel <- nll
}
}
}()
// Check if container is still running or paused
if options.Follow {
state, err := c.State()
if err != nil || state != define.ContainerStateRunning {
// If the container isn't running or if we encountered
// an error getting its state, instruct the logger to
// read the file until EOF.
tailError := t.StopAtEOF()
if tailError != nil && fmt.Sprintf("%v", tailError) != "tail: stop at eof" {
logrus.Errorf("Error stopping logger: %v", tailError)
}
if err != nil && errors.Cause(err) != define.ErrNoSuchCtr {
logrus.Errorf("Error getting container state: %v", err)
}
return nil
}
// The container is running, so we need to wait until the container exited
go func() {
eventChannel := make(chan *events.Event)
eventOptions := events.ReadOptions{
EventChannel: eventChannel,
Filters: []string{"event=died", "container=" + c.ID()},
Stream: true,
}
go func() {
if err := c.runtime.Events(ctx, eventOptions); err != nil {
logrus.Errorf("Error waiting for container to exit: %v", err)
}
}()
// Now wait for the died event and signal to finish
// reading the log until EOF.
<-eventChannel
tailError := t.StopAtEOF()
if tailError != nil && fmt.Sprintf("%v", tailError) != "tail: stop at eof" {
logrus.Errorf("Error stopping logger: %v", tailError)
}
}()
}
return nil
}