package pods import ( "os" "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/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.PodLogsOptions SinceRaw string UntilRaw string } var ( logsPodOptions logsOptionsWrapper logsPodDescription = `Displays logs for pod with one or more containers.` logsPodCommand = &cobra.Command{ Use: "logs [options] POD", Short: "Fetch logs for pod with one or more containers", Long: logsPodDescription, // We dont want users to invoke latest and pod togather Args: func(cmd *cobra.Command, args []string) error { switch { case registry.IsRemote() && logsPodOptions.Latest: return errors.New(cmd.Name() + " does not support 'latest' when run remotely") case len(args) > 1: return errors.New("requires exactly 1 arg") case logsPodOptions.Latest && len(args) > 0: return errors.New("--latest and pods cannot be used together") case !logsPodOptions.Latest && len(args) < 1: return errors.New("specify at least one pod name or ID to log") } return nil }, RunE: logs, ValidArgsFunction: common.AutocompletePods, Example: `podman pod logs podID podman pod logs -c ctrname podName podman pod logs --tail 2 mywebserver podman pod logs --follow=true --since 10m podID podman pod logs mywebserver`, } containerLogsCommand = &cobra.Command{ Use: logsPodCommand.Use, Short: logsPodCommand.Short, Long: logsPodCommand.Long, Args: logsPodCommand.Args, RunE: logsPodCommand.RunE, ValidArgsFunction: logsPodCommand.ValidArgsFunction, Example: `podman pod logs podId podman pod logs -c ctrname podName podman pod logs --tail 2 mywebserver podman pod logs --follow=true --since 10m podID`, } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Command: logsPodCommand, }) logsFlags(logsPodCommand) validate.AddLatestFlag(logsPodCommand, &logsPodOptions.Latest) // container logs registry.Commands = append(registry.Commands, registry.CliCommand{ Command: containerLogsCommand, Parent: podCmd, }) logsFlags(containerLogsCommand) validate.AddLatestFlag(containerLogsCommand, &logsPodOptions.Latest) } func logsFlags(cmd *cobra.Command) { flags := cmd.Flags() flags.BoolVar(&logsPodOptions.Details, "details", false, "Show extra details provided to the logs") flags.BoolVarP(&logsPodOptions.Follow, "follow", "f", false, "Follow log output.") containerNameFlag := "container" flags.StringVarP(&logsPodOptions.ContainerName, containerNameFlag, "c", "", "Filter logs by container name or id which belongs to pod") _ = cmd.RegisterFlagCompletionFunc(containerNameFlag, common.AutocompleteContainers) sinceFlagName := "since" flags.StringVar(&logsPodOptions.SinceRaw, sinceFlagName, "", "Show logs since TIMESTAMP") _ = cmd.RegisterFlagCompletionFunc(sinceFlagName, completion.AutocompleteNone) untilFlagName := "until" flags.StringVar(&logsPodOptions.UntilRaw, untilFlagName, "", "Show logs until TIMESTAMP") _ = cmd.RegisterFlagCompletionFunc(untilFlagName, completion.AutocompleteNone) tailFlagName := "tail" flags.Int64Var(&logsPodOptions.Tail, tailFlagName, -1, "Output the specified number of LINES at the end of the logs.") _ = cmd.RegisterFlagCompletionFunc(tailFlagName, completion.AutocompleteNone) flags.BoolVarP(&logsPodOptions.Timestamps, "timestamps", "t", false, "Output the timestamps in the log") flags.SetInterspersed(false) _ = flags.MarkHidden("details") } func logs(_ *cobra.Command, args []string) error { if logsPodOptions.SinceRaw != "" { // parse time, error out if something is wrong since, err := util.ParseInputTime(logsPodOptions.SinceRaw, true) if err != nil { return errors.Wrapf(err, "error parsing --since %q", logsPodOptions.SinceRaw) } logsPodOptions.Since = since } if logsPodOptions.UntilRaw != "" { // parse time, error out if something is wrong until, err := util.ParseInputTime(logsPodOptions.UntilRaw, false) if err != nil { return errors.Wrapf(err, "error parsing --until %q", logsPodOptions.UntilRaw) } logsPodOptions.Until = until } // Remote can only process one container at a time if registry.IsRemote() && logsPodOptions.ContainerName == "" { return errors.Wrapf(define.ErrInvalidArg, "-c or --container cannot be empty") } logsPodOptions.StdoutWriter = os.Stdout logsPodOptions.StderrWriter = os.Stderr return registry.ContainerEngine().PodLogs(registry.GetContext(), args[0], logsPodOptions.PodLogsOptions) }