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 }