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

     1  /*
     2  Copyright 2023 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 log
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"runtime"
    24  	"strings"
    25  	"time"
    26  
    27  	"github.com/fatih/color"
    28  	"github.com/go-logr/logr"
    29  	"github.com/kubevela/pkg/util/slices"
    30  	"github.com/kubevela/pkg/util/stringtools"
    31  	"k8s.io/klog/v2"
    32  )
    33  
    34  var _ logr.LogSink = &logger{}
    35  
    36  // NewLogger create a logr.Logger with vela-cli format
    37  func NewLogger(name string) logr.Logger {
    38  	return NewLoggerWithWriter(name, os.Stdout, os.Stderr)
    39  }
    40  
    41  // NewLoggerWithWriter create a logr.Logger with vela-cli format and customized writer
    42  func NewLoggerWithWriter(name string, out io.Writer, errWriter io.Writer) logr.Logger {
    43  	return logr.New(&logger{name: name, out: out, errWriter: errWriter})
    44  }
    45  
    46  type logger struct {
    47  	name      string
    48  	callDepth int
    49  	values    []interface{}
    50  	out       io.Writer
    51  	errWriter io.Writer
    52  }
    53  
    54  // Init init
    55  func (in *logger) Init(info logr.RuntimeInfo) {
    56  	in.callDepth += info.CallDepth
    57  }
    58  
    59  // Enabled check if enabled
    60  func (in *logger) Enabled(level int) bool {
    61  	return klog.VDepth(in.callDepth+2, klog.Level(level)).Enabled()
    62  }
    63  
    64  func (in *logger) mergeKeysAndValues(keysAndValues []interface{}) string {
    65  	lookup := map[string]string{}
    66  	var keys []string
    67  	for i := 0; i < len(keysAndValues); i += 2 {
    68  		key := fmt.Sprintf("%s", keysAndValues[i])
    69  		value := fmt.Sprintf("%s", keysAndValues[i+1])
    70  		if _, found := lookup[key]; !found {
    71  			keys = append(keys, key)
    72  		}
    73  		lookup[key] = fmt.Sprintf("%s=\"%s\"", key, value)
    74  	}
    75  	return strings.Join(slices.Map(keys, func(key string) string { return lookup[key] }), " ")
    76  }
    77  
    78  func (in *logger) print(writer io.Writer, head string, msg string, keysAndValues ...interface{}) {
    79  	var caller, timeStr, nameStr string
    80  	if klog.V(4).Enabled() {
    81  		nameStr = color.MagentaString(fmt.Sprintf("%s ", in.name))
    82  		if _, file, line, ok := runtime.Caller(7); ok {
    83  			if !klog.V(7).Enabled() {
    84  				file = file[strings.LastIndex(file, "/")+1:]
    85  			}
    86  			caller = fmt.Sprintf("[%s:%d] ", file, line)
    87  		}
    88  		t := time.Now()
    89  		timeStr = fmt.Sprintf("%02d:%02d:%02d ", t.Hour(), t.Minute(), t.Second())
    90  		if klog.V(7).Enabled() {
    91  			timeStr = t.Format(time.RFC3339) + " "
    92  		}
    93  	}
    94  	_, _ = fmt.Fprintf(writer, "%s %s%s%s%s %s\n", head, nameStr, timeStr, caller, strings.TrimSpace(stringtools.Capitalize(msg)), in.mergeKeysAndValues(keysAndValues))
    95  }
    96  
    97  // Info .
    98  func (in *logger) Info(level int, msg string, keysAndValues ...interface{}) {
    99  	if level < 1 {
   100  		level = 1
   101  	}
   102  	if in.Enabled(level) {
   103  		in.print(in.out, color.CyanString("INFO"), msg, append(in.values, keysAndValues...)...)
   104  	}
   105  }
   106  
   107  // Error .
   108  func (in *logger) Error(err error, msg string, keysAndValues ...interface{}) {
   109  	if err != nil {
   110  		keysAndValues = append(keysAndValues, "err", err.Error())
   111  	}
   112  	in.print(in.errWriter, color.RedString("ERR "), msg, append(in.values, keysAndValues...)...)
   113  }
   114  
   115  // WithValues fork a logger with given key-values
   116  func (in *logger) WithValues(keysAndValues ...interface{}) logr.LogSink {
   117  	sink := &logger{}
   118  	*sink = *in
   119  	if len(keysAndValues)%2 != 0 {
   120  		keysAndValues = append(keysAndValues, "(MISSING)")
   121  	}
   122  	sink.values = append(in.values, keysAndValues...) // nolint
   123  	return sink
   124  }
   125  
   126  // WithName fork a logger with given name
   127  func (in *logger) WithName(name string) logr.LogSink {
   128  	sink := &logger{}
   129  	*sink = *in
   130  	sink.name = name
   131  	return sink
   132  }