summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/containers/logs.go14
-rw-r--r--docs/source/markdown/podman-logs.1.md29
-rw-r--r--libpod/container_log_linux.go19
-rw-r--r--pkg/domain/entities/containers.go2
-rw-r--r--pkg/domain/infra/abi/containers.go1
-rw-r--r--pkg/domain/infra/tunnel/containers.go3
-rw-r--r--test/e2e/logs_test.go29
-rw-r--r--test/system/035-logs.bats49
8 files changed, 129 insertions, 17 deletions
diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go
index 0d745291e..00a8d4b52 100644
--- a/cmd/podman/containers/logs.go
+++ b/cmd/podman/containers/logs.go
@@ -19,6 +19,8 @@ type logsOptionsWrapper struct {
entities.ContainerLogsOptions
SinceRaw string
+
+ UntilRaw string
}
var (
@@ -101,6 +103,10 @@ func logsFlags(cmd *cobra.Command) {
flags.StringVar(&logsOptions.SinceRaw, sinceFlagName, "", "Show logs since TIMESTAMP")
_ = cmd.RegisterFlagCompletionFunc(sinceFlagName, completion.AutocompleteNone)
+ untilFlagName := "until"
+ flags.StringVar(&logsOptions.UntilRaw, untilFlagName, "", "Show logs until TIMESTAMP")
+ _ = cmd.RegisterFlagCompletionFunc(untilFlagName, completion.AutocompleteNone)
+
tailFlagName := "tail"
flags.Int64Var(&logsOptions.Tail, tailFlagName, -1, "Output the specified number of LINES at the end of the logs. Defaults to -1, which prints all lines")
_ = cmd.RegisterFlagCompletionFunc(tailFlagName, completion.AutocompleteNone)
@@ -120,6 +126,14 @@ func logs(_ *cobra.Command, args []string) error {
}
logsOptions.Since = since
}
+ if logsOptions.UntilRaw != "" {
+ // parse time, error out if something is wrong
+ until, err := util.ParseInputTime(logsOptions.UntilRaw)
+ if err != nil {
+ return errors.Wrapf(err, "error parsing --until %q", logsOptions.UntilRaw)
+ }
+ logsOptions.Until = until
+ }
logsOptions.StdoutWriter = os.Stdout
logsOptions.StderrWriter = os.Stderr
return registry.ContainerEngine().ContainerLogs(registry.GetContext(), args, logsOptions.ContainerLogsOptions)
diff --git a/docs/source/markdown/podman-logs.1.md b/docs/source/markdown/podman-logs.1.md
index 8f1301102..6b3ef79d7 100644
--- a/docs/source/markdown/podman-logs.1.md
+++ b/docs/source/markdown/podman-logs.1.md
@@ -39,6 +39,14 @@ strings (e.g. 10m, 1h30m) computed relative to the client machine's time. Suppor
time stamps include RFC3339Nano, RFC3339, 2006-01-02T15:04:05, 2006-01-02T15:04:05.999999999, 2006-01-02Z07:00,
and 2006-01-02.
+#### **--until**=*TIMESTAMP*
+
+Show logs until TIMESTAMP. The --until option can be Unix timestamps, date formatted timestamps, or Go duration
+strings (e.g. 10m, 1h30m) computed relative to the client machine's time. Supported formats for date formatted
+time stamps include RFC3339Nano, RFC3339, 2006-01-02T15:04:05, 2006-01-02T15:04:05.999999999, 2006-01-02Z07:00,
+and 2006-01-02.
+
+
#### **--tail**=*LINES*
Output the specified number of LINES at the end of the logs. LINES must be an integer. Defaults to -1,
@@ -74,6 +82,17 @@ podman logs --tail 2 b3f2436bdb97
# Server initialized
```
+To view all containers logs:
+```
+podman logs -t --since 0 myserver
+
+1:M 07 Aug 14:10:09.055 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
+1:M 07 Aug 14:10:09.055 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
+1:M 07 Aug 14:10:09.056 * Running mode=standalone, port=6379.
+1:M 07 Aug 14:10:09.056 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
+1:M 07 Aug 14:10:09.056 # Server initialized
+```
+
To view a containers logs since a certain time:
```
podman logs -t --since 2017-08-07T10:10:09.055837383-04:00 myserver
@@ -93,6 +112,16 @@ podman logs --since 10m myserver
# Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
```
+To view a container's logs until 30 minutes ago:
+```
+podman logs --until 30m myserver
+
+AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.0.2.100. Set the 'ServerName' directive globally to suppress this message
+AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.0.2.100. Set the 'ServerName' directive globally to suppress this message
+[Tue Jul 20 13:18:14.223727 2021] [mpm_event:notice] [pid 1:tid 140021067187328] AH00489: Apache/2.4.48 (Unix) configured -- resuming normal operations
+[Tue Jul 20 13:18:14.223819 2021] [core:notice] [pid 1:tid 140021067187328] AH00094: Command line: 'httpd -D FOREGROUND'
+```
+
## SEE ALSO
podman(1), podman-run(1), podman-container-rm(1)
diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go
index 9f9dd3b0d..d4afaa52a 100644
--- a/libpod/container_log_linux.go
+++ b/libpod/container_log_linux.go
@@ -97,8 +97,6 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
}
}()
- beforeTimeStamp := true
- afterTimeStamp := false // needed for options.Since
tailQueue := []*logs.LogLine{} // needed for options.Tail
doTail := options.Tail > 0
for {
@@ -150,21 +148,10 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
return
}
- if !afterTimeStamp {
- entryTime := time.Unix(0, int64(entry.RealtimeTimestamp)*int64(time.Microsecond))
- if entryTime.Before(options.Since) {
- continue
- }
- afterTimeStamp = true
- }
- if beforeTimeStamp {
- entryTime := time.Unix(0, int64(entry.RealtimeTimestamp)*int64(time.Microsecond))
- if entryTime.Before(options.Until) || !options.Until.IsZero() {
- continue
- }
- beforeTimeStamp = false
+ entryTime := time.Unix(0, int64(entry.RealtimeTimestamp)*int64(time.Microsecond))
+ if (entryTime.Before(options.Since) && !options.Since.IsZero()) || (entryTime.After(options.Until) && !options.Until.IsZero()) {
+ continue
}
-
// If we're reading an event and the container exited/died,
// then we're done and can return.
event, ok := entry.Fields["PODMAN_EVENT"]
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 302b35a47..4d85941cd 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -242,6 +242,8 @@ type ContainerLogsOptions struct {
Names bool
// Show logs since this timestamp.
Since time.Time
+ // Show logs until this timestamp.
+ Until time.Time
// Number of lines to display at the end of the output.
Tail int64
// Show timestamps in the logs.
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 50751aa12..485ab944d 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -998,6 +998,7 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin
Details: options.Details,
Follow: options.Follow,
Since: options.Since,
+ Until: options.Until,
Tail: options.Tail,
Timestamps: options.Timestamps,
UseName: options.Names,
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index c17d7b54f..d7e8a4e46 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -369,10 +369,11 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG
func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, opts entities.ContainerLogsOptions) error {
since := opts.Since.Format(time.RFC3339)
+ until := opts.Until.Format(time.RFC3339)
tail := strconv.FormatInt(opts.Tail, 10)
stdout := opts.StdoutWriter != nil
stderr := opts.StderrWriter != nil
- options := new(containers.LogOptions).WithFollow(opts.Follow).WithSince(since).WithStderr(stderr)
+ options := new(containers.LogOptions).WithFollow(opts.Follow).WithSince(since).WithUntil(until).WithStderr(stderr)
options.WithStdout(stdout).WithTail(tail)
var err error
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index 507fab461..0a973b802 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -5,6 +5,7 @@ import (
"os"
"os/exec"
"strings"
+ "time"
. "github.com/containers/podman/v3/test/utils"
. "github.com/onsi/ginkgo"
@@ -135,6 +136,34 @@ var _ = Describe("Podman logs", func() {
Expect(len(results.OutputToStringArray())).To(Equal(3))
})
+ It("until duration 10m: "+log, func() {
+ logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
+ logc.WaitWithDefaultTimeout()
+ Expect(logc).To(Exit(0))
+ cid := logc.OutputToString()
+
+ results := podmanTest.Podman([]string{"logs", "--until", "10m", cid})
+ results.WaitWithDefaultTimeout()
+ Expect(results).To(Exit(0))
+ Expect(len(results.OutputToStringArray())).To(Equal(0))
+ })
+
+ It("until time NOW: "+log, func() {
+
+ logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
+ logc.WaitWithDefaultTimeout()
+ Expect(logc).To(Exit(0))
+ cid := logc.OutputToString()
+
+ now := time.Now()
+ now = now.Add(time.Minute * 1)
+ nowS := now.Format(time.RFC3339)
+ results := podmanTest.Podman([]string{"logs", "--until", nowS, cid})
+ results.WaitWithDefaultTimeout()
+ Expect(results).To(Exit(0))
+ Expect(len(results.OutputToStringArray())).To(Equal(3))
+ })
+
It("latest and container name should fail: "+log, func() {
results := podmanTest.Podman([]string{"logs", "-l", "foobar"})
results.WaitWithDefaultTimeout()
diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats
index ccf83df14..32282c8e1 100644
--- a/test/system/035-logs.bats
+++ b/test/system/035-logs.bats
@@ -24,6 +24,9 @@ load helpers
# test --since with Unix timestamps
run_podman logs --since 1000 $cid
+ # test --until with Unix timestamps
+ run_podman logs --until 1000 $cid
+
run_podman rm $cid
}
@@ -125,4 +128,50 @@ $s_after"
_log_test_since journald
}
+function _log_test_until() {
+ local driver=$1
+
+ s_before="before_$(random_string)_${driver}"
+ s_after="after_$(random_string)_${driver}"
+
+ before=$(date --iso-8601=seconds)
+ sleep 5
+ run_podman run --log-driver=$driver -d --name test $IMAGE sh -c \
+ "echo $s_before; trap 'echo $s_after; exit' SIGTERM; while :; do sleep 1; done"
+
+ # sleep a second to make sure the date is after the first echo
+ sleep 1
+ run_podman stop test
+ # sleep for 20 seconds to get the proper after time
+ sleep 20
+
+ run_podman logs test
+ is "$output" \
+ "$s_before
+$s_after"
+
+ run_podman logs --until $before test
+ is "$output" \
+ ""
+
+ after=$(date --iso-8601=seconds)
+
+ run_podman logs --until $after test
+ is "$output" \
+ "$s_before
+$s_after"
+ run_podman rm -f test
+}
+
+@test "podman logs - until k8s-file" {
+ _log_test_until k8s-file
+}
+
+@test "podman logs - until journald" {
+ # We can't use journald on RHEL as rootless: rhbz#1895105
+ skip_if_journald_unavailable
+
+ _log_test_until journald
+}
+
# vim: filetype=sh