github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/container_logs.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"fmt"
    21  	"strconv"
    22  
    23  	"github.com/containerd/nerdctl/pkg/api/types"
    24  	"github.com/containerd/nerdctl/pkg/clientutil"
    25  	"github.com/containerd/nerdctl/pkg/cmd/container"
    26  	"github.com/spf13/cobra"
    27  )
    28  
    29  func newLogsCommand() *cobra.Command {
    30  	const shortUsage = "Fetch the logs of a container. Expected to be used with 'nerdctl run -d'."
    31  	const longUsage = `Fetch the logs of a container.
    32  
    33  The following containers are supported:
    34  - Containers created with 'nerdctl run -d'. The log is currently empty for containers created without '-d'.
    35  - Containers created with 'nerdctl compose'.
    36  - Containers created with Kubernetes (EXPERIMENTAL).
    37  `
    38  	var logsCommand = &cobra.Command{
    39  		Use:               "logs [flags] CONTAINER",
    40  		Args:              IsExactArgs(1),
    41  		Short:             shortUsage,
    42  		Long:              longUsage,
    43  		RunE:              logsAction,
    44  		ValidArgsFunction: logsShellComplete,
    45  		SilenceUsage:      true,
    46  		SilenceErrors:     true,
    47  	}
    48  	logsCommand.Flags().BoolP("follow", "f", false, "Follow log output")
    49  	logsCommand.Flags().BoolP("timestamps", "t", false, "Show timestamps")
    50  	logsCommand.Flags().StringP("tail", "n", "all", "Number of lines to show from the end of the logs")
    51  	logsCommand.Flags().String("since", "", "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)")
    52  	logsCommand.Flags().String("until", "", "Show logs before a timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)")
    53  	return logsCommand
    54  }
    55  
    56  func processContainerLogsOptions(cmd *cobra.Command) (types.ContainerLogsOptions, error) {
    57  	globalOptions, err := processRootCmdFlags(cmd)
    58  	if err != nil {
    59  		return types.ContainerLogsOptions{}, err
    60  	}
    61  	follow, err := cmd.Flags().GetBool("follow")
    62  	if err != nil {
    63  		return types.ContainerLogsOptions{}, err
    64  	}
    65  	tailArg, err := cmd.Flags().GetString("tail")
    66  	if err != nil {
    67  		return types.ContainerLogsOptions{}, err
    68  	}
    69  	var tail uint
    70  	if tailArg != "" {
    71  		tail, err = getTailArgAsUint(tailArg)
    72  		if err != nil {
    73  			return types.ContainerLogsOptions{}, err
    74  		}
    75  	}
    76  	timestamps, err := cmd.Flags().GetBool("timestamps")
    77  	if err != nil {
    78  		return types.ContainerLogsOptions{}, err
    79  	}
    80  	since, err := cmd.Flags().GetString("since")
    81  	if err != nil {
    82  		return types.ContainerLogsOptions{}, err
    83  	}
    84  	until, err := cmd.Flags().GetString("until")
    85  	if err != nil {
    86  		return types.ContainerLogsOptions{}, err
    87  	}
    88  	return types.ContainerLogsOptions{
    89  		Stdout:     cmd.OutOrStdout(),
    90  		Stderr:     cmd.OutOrStderr(),
    91  		GOptions:   globalOptions,
    92  		Follow:     follow,
    93  		Timestamps: timestamps,
    94  		Tail:       tail,
    95  		Since:      since,
    96  		Until:      until,
    97  	}, nil
    98  }
    99  
   100  func logsAction(cmd *cobra.Command, args []string) error {
   101  	options, err := processContainerLogsOptions(cmd)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), options.GOptions.Namespace, options.GOptions.Address)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	defer cancel()
   111  
   112  	return container.Logs(ctx, client, args[0], options)
   113  }
   114  
   115  func logsShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   116  	// show container names (TODO: only show containers with logs)
   117  	return shellCompleteContainerNames(cmd, nil)
   118  }
   119  
   120  // Attempts to parse the argument given to `-n/--tail` as a uint.
   121  func getTailArgAsUint(arg string) (uint, error) {
   122  	if arg == "all" {
   123  		return 0, nil
   124  	}
   125  	num, err := strconv.Atoi(arg)
   126  	if err != nil {
   127  		return 0, fmt.Errorf("failed to parse `-n/--tail` argument %q: %s", arg, err)
   128  	}
   129  	if num < 0 {
   130  		return 0, fmt.Errorf("`-n/--tail` argument must be positive, got: %d", num)
   131  	}
   132  	return uint(num), nil
   133  }