github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/kubernetes/logger/formatter.go (about)

     1  /*
     2  Copyright 2021 The Skaffold 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 logger
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"sync"
    23  
    24  	v1 "k8s.io/api/core/v1"
    25  
    26  	eventV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event/v2"
    27  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/log"
    28  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output"
    29  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
    30  	tagutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/tag/util"
    31  )
    32  
    33  type Formatter func(pod v1.Pod, containerStatus v1.ContainerStatus, isMuted func() bool) log.Formatter
    34  
    35  type kubernetesLogFormatter struct {
    36  	colorPicker     output.ColorPicker
    37  	prefix          string
    38  	JSONParseConfig latest.JSONParseConfig
    39  
    40  	pod       *v1.Pod
    41  	container v1.ContainerStatus
    42  
    43  	lock    sync.Mutex
    44  	isMuted func() bool
    45  }
    46  
    47  func newKubernetesLogFormatter(config Config, colorPicker output.ColorPicker, isMuted func() bool, pod *v1.Pod, container v1.ContainerStatus) *kubernetesLogFormatter {
    48  	return &kubernetesLogFormatter{
    49  		colorPicker:     colorPicker,
    50  		prefix:          prefix(config, pod, container),
    51  		JSONParseConfig: config.JSONParseConfig(),
    52  		pod:             pod,
    53  		container:       container,
    54  		isMuted:         isMuted,
    55  	}
    56  }
    57  
    58  func (k *kubernetesLogFormatter) Name() string { return k.prefix }
    59  
    60  func (k *kubernetesLogFormatter) PrintLine(out io.Writer, line string) {
    61  	if k.isMuted() {
    62  		return
    63  	}
    64  	formattedPrefix := k.prefix
    65  	if output.IsColorable(out) {
    66  		formattedPrefix = k.color().Sprintf("%s", k.prefix)
    67  		// if our original prefix was empty, don't prepend a space to the line,
    68  		// but keep the color prefix we just added.
    69  		if k.prefix != "" {
    70  			formattedPrefix = fmt.Sprintf("%s ", formattedPrefix)
    71  		}
    72  	}
    73  
    74  	line = log.ParseJSON(k.JSONParseConfig, line)
    75  	formattedLine := fmt.Sprintf("%s%s", formattedPrefix, line)
    76  	eventV2.ApplicationLog(k.pod.Name, k.container.Name, formattedPrefix, line, formattedLine)
    77  
    78  	k.lock.Lock()
    79  	defer k.lock.Unlock()
    80  	fmt.Fprint(out, formattedLine)
    81  }
    82  
    83  func (k *kubernetesLogFormatter) color() output.Color {
    84  	for _, container := range k.pod.Spec.Containers {
    85  		if c := k.colorPicker.Pick(container.Image); c != output.None {
    86  			return c
    87  		}
    88  	}
    89  
    90  	// If no mapping is found, don't add any color formatting
    91  	return output.None
    92  }
    93  
    94  func prefix(config Config, pod *v1.Pod, container v1.ContainerStatus) string {
    95  	var c latest.Pipeline
    96  	var present bool
    97  	for _, container := range pod.Spec.Containers {
    98  		if c, present = config.PipelineForImage(tagutil.StripTag(container.Image, false)); present {
    99  			break
   100  		}
   101  	}
   102  	if !present {
   103  		c = config.DefaultPipeline()
   104  	}
   105  	switch c.Deploy.Logs.Prefix {
   106  	case "auto":
   107  		if pod.Name != container.Name {
   108  			return podAndContainerPrefix(pod, container)
   109  		}
   110  		return autoPrefix(pod, container)
   111  	case "container":
   112  		return containerPrefix(container)
   113  	case "podAndContainer":
   114  		return podAndContainerPrefix(pod, container)
   115  	case "none":
   116  		return ""
   117  	default:
   118  		panic("unsupported prefix: " + c.Deploy.Logs.Prefix)
   119  	}
   120  }
   121  
   122  func autoPrefix(pod *v1.Pod, container v1.ContainerStatus) string {
   123  	if pod.Name != container.Name {
   124  		return fmt.Sprintf("[%s %s]", pod.Name, container.Name)
   125  	}
   126  	return fmt.Sprintf("[%s]", container.Name)
   127  }
   128  
   129  func containerPrefix(container v1.ContainerStatus) string {
   130  	return fmt.Sprintf("[%s]", container.Name)
   131  }
   132  
   133  func podAndContainerPrefix(pod *v1.Pod, container v1.ContainerStatus) string {
   134  	return fmt.Sprintf("[%s %s]", pod.Name, container.Name)
   135  }