github.com/AbhinandanKurakure/podman/v3@v3.4.10/libpod/container_log.go (about)

     1  package libpod
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/containers/podman/v3/libpod/define"
    10  	"github.com/containers/podman/v3/libpod/events"
    11  	"github.com/containers/podman/v3/libpod/logs"
    12  	"github.com/hpcloud/tail/watch"
    13  	"github.com/pkg/errors"
    14  	"github.com/sirupsen/logrus"
    15  )
    16  
    17  // logDrivers stores the currently available log drivers, do not modify
    18  var logDrivers []string
    19  
    20  func init() {
    21  	logDrivers = append(logDrivers, define.KubernetesLogging, define.NoLogging)
    22  }
    23  
    24  // Log is a runtime function that can read one or more container logs.
    25  func (r *Runtime) Log(ctx context.Context, containers []*Container, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
    26  	for _, ctr := range containers {
    27  		if err := ctr.ReadLog(ctx, options, logChannel); err != nil {
    28  			return err
    29  		}
    30  	}
    31  	return nil
    32  }
    33  
    34  // ReadLog reads a containers log based on the input options and returns log lines over a channel.
    35  func (c *Container) ReadLog(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
    36  	switch c.LogDriver() {
    37  	case define.NoLogging:
    38  		return errors.Wrapf(define.ErrNoLogs, "this container is using the 'none' log driver, cannot read logs")
    39  	case define.JournaldLogging:
    40  		return c.readFromJournal(ctx, options, logChannel)
    41  	case define.JSONLogging:
    42  		// TODO provide a separate implementation of this when Conmon
    43  		// has support.
    44  		fallthrough
    45  	case define.KubernetesLogging, "":
    46  		return c.readFromLogFile(ctx, options, logChannel)
    47  	default:
    48  		return errors.Wrapf(define.ErrInternal, "unrecognized log driver %q, cannot read logs", c.LogDriver())
    49  	}
    50  }
    51  
    52  func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
    53  	t, tailLog, err := logs.GetLogFile(c.LogPath(), options)
    54  	if err != nil {
    55  		// If the log file does not exist, this is not fatal.
    56  		if os.IsNotExist(errors.Cause(err)) {
    57  			return nil
    58  		}
    59  		return errors.Wrapf(err, "unable to read log file %s for %s ", c.ID(), c.LogPath())
    60  	}
    61  	options.WaitGroup.Add(1)
    62  	if len(tailLog) > 0 {
    63  		for _, nll := range tailLog {
    64  			nll.CID = c.ID()
    65  			nll.CName = c.Name()
    66  			if nll.Since(options.Since) && nll.Until(options.Until) {
    67  				logChannel <- nll
    68  			}
    69  		}
    70  	}
    71  
    72  	go func() {
    73  		defer options.WaitGroup.Done()
    74  
    75  		var partial string
    76  		for line := range t.Lines {
    77  			select {
    78  			case <-ctx.Done():
    79  				// the consumer has cancelled
    80  				return
    81  			default:
    82  				// fallthrough
    83  			}
    84  			nll, err := logs.NewLogLine(line.Text)
    85  			if err != nil {
    86  				logrus.Errorf("Error getting new log line: %v", err)
    87  				continue
    88  			}
    89  			if nll.Partial() {
    90  				partial += nll.Msg
    91  				continue
    92  			} else if !nll.Partial() && len(partial) > 0 {
    93  				nll.Msg = partial + nll.Msg
    94  				partial = ""
    95  			}
    96  			nll.CID = c.ID()
    97  			nll.CName = c.Name()
    98  			if nll.Since(options.Since) && nll.Until(options.Until) {
    99  				logChannel <- nll
   100  			}
   101  		}
   102  	}()
   103  	// Check if container is still running or paused
   104  	if options.Follow {
   105  		// If the container isn't running or if we encountered an error
   106  		// getting its state, instruct the logger to read the file
   107  		// until EOF.
   108  		state, err := c.State()
   109  		if err != nil || state != define.ContainerStateRunning {
   110  			if err != nil && errors.Cause(err) != define.ErrNoSuchCtr {
   111  				logrus.Errorf("Error getting container state: %v", err)
   112  			}
   113  			go func() {
   114  				// Make sure to wait at least for the poll duration
   115  				// before stopping the file logger (see #10675).
   116  				time.Sleep(watch.POLL_DURATION)
   117  				tailError := t.StopAtEOF()
   118  				if tailError != nil && tailError.Error() != "tail: stop at eof" {
   119  					logrus.Errorf("Error stopping logger: %v", tailError)
   120  				}
   121  			}()
   122  			return nil
   123  		}
   124  
   125  		// The container is running, so we need to wait until the container exited
   126  		go func() {
   127  			eventChannel := make(chan *events.Event)
   128  			eventOptions := events.ReadOptions{
   129  				EventChannel: eventChannel,
   130  				Filters:      []string{"event=died", "container=" + c.ID()},
   131  				Stream:       true,
   132  			}
   133  			go func() {
   134  				if err := c.runtime.Events(ctx, eventOptions); err != nil {
   135  					logrus.Errorf("Error waiting for container to exit: %v", err)
   136  				}
   137  			}()
   138  			// Now wait for the died event and signal to finish
   139  			// reading the log until EOF.
   140  			<-eventChannel
   141  			// Make sure to wait at least for the poll duration
   142  			// before stopping the file logger (see #10675).
   143  			time.Sleep(watch.POLL_DURATION)
   144  			tailError := t.StopAtEOF()
   145  			if tailError != nil && fmt.Sprintf("%v", tailError) != "tail: stop at eof" {
   146  				logrus.Errorf("Error stopping logger: %v", tailError)
   147  			}
   148  		}()
   149  	}
   150  	return nil
   151  }