mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
Merge pull request #10996 from cdoern/untilLog
Implemented --until flag for Libpod's Container Logs
This commit is contained in:
@ -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)
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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) {
|
if (entryTime.Before(options.Since) && !options.Since.IsZero()) || (entryTime.After(options.Until) && !options.Until.IsZero()) {
|
||||||
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"]
|
||||||
|
@ -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.
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user