diff --git a/libpod/container_log.go b/libpod/container_log.go index e33ede5dd1..680adfd389 100644 --- a/libpod/container_log.go +++ b/libpod/container_log.go @@ -10,6 +10,7 @@ import ( "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/libpod/events" "github.com/containers/podman/v4/libpod/logs" + systemdDefine "github.com/containers/podman/v4/pkg/systemd/define" "github.com/nxadm/tail" "github.com/nxadm/tail/watch" "github.com/sirupsen/logrus" @@ -36,11 +37,15 @@ func (r *Runtime) Log(ctx context.Context, containers []*Container, options *log func (c *Container) ReadLog(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine, colorID int64) error { switch c.LogDriver() { case define.PassthroughLogging: + // if running under systemd fallback to a more native journald reading + if _, ok := c.config.Labels[systemdDefine.EnvVariable]; ok { + return c.readFromJournal(ctx, options, logChannel, colorID, true) + } return fmt.Errorf("this container is using the 'passthrough' log driver, cannot read logs: %w", define.ErrNoLogs) case define.NoLogging: return fmt.Errorf("this container is using the 'none' log driver, cannot read logs: %w", define.ErrNoLogs) case define.JournaldLogging: - return c.readFromJournal(ctx, options, logChannel, colorID) + return c.readFromJournal(ctx, options, logChannel, colorID, false) case define.JSONLogging: // TODO provide a separate implementation of this when Conmon // has support. diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go index dc305fabfd..1e4e5c198a 100644 --- a/libpod/container_log_linux.go +++ b/libpod/container_log_linux.go @@ -31,7 +31,8 @@ func init() { logDrivers = append(logDrivers, define.JournaldLogging) } -func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine, colorID int64) error { +func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOptions, + logChannel chan *logs.LogLine, colorID int64, isPassthrough bool) error { // We need the container's events in the same journal to guarantee // consistency, see #10323. if options.Follow && c.runtime.config.Engine.EventsLogger != "journald" { @@ -67,10 +68,28 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption if err := journal.AddDisjunction(); err != nil { return fmt.Errorf("adding filter disjunction to journald logger: %w", err) } - match = sdjournal.Match{Field: "CONTAINER_ID_FULL", Value: c.ID()} - if err := journal.AddMatch(match.String()); err != nil { - return fmt.Errorf("adding filter to journald logger: %v: %w", match, err) + + if isPassthrough { + // Match based on systemd unit which is the container is cgroup + // so we get the exact logs for a single container even in the + // play kube case where a single unit starts more than one container. + unitTypeName := "_SYSTEMD_UNIT" + if rootless.IsRootless() { + unitTypeName = "_SYSTEMD_USER_UNIT" + } + // FIXME: It looks like it is hardcoded to libpod-ID.scope but I am not + // sure were we set this with all the different cgroup modes??? + match = sdjournal.Match{Field: unitTypeName, Value: "libpod-" + c.ID() + ".scope"} + if err := journal.AddMatch(match.String()); err != nil { + return fmt.Errorf("adding filter to journald logger: %v: %w", match, err) + } + } else { + match = sdjournal.Match{Field: "CONTAINER_ID_FULL", Value: c.ID()} + if err := journal.AddMatch(match.String()); err != nil { + return fmt.Errorf("adding filter to journald logger: %v: %w", match, err) + } } + if err := journal.AddMatch(uidMatch.String()); err != nil { return fmt.Errorf("adding filter to journald logger: %v: %w", uidMatch, err) } diff --git a/libpod/container_log_unsupported.go b/libpod/container_log_unsupported.go index cdce820b4f..736378a6b7 100644 --- a/libpod/container_log_unsupported.go +++ b/libpod/container_log_unsupported.go @@ -11,6 +11,6 @@ import ( "github.com/containers/podman/v4/libpod/logs" ) -func (c *Container) readFromJournal(_ context.Context, _ *logs.LogOptions, _ chan *logs.LogLine, colorID int64) error { +func (c *Container) readFromJournal(_ context.Context, _ *logs.LogOptions, _ chan *logs.LogLine, _ int64, _ bool) error { return fmt.Errorf("journald logging only enabled with systemd on linux: %w", define.ErrOSNotSupported) } diff --git a/test/system/250-systemd.bats b/test/system/250-systemd.bats index dad551b007..8fab04bb29 100644 --- a/test/system/250-systemd.bats +++ b/test/system/250-systemd.bats @@ -383,11 +383,15 @@ metadata: spec: containers: - command: - - top + - sh + - -c + - echo a stdout; echo a stderr 1>&2; sleep inf image: $IMAGE name: a - command: - - top + - sh + - -c + - echo b stdout; echo b stderr 1>&2; sleep inf image: $IMAGE name: b EOF @@ -418,6 +422,10 @@ EOF for name in "a" "b"; do run_podman container inspect test_pod-${name} --format "{{.HostConfig.LogConfig.Type}}" assert $output != "passthrough" + # check that we can get the logs with passthrough when we run in a systemd unit + run_podman logs test_pod-$name + assert "$output" == "$name stdout +$name stderr" "logs work with passthrough" done # Add a simple `auto-update --dry-run` test here to avoid too much redundancy