github.com/oam-dev/kubevela@v1.9.11/references/cli/logs.go (about)

     1  /*
     2  Copyright 2021 The KubeVela 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 cli
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"regexp"
    23  	"strings"
    24  
    25  	"github.com/fatih/color"
    26  	"github.com/spf13/cobra"
    27  
    28  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    29  	"github.com/oam-dev/kubevela/apis/types"
    30  	"github.com/oam-dev/kubevela/pkg/multicluster"
    31  	"github.com/oam-dev/kubevela/pkg/utils"
    32  	"github.com/oam-dev/kubevela/pkg/utils/common"
    33  	"github.com/oam-dev/kubevela/pkg/utils/util"
    34  	querytypes "github.com/oam-dev/kubevela/pkg/velaql/providers/query/types"
    35  	"github.com/oam-dev/kubevela/references/appfile"
    36  )
    37  
    38  var re = regexp.MustCompile(`"((?:[^"\\]|\\.)*)"`)
    39  
    40  // NewLogsCommand creates `logs` command to tail logs of application
    41  func NewLogsCommand(c common.Args, order string, ioStreams util.IOStreams) *cobra.Command {
    42  	largs := &Args{Args: c}
    43  	cmd := &cobra.Command{
    44  		Use:   "logs",
    45  		Short: "Tail logs for application.",
    46  		Long:  "Tail logs for vela application.",
    47  		Args:  cobra.ExactArgs(1),
    48  		RunE: func(cmd *cobra.Command, args []string) error {
    49  			var err error
    50  			largs.Namespace, err = GetFlagNamespaceOrEnv(cmd, c)
    51  			if err != nil {
    52  				return err
    53  			}
    54  			largs.Name = args[0]
    55  			ctx := context.Background()
    56  			app, err := appfile.LoadApplication(largs.Namespace, args[0], c)
    57  			if err != nil {
    58  				return err
    59  			}
    60  			largs.App = app
    61  			if err := largs.Run(ctx, ioStreams); err != nil {
    62  				return err
    63  			}
    64  			return nil
    65  		},
    66  		Annotations: map[string]string{
    67  			types.TagCommandOrder: order,
    68  			types.TagCommandType:  types.TypeApp,
    69  		},
    70  	}
    71  
    72  	cmd.Flags().StringVarP(&largs.Output, "output", "o", "default", "output format for logs, support: [default, raw, json]")
    73  	cmd.Flags().StringVarP(&largs.ComponentName, "component", "c", "", "filter the pod by the component name")
    74  	cmd.Flags().StringVarP(&largs.ClusterName, "cluster", "", "", "filter the pod by the cluster name")
    75  	cmd.Flags().StringVarP(&largs.PodName, "pod", "p", "", "specify the pod name")
    76  	cmd.Flags().StringVarP(&largs.ContainerName, "container", "", "", "specify the container name")
    77  	addNamespaceAndEnvArg(cmd)
    78  	return cmd
    79  }
    80  
    81  // Args creates arguments for `logs` command
    82  type Args struct {
    83  	Output        string
    84  	Args          common.Args
    85  	Name          string
    86  	CtxName       string
    87  	Namespace     string
    88  	ContainerName string
    89  	PodName       string
    90  	ClusterName   string
    91  	ComponentName string
    92  	StepName      string
    93  	App           *v1beta1.Application
    94  }
    95  
    96  func (l *Args) printPodLogs(ctx context.Context, ioStreams util.IOStreams, selectPod *querytypes.PodBase, filters []string) error {
    97  	config, err := l.Args.GetConfig()
    98  	if err != nil {
    99  		return err
   100  	}
   101  	logC := make(chan string, 1024)
   102  
   103  	var t string
   104  	switch l.Output {
   105  	case "default":
   106  		if color.NoColor {
   107  			t = "{{.ContainerName}} {{.Message}}"
   108  		} else {
   109  			t = "{{color .ContainerColor .ContainerName}} {{.Message}}"
   110  		}
   111  	case "raw":
   112  		t = "{{.Message}}"
   113  	case "json":
   114  		t = "{{json .}}\n"
   115  	}
   116  	go func() {
   117  		for {
   118  			select {
   119  			case str := <-logC:
   120  				show := true
   121  				for _, filter := range filters {
   122  					if !strings.Contains(str, filter) {
   123  						show = false
   124  						break
   125  					}
   126  				}
   127  				if show {
   128  					match := re.FindStringSubmatch(str)
   129  					if len(match) > 1 {
   130  						str = strings.ReplaceAll(match[1], "\\n", "\n")
   131  					}
   132  					ioStreams.Infonln(str)
   133  				}
   134  			case <-ctx.Done():
   135  				return
   136  			}
   137  		}
   138  	}()
   139  
   140  	err = utils.GetPodsLogs(ctx, config, l.ContainerName, []*querytypes.PodBase{selectPod}, t, logC, nil)
   141  	if err != nil {
   142  		return err
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  // Run refer to the implementation at https://github.com/oam-dev/stern/blob/master/stern/main.go
   149  func (l *Args) Run(ctx context.Context, ioStreams util.IOStreams) error {
   150  	pods, err := GetApplicationPods(ctx, l.App.Name, l.App.Namespace, l.Args, Filter{
   151  		Component: l.ComponentName,
   152  		Cluster:   l.ClusterName,
   153  	})
   154  	if err != nil {
   155  		return err
   156  	}
   157  	var selectPod *querytypes.PodBase
   158  	if l.PodName != "" {
   159  		for i, pod := range pods {
   160  			if pod.Metadata.Name == l.PodName {
   161  				selectPod = &pods[i]
   162  				break
   163  			}
   164  		}
   165  		if selectPod == nil {
   166  			fmt.Println("The Pod you specified does not exist, please select it from the list.")
   167  		}
   168  	}
   169  	if selectPod == nil {
   170  		selectPod, err = AskToChooseOnePod(pods)
   171  		if err != nil {
   172  			return err
   173  		}
   174  	}
   175  
   176  	if selectPod == nil {
   177  		return nil
   178  	}
   179  
   180  	if selectPod.Cluster != "" {
   181  		ctx = multicluster.ContextWithClusterName(ctx, selectPod.Cluster)
   182  	}
   183  	return l.printPodLogs(ctx, ioStreams, selectPod, nil)
   184  }