mirror of
https://github.com/containers/podman.git
synced 2025-06-27 05:26:50 +08:00
podman logs honor stderr correctly
Make the ContainerLogsOptions support two io.Writers, one for stdout and the other for stderr. The logline already includes the information to which Writer it has to be written. Signed-off-by: Paul Holzinger <paul.holzinger@web.de>
This commit is contained in:
@ -122,6 +122,7 @@ func logs(_ *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
logsOptions.Since = since
|
logsOptions.Since = since
|
||||||
}
|
}
|
||||||
logsOptions.Writer = os.Stdout
|
logsOptions.StdoutWriter = os.Stdout
|
||||||
|
logsOptions.StderrWriter = os.Stderr
|
||||||
return registry.ContainerEngine().ContainerLogs(registry.GetContext(), args, logsOptions.ContainerLogsOptions)
|
return registry.ContainerEngine().ContainerLogs(registry.GetContext(), args, logsOptions.ContainerLogsOptions)
|
||||||
}
|
}
|
||||||
|
@ -210,3 +210,19 @@ func NewLogLine(line string) (*LogLine, error) {
|
|||||||
func (l *LogLine) Partial() bool {
|
func (l *LogLine) Partial() bool {
|
||||||
return l.ParseLogType == PartialLogType
|
return l.ParseLogType == PartialLogType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *LogLine) Write(stdout io.Writer, stderr io.Writer, logOpts *LogOptions) {
|
||||||
|
switch l.Device {
|
||||||
|
case "stdout":
|
||||||
|
if stdout != nil {
|
||||||
|
fmt.Fprintln(stdout, l.String(logOpts))
|
||||||
|
}
|
||||||
|
case "stderr":
|
||||||
|
if stderr != nil {
|
||||||
|
fmt.Fprintln(stderr, l.String(logOpts))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Warn the user if the device type does not match. Most likely the file is corrupted.
|
||||||
|
logrus.Warnf("unknown Device type '%s' in log file from Container %s", l.Device, l.CID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -227,8 +227,10 @@ type ContainerLogsOptions struct {
|
|||||||
Tail int64
|
Tail int64
|
||||||
// Show timestamps in the logs.
|
// Show timestamps in the logs.
|
||||||
Timestamps bool
|
Timestamps bool
|
||||||
// Write the logs to Writer.
|
// Write the stdout to this Writer.
|
||||||
Writer io.Writer
|
StdoutWriter io.Writer
|
||||||
|
// Write the stderr to this Writer.
|
||||||
|
StderrWriter io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecOptions describes the cli values to exec into
|
// ExecOptions describes the cli values to exec into
|
||||||
|
@ -925,7 +925,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error {
|
func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error {
|
||||||
if options.Writer == nil {
|
if options.StdoutWriter == nil && options.StderrWriter == nil {
|
||||||
return errors.New("no io.Writer set for container logs")
|
return errors.New("no io.Writer set for container logs")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -963,7 +963,7 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
for line := range logChannel {
|
for line := range logChannel {
|
||||||
fmt.Fprintln(options.Writer, line.String(logOpts))
|
line.Write(options.StdoutWriter, options.StderrWriter, logOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -360,11 +360,12 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG
|
|||||||
func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, options entities.ContainerLogsOptions) error {
|
func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, options entities.ContainerLogsOptions) error {
|
||||||
since := options.Since.Format(time.RFC3339)
|
since := options.Since.Format(time.RFC3339)
|
||||||
tail := strconv.FormatInt(options.Tail, 10)
|
tail := strconv.FormatInt(options.Tail, 10)
|
||||||
stdout := options.Writer != nil
|
stdout := options.StdoutWriter != nil
|
||||||
|
stderr := options.StderrWriter != nil
|
||||||
opts := containers.LogOptions{
|
opts := containers.LogOptions{
|
||||||
Follow: &options.Follow,
|
Follow: &options.Follow,
|
||||||
Since: &since,
|
Since: &since,
|
||||||
Stderr: &stdout,
|
Stderr: &stderr,
|
||||||
Stdout: &stdout,
|
Stdout: &stdout,
|
||||||
Tail: &tail,
|
Tail: &tail,
|
||||||
Timestamps: &options.Timestamps,
|
Timestamps: &options.Timestamps,
|
||||||
@ -372,10 +373,11 @@ func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
outCh := make(chan string)
|
stdoutCh := make(chan string)
|
||||||
|
stderrCh := make(chan string)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
go func() {
|
go func() {
|
||||||
err = containers.Logs(ic.ClientCxt, nameOrIDs[0], opts, outCh, outCh)
|
err = containers.Logs(ic.ClientCxt, nameOrIDs[0], opts, stdoutCh, stderrCh)
|
||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -383,8 +385,14 @@ func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string,
|
|||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return err
|
return err
|
||||||
case line := <-outCh:
|
case line := <-stdoutCh:
|
||||||
_, _ = io.WriteString(options.Writer, line+"\n")
|
if options.StdoutWriter != nil {
|
||||||
|
_, _ = io.WriteString(options.StdoutWriter, line+"\n")
|
||||||
|
}
|
||||||
|
case line := <-stderrCh:
|
||||||
|
if options.StderrWriter != nil {
|
||||||
|
_, _ = io.WriteString(options.StderrWriter, line+"\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,4 +355,21 @@ var _ = Describe("Podman logs", func() {
|
|||||||
Expect(outlines[0]).To(Equal("1\r"))
|
Expect(outlines[0]).To(Equal("1\r"))
|
||||||
Expect(outlines[1]).To(Equal("2\r"))
|
Expect(outlines[1]).To(Equal("2\r"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("podman logs test stdout and stderr", func() {
|
||||||
|
cname := "log-test"
|
||||||
|
logc := podmanTest.Podman([]string{"run", "--name", cname, ALPINE, "sh", "-c", "echo stdout; echo stderr >&2"})
|
||||||
|
logc.WaitWithDefaultTimeout()
|
||||||
|
Expect(logc).To(Exit(0))
|
||||||
|
|
||||||
|
wait := podmanTest.Podman([]string{"wait", cname})
|
||||||
|
wait.WaitWithDefaultTimeout()
|
||||||
|
Expect(wait).To(Exit(0))
|
||||||
|
|
||||||
|
results := podmanTest.Podman([]string{"logs", cname})
|
||||||
|
results.WaitWithDefaultTimeout()
|
||||||
|
Expect(results).To(Exit(0))
|
||||||
|
Expect(results.OutputToString()).To(Equal("stdout"))
|
||||||
|
Expect(results.ErrorToString()).To(Equal("stderr"))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1072,7 +1072,7 @@ var _ = Describe("Podman play kube", func() {
|
|||||||
logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(pod)})
|
logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(pod)})
|
||||||
logs.WaitWithDefaultTimeout()
|
logs.WaitWithDefaultTimeout()
|
||||||
Expect(logs.ExitCode()).To(Equal(0))
|
Expect(logs.ExitCode()).To(Equal(0))
|
||||||
Expect(logs.OutputToString()).To(ContainSubstring("Operation not permitted"))
|
Expect(logs.ErrorToString()).To(ContainSubstring("Operation not permitted"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman play kube seccomp pod level", func() {
|
It("podman play kube seccomp pod level", func() {
|
||||||
@ -1099,7 +1099,7 @@ var _ = Describe("Podman play kube", func() {
|
|||||||
logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(pod)})
|
logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(pod)})
|
||||||
logs.WaitWithDefaultTimeout()
|
logs.WaitWithDefaultTimeout()
|
||||||
Expect(logs.ExitCode()).To(Equal(0))
|
Expect(logs.ExitCode()).To(Equal(0))
|
||||||
Expect(logs.OutputToString()).To(ContainSubstring("Operation not permitted"))
|
Expect(logs.ErrorToString()).To(ContainSubstring("Operation not permitted"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman play kube with pull policy of never should be 125", func() {
|
It("podman play kube with pull policy of never should be 125", func() {
|
||||||
|
@ -239,7 +239,7 @@ var _ = Describe("Toolbox-specific testing", func() {
|
|||||||
session = podmanTest.Podman([]string{"logs", "test"})
|
session = podmanTest.Podman([]string{"logs", "test"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session.ExitCode()).To(Equal(0))
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
Expect(session.OutputToString()).To(ContainSubstring(expectedOutput))
|
Expect(session.ErrorToString()).To(ContainSubstring(expectedOutput))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman create --userns=keep-id + podman exec - adding group with groupadd", func() {
|
It("podman create --userns=keep-id + podman exec - adding group with groupadd", func() {
|
||||||
|
Reference in New Issue
Block a user