12
"github.com/containers/podman/v5/libpod/define"
13
"github.com/containers/podman/v5/libpod/events"
14
"github.com/containers/podman/v5/libpod/logs"
15
systemdDefine "github.com/containers/podman/v5/pkg/systemd/define"
16
"github.com/nxadm/tail"
17
"github.com/nxadm/tail/watch"
18
"github.com/sirupsen/logrus"
21
// logDrivers stores the currently available log drivers, do not modify
22
var logDrivers []string
25
logDrivers = append(logDrivers, define.KubernetesLogging, define.NoLogging, define.PassthroughLogging)
28
// Log is a runtime function that can read one or more container logs.
29
func (r *Runtime) Log(ctx context.Context, containers []*Container, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
30
for c, ctr := range containers {
31
if err := ctr.ReadLog(ctx, options, logChannel, int64(c)); err != nil {
38
// ReadLog reads a container's log based on the input options and returns log lines over a channel.
39
func (c *Container) ReadLog(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine, colorID int64) error {
40
switch c.LogDriver() {
41
case define.PassthroughLogging:
42
// if running under systemd fallback to a more native journald reading
43
if unitName, ok := c.config.Labels[systemdDefine.EnvVariable]; ok {
44
return c.readFromJournal(ctx, options, logChannel, colorID, unitName)
46
return fmt.Errorf("this container is using the 'passthrough' log driver, cannot read logs: %w", define.ErrNoLogs)
47
case define.NoLogging:
48
return fmt.Errorf("this container is using the 'none' log driver, cannot read logs: %w", define.ErrNoLogs)
49
case define.JournaldLogging:
50
return c.readFromJournal(ctx, options, logChannel, colorID, "")
51
case define.JSONLogging:
52
// TODO provide a separate implementation of this when Conmon
55
case define.KubernetesLogging, "":
56
return c.readFromLogFile(ctx, options, logChannel, colorID)
58
return fmt.Errorf("unrecognized log driver %q, cannot read logs: %w", c.LogDriver(), define.ErrInternal)
62
func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine, colorID int64) error {
63
t, tailLog, err := logs.GetLogFile(c.LogPath(), options)
65
// If the log file does not exist, this is not fatal.
66
if errors.Is(err, os.ErrNotExist) {
69
return fmt.Errorf("unable to read log file %s for %s : %w", c.ID(), c.LogPath(), err)
71
options.WaitGroup.Add(1)
73
if options.Until.After(time.Now()) {
74
time.Sleep(time.Until(options.Until))
75
if err := t.Stop(); err != nil {
76
logrus.Errorf("Stopping logger: %v", err)
82
for _, nll := range tailLog {
86
if nll.Since(options.Since) && nll.Until(options.Until) {
90
defer options.WaitGroup.Done()
96
// the consumer has cancelled
97
t.Kill(errors.New("hangup by client"))
99
case line, ok = <-t.Lines:
101
// channel was closed
105
nll, err := logs.NewLogLine(line.Text)
107
logrus.Errorf("Getting new log line: %v", err)
112
nll.ColorID = colorID
113
if nll.Since(options.Since) && nll.Until(options.Until) {
118
// Check if container is still running or paused
120
// If the container isn't running or if we encountered an error
121
// getting its state, instruct the logger to read the file
123
state, err := c.State()
124
if err != nil || state != define.ContainerStateRunning {
125
if err != nil && !errors.Is(err, define.ErrNoSuchCtr) {
126
logrus.Errorf("Getting container state: %v", err)
129
// Make sure to wait at least for the poll duration
130
// before stopping the file logger (see #10675).
131
time.Sleep(watch.POLL_DURATION)
132
tailError := t.StopAtEOF()
133
if tailError != nil && tailError.Error() != "tail: stop at eof" {
134
logrus.Errorf("Stopping logger: %v", tailError)
140
// The container is running, so we need to wait until the container exited
142
eventChannel := make(chan *events.Event)
143
eventOptions := events.ReadOptions{
144
EventChannel: eventChannel,
145
Filters: []string{"event=died", "container=" + c.ID()},
149
if err := c.runtime.Events(ctx, eventOptions); err != nil {
150
logrus.Errorf("Waiting for container to exit: %v", err)
153
// Now wait for the died event and signal to finish
154
// reading the log until EOF.
156
// Make sure to wait at least for the poll duration
157
// before stopping the file logger (see #10675).
158
time.Sleep(watch.POLL_DURATION)
159
tailError := t.StopAtEOF()
160
if tailError != nil && tailError.Error() != "tail: stop at eof" {
161
logrus.Errorf("Stopping logger: %v", tailError)