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

     1  /*
     2  Copyright 2022 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 model
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"regexp"
    23  	"text/template"
    24  	"time"
    25  
    26  	"github.com/fatih/color"
    27  	"github.com/pkg/errors"
    28  	"github.com/wercker/stern/stern"
    29  	"k8s.io/apimachinery/pkg/labels"
    30  	"k8s.io/client-go/kubernetes"
    31  	"k8s.io/client-go/rest"
    32  
    33  	"github.com/oam-dev/kubevela/pkg/multicluster"
    34  )
    35  
    36  // PrintLogOfPod print the log during 48h of aimed pod
    37  func PrintLogOfPod(ctx context.Context, config *rest.Config, cluster, namespace, podName, containerName string) (chan string, error) {
    38  	if cluster != "local" {
    39  		ctx = multicluster.ContextWithClusterName(ctx, cluster)
    40  	}
    41  	pod, err := regexp.Compile(podName + ".*")
    42  	if err != nil {
    43  		return nil, fmt.Errorf("fail to compile '%s' for logs query", podName+".*")
    44  	}
    45  	container := regexp.MustCompile(".*")
    46  	if containerName != "" {
    47  		container = regexp.MustCompile(containerName + ".*")
    48  	}
    49  	selector := labels.NewSelector()
    50  
    51  	logC := make(chan string, 1024)
    52  
    53  	go podLog(ctx, config, namespace, pod, container, selector, logC)
    54  
    55  	return logC, nil
    56  }
    57  
    58  func podLog(ctx context.Context, config *rest.Config, namespace string, pod, container *regexp.Regexp, selector labels.Selector, logC chan string) {
    59  	clientSet, err := kubernetes.NewForConfig(config)
    60  	if err != nil {
    61  		logC <- err.Error()
    62  		return
    63  	}
    64  
    65  	added, removed, err := stern.Watch(ctx,
    66  		clientSet.CoreV1().Pods(namespace),
    67  		pod,
    68  		container,
    69  		nil,
    70  		[]stern.ContainerState{stern.RUNNING, stern.TERMINATED},
    71  		selector,
    72  	)
    73  	if err != nil {
    74  		logC <- err.Error()
    75  		return
    76  	}
    77  
    78  	tails := make(map[string]*stern.Tail)
    79  
    80  	var t string
    81  	if color.NoColor {
    82  		t = "{{.ContainerName}} {{.Message}}"
    83  	} else {
    84  		t = "{{color .ContainerColor .ContainerName}} {{.Message}}"
    85  	}
    86  
    87  	funs := map[string]interface{}{
    88  		"color": func(color color.Color, text string) string {
    89  			return color.SprintFunc()(text)
    90  		},
    91  	}
    92  	template, err := template.New("log").Funcs(funs).Parse(t)
    93  	if err != nil {
    94  		if err != nil {
    95  			logC <- errors.Wrap(err, "unable to parse template").Error()
    96  			return
    97  		}
    98  	}
    99  
   100  	go func() {
   101  		for p := range added {
   102  			id := p.GetID()
   103  			if tails[id] != nil {
   104  				continue
   105  			}
   106  			// 48h
   107  			dur, _ := time.ParseDuration("48h")
   108  			tail := stern.NewTail(p.Namespace, p.Pod, p.Container, template, &stern.TailOptions{
   109  				Timestamps:   true,
   110  				SinceSeconds: int64(dur.Seconds()),
   111  				Exclude:      nil,
   112  				Include:      nil,
   113  				Namespace:    false,
   114  				TailLines:    nil, // default for all logs
   115  			})
   116  			tails[id] = tail
   117  
   118  			tail.Start(ctx, clientSet.CoreV1().Pods(p.Namespace), logC)
   119  		}
   120  	}()
   121  
   122  	go func() {
   123  		for p := range removed {
   124  			id := p.GetID()
   125  			if tails[id] == nil {
   126  				continue
   127  			}
   128  			tails[id].Close()
   129  			delete(tails, id)
   130  		}
   131  	}()
   132  
   133  	<-ctx.Done()
   134  }