mirror of
https://github.com/containers/podman.git
synced 2025-07-15 03:02:52 +08:00

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>
129 lines
4.6 KiB
Go
129 lines
4.6 KiB
Go
package containers
|
|
|
|
import (
|
|
"os"
|
|
|
|
"github.com/containers/common/pkg/completion"
|
|
"github.com/containers/podman/v2/cmd/podman/common"
|
|
"github.com/containers/podman/v2/cmd/podman/registry"
|
|
"github.com/containers/podman/v2/cmd/podman/validate"
|
|
"github.com/containers/podman/v2/pkg/domain/entities"
|
|
"github.com/containers/podman/v2/pkg/util"
|
|
"github.com/pkg/errors"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// logsOptionsWrapper wraps entities.LogsOptions and prevents leaking
|
|
// CLI-only fields into the API types.
|
|
type logsOptionsWrapper struct {
|
|
entities.ContainerLogsOptions
|
|
|
|
SinceRaw string
|
|
}
|
|
|
|
var (
|
|
logsOptions logsOptionsWrapper
|
|
logsDescription = `Retrieves logs for one or more containers.
|
|
|
|
This does not guarantee execution order when combined with podman run (i.e., your run may not have generated any logs at the time you execute podman logs).
|
|
`
|
|
logsCommand = &cobra.Command{
|
|
Use: "logs [options] CONTAINER [CONTAINER...]",
|
|
Short: "Fetch the logs of one or more containers",
|
|
Long: logsDescription,
|
|
Args: func(cmd *cobra.Command, args []string) error {
|
|
switch {
|
|
case registry.IsRemote() && logsOptions.Latest:
|
|
return errors.New(cmd.Name() + " does not support 'latest' when run remotely")
|
|
case registry.IsRemote() && len(args) > 1:
|
|
return errors.New(cmd.Name() + " does not support multiple containers when run remotely")
|
|
case logsOptions.Latest && len(args) > 0:
|
|
return errors.New("--latest and containers cannot be used together")
|
|
case !logsOptions.Latest && len(args) < 1:
|
|
return errors.New("specify at least one container name or ID to log")
|
|
}
|
|
return nil
|
|
},
|
|
RunE: logs,
|
|
ValidArgsFunction: common.AutocompleteContainers,
|
|
Example: `podman logs ctrID
|
|
podman logs --names ctrID1 ctrID2
|
|
podman logs --tail 2 mywebserver
|
|
podman logs --follow=true --since 10m ctrID
|
|
podman logs mywebserver mydbserver`,
|
|
}
|
|
|
|
containerLogsCommand = &cobra.Command{
|
|
Use: logsCommand.Use,
|
|
Short: logsCommand.Short,
|
|
Long: logsCommand.Long,
|
|
Args: logsCommand.Args,
|
|
RunE: logsCommand.RunE,
|
|
ValidArgsFunction: logsCommand.ValidArgsFunction,
|
|
Example: `podman container logs ctrID
|
|
podman container logs --names ctrID1 ctrID2
|
|
podman container logs --tail 2 mywebserver
|
|
podman container logs --follow=true --since 10m ctrID
|
|
podman container logs mywebserver mydbserver`,
|
|
}
|
|
)
|
|
|
|
func init() {
|
|
// if run remotely we only allow one container arg
|
|
if registry.IsRemote() {
|
|
logsCommand.Use = "logs [options] CONTAINER"
|
|
containerLogsCommand.Use = logsCommand.Use
|
|
}
|
|
|
|
// logs
|
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
|
Command: logsCommand,
|
|
})
|
|
logsFlags(logsCommand)
|
|
validate.AddLatestFlag(logsCommand, &logsOptions.Latest)
|
|
|
|
// container logs
|
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
|
Command: containerLogsCommand,
|
|
Parent: containerCmd,
|
|
})
|
|
logsFlags(containerLogsCommand)
|
|
validate.AddLatestFlag(containerLogsCommand, &logsOptions.Latest)
|
|
}
|
|
|
|
func logsFlags(cmd *cobra.Command) {
|
|
flags := cmd.Flags()
|
|
|
|
flags.BoolVar(&logsOptions.Details, "details", false, "Show extra details provided to the logs")
|
|
flags.BoolVarP(&logsOptions.Follow, "follow", "f", false, "Follow log output. The default is false")
|
|
|
|
sinceFlagName := "since"
|
|
flags.StringVar(&logsOptions.SinceRaw, sinceFlagName, "", "Show logs since TIMESTAMP")
|
|
_ = cmd.RegisterFlagCompletionFunc(sinceFlagName, 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)
|
|
|
|
flags.BoolVarP(&logsOptions.Timestamps, "timestamps", "t", false, "Output the timestamps in the log")
|
|
flags.BoolVarP(&logsOptions.Names, "names", "n", false, "Output the container name in the log")
|
|
flags.SetInterspersed(false)
|
|
_ = flags.MarkHidden("details")
|
|
}
|
|
|
|
func logs(_ *cobra.Command, args []string) error {
|
|
if logsOptions.SinceRaw != "" {
|
|
// parse time, error out if something is wrong
|
|
since, err := util.ParseInputTime(logsOptions.SinceRaw)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error parsing --since %q", logsOptions.SinceRaw)
|
|
}
|
|
logsOptions.Since = since
|
|
}
|
|
logsOptions.StdoutWriter = os.Stdout
|
|
logsOptions.StderrWriter = os.Stderr
|
|
return registry.ContainerEngine().ContainerLogs(registry.GetContext(), args, logsOptions.ContainerLogsOptions)
|
|
}
|