summaryrefslogtreecommitdiff
path: root/cmd/podman/pods/logs.go
blob: fe520566946ebc04ee708863347523de6e9f755c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
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)
}