Merge pull request #10996 from cdoern/untilLog

Implemented --until flag for Libpod's Container Logs
This commit is contained in:
OpenShift Merge Robot
2021-07-24 05:56:18 -04:00
committed by GitHub
8 changed files with 129 additions and 17 deletions

View File

@ -19,6 +19,8 @@ type logsOptionsWrapper struct {
entities.ContainerLogsOptions entities.ContainerLogsOptions
SinceRaw string SinceRaw string
UntilRaw string
} }
var ( var (
@ -101,6 +103,10 @@ func logsFlags(cmd *cobra.Command) {
flags.StringVar(&logsOptions.SinceRaw, sinceFlagName, "", "Show logs since TIMESTAMP") flags.StringVar(&logsOptions.SinceRaw, sinceFlagName, "", "Show logs since TIMESTAMP")
_ = cmd.RegisterFlagCompletionFunc(sinceFlagName, completion.AutocompleteNone) _ = cmd.RegisterFlagCompletionFunc(sinceFlagName, completion.AutocompleteNone)
untilFlagName := "until"
flags.StringVar(&logsOptions.UntilRaw, untilFlagName, "", "Show logs until TIMESTAMP")
_ = cmd.RegisterFlagCompletionFunc(untilFlagName, completion.AutocompleteNone)
tailFlagName := "tail" 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") 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) _ = cmd.RegisterFlagCompletionFunc(tailFlagName, completion.AutocompleteNone)
@ -120,6 +126,14 @@ func logs(_ *cobra.Command, args []string) error {
} }
logsOptions.Since = since 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.StdoutWriter = os.Stdout
logsOptions.StderrWriter = os.Stderr logsOptions.StderrWriter = os.Stderr
return registry.ContainerEngine().ContainerLogs(registry.GetContext(), args, logsOptions.ContainerLogsOptions) return registry.ContainerEngine().ContainerLogs(registry.GetContext(), args, logsOptions.ContainerLogsOptions)

View File

@ -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, time stamps include RFC3339Nano, RFC3339, 2006-01-02T15:04:05, 2006-01-02T15:04:05.999999999, 2006-01-02Z07:00,
and 2006-01-02. 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* #### **--tail**=*LINES*
Output the specified number of LINES at the end of the logs. LINES must be an integer. Defaults to -1, 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 # 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: To view a containers logs since a certain time:
``` ```
podman logs -t --since 2017-08-07T10:10:09.055837383-04:00 myserver 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'. # 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 ## SEE ALSO
podman(1), podman-run(1), podman-container-rm(1) podman(1), podman-run(1), podman-container-rm(1)

View File

@ -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 tailQueue := []*logs.LogLine{} // needed for options.Tail
doTail := options.Tail > 0 doTail := options.Tail > 0
for { for {
@ -150,21 +148,10 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
return return
} }
if !afterTimeStamp { entryTime := time.Unix(0, int64(entry.RealtimeTimestamp)*int64(time.Microsecond))
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()) {
if entryTime.Before(options.Since) { continue
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
}
// If we're reading an event and the container exited/died, // If we're reading an event and the container exited/died,
// then we're done and can return. // then we're done and can return.
event, ok := entry.Fields["PODMAN_EVENT"] event, ok := entry.Fields["PODMAN_EVENT"]

View File

@ -242,6 +242,8 @@ type ContainerLogsOptions struct {
Names bool Names bool
// Show logs since this timestamp. // Show logs since this timestamp.
Since time.Time Since time.Time
// Show logs until this timestamp.
Until time.Time
// Number of lines to display at the end of the output. // Number of lines to display at the end of the output.
Tail int64 Tail int64
// Show timestamps in the logs. // Show timestamps in the logs.

View File

@ -998,6 +998,7 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin
Details: options.Details, Details: options.Details,
Follow: options.Follow, Follow: options.Follow,
Since: options.Since, Since: options.Since,
Until: options.Until,
Tail: options.Tail, Tail: options.Tail,
Timestamps: options.Timestamps, Timestamps: options.Timestamps,
UseName: options.Names, UseName: options.Names,

View File

@ -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 { func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, opts entities.ContainerLogsOptions) error {
since := opts.Since.Format(time.RFC3339) since := opts.Since.Format(time.RFC3339)
until := opts.Until.Format(time.RFC3339)
tail := strconv.FormatInt(opts.Tail, 10) tail := strconv.FormatInt(opts.Tail, 10)
stdout := opts.StdoutWriter != nil stdout := opts.StdoutWriter != nil
stderr := opts.StderrWriter != 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) options.WithStdout(stdout).WithTail(tail)
var err error var err error

View File

@ -5,6 +5,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"time"
. "github.com/containers/podman/v3/test/utils" . "github.com/containers/podman/v3/test/utils"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
@ -135,6 +136,34 @@ var _ = Describe("Podman logs", func() {
Expect(len(results.OutputToStringArray())).To(Equal(3)) 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() { It("latest and container name should fail: "+log, func() {
results := podmanTest.Podman([]string{"logs", "-l", "foobar"}) results := podmanTest.Podman([]string{"logs", "-l", "foobar"})
results.WaitWithDefaultTimeout() results.WaitWithDefaultTimeout()

View File

@ -24,6 +24,9 @@ load helpers
# test --since with Unix timestamps # test --since with Unix timestamps
run_podman logs --since 1000 $cid run_podman logs --since 1000 $cid
# test --until with Unix timestamps
run_podman logs --until 1000 $cid
run_podman rm $cid run_podman rm $cid
} }
@ -125,4 +128,50 @@ $s_after"
_log_test_since journald _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 # vim: filetype=sh