gitee.com/sasukebo/go-micro/v4@v4.7.1/logger/default.go (about) 1 package logger 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "runtime" 8 "sort" 9 "strings" 10 "sync" 11 "time" 12 13 dlog "gitee.com/sasukebo/go-micro/v4/debug/log" 14 ) 15 16 func init() { 17 lvl, err := GetLevel(os.Getenv("MICRO_LOG_LEVEL")) 18 if err != nil { 19 lvl = InfoLevel 20 } 21 22 DefaultLogger = NewLogger(WithLevel(lvl)) 23 } 24 25 type defaultLogger struct { 26 sync.RWMutex 27 opts Options 28 } 29 30 // Init (opts...) should only overwrite provided options 31 func (l *defaultLogger) Init(opts ...Option) error { 32 for _, o := range opts { 33 o(&l.opts) 34 } 35 return nil 36 } 37 38 func (l *defaultLogger) String() string { 39 return "default" 40 } 41 42 func (l *defaultLogger) Fields(fields map[string]interface{}) Logger { 43 l.Lock() 44 nfields := make(map[string]interface{}, len(l.opts.Fields)) 45 for k, v := range l.opts.Fields { 46 nfields[k] = v 47 } 48 l.Unlock() 49 50 for k, v := range fields { 51 nfields[k] = v 52 } 53 54 return &defaultLogger{opts: Options{ 55 Level: l.opts.Level, 56 Fields: nfields, 57 Out: l.opts.Out, 58 CallerSkipCount: l.opts.CallerSkipCount, 59 Context: l.opts.Context, 60 }} 61 } 62 63 func copyFields(src map[string]interface{}) map[string]interface{} { 64 dst := make(map[string]interface{}, len(src)) 65 for k, v := range src { 66 dst[k] = v 67 } 68 return dst 69 } 70 71 // logCallerfilePath returns a package/file:line description of the caller, 72 // preserving only the leaf directory name and file name. 73 func logCallerfilePath(loggingFilePath string) string { 74 // To make sure we trim the path correctly on Windows too, we 75 // counter-intuitively need to use '/' and *not* os.PathSeparator here, 76 // because the path given originates from Go stdlib, specifically 77 // runtime.Caller() which (as of Mar/17) returns forward slashes even on 78 // Windows. 79 // 80 // See https://github.com/golang/go/issues/3335 81 // and https://github.com/golang/go/issues/18151 82 // 83 // for discussion on the issue on Go side. 84 idx := strings.LastIndexByte(loggingFilePath, '/') 85 if idx == -1 { 86 return loggingFilePath 87 } 88 idx = strings.LastIndexByte(loggingFilePath[:idx], '/') 89 if idx == -1 { 90 return loggingFilePath 91 } 92 return loggingFilePath[idx+1:] 93 } 94 95 func (l *defaultLogger) Log(level Level, v ...interface{}) { 96 // TODO decide does we need to write message if log level not used? 97 if !l.opts.Level.Enabled(level) { 98 return 99 } 100 101 l.RLock() 102 fields := copyFields(l.opts.Fields) 103 l.RUnlock() 104 105 fields["level"] = level.String() 106 107 if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { 108 fields["file"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line) 109 } 110 111 rec := dlog.Record{ 112 Timestamp: time.Now(), 113 Message: fmt.Sprint(v...), 114 Metadata: make(map[string]string, len(fields)), 115 } 116 117 keys := make([]string, 0, len(fields)) 118 for k, v := range fields { 119 keys = append(keys, k) 120 rec.Metadata[k] = fmt.Sprintf("%v", v) 121 } 122 123 sort.Strings(keys) 124 metadata := "" 125 126 for _, k := range keys { 127 metadata += fmt.Sprintf(" %s=%v", k, fields[k]) 128 } 129 130 dlog.DefaultLog.Write(rec) 131 132 t := rec.Timestamp.Format("2006-01-02 15:04:05") 133 fmt.Printf("%s %s %v\n", t, metadata, rec.Message) 134 } 135 136 func (l *defaultLogger) Logf(level Level, format string, v ...interface{}) { 137 // TODO decide does we need to write message if log level not used? 138 if level < l.opts.Level { 139 return 140 } 141 142 l.RLock() 143 fields := copyFields(l.opts.Fields) 144 l.RUnlock() 145 146 fields["level"] = level.String() 147 148 if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { 149 fields["file"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line) 150 } 151 152 rec := dlog.Record{ 153 Timestamp: time.Now(), 154 Message: fmt.Sprintf(format, v...), 155 Metadata: make(map[string]string, len(fields)), 156 } 157 158 keys := make([]string, 0, len(fields)) 159 for k, v := range fields { 160 keys = append(keys, k) 161 rec.Metadata[k] = fmt.Sprintf("%v", v) 162 } 163 164 sort.Strings(keys) 165 metadata := "" 166 167 for _, k := range keys { 168 metadata += fmt.Sprintf(" %s=%v", k, fields[k]) 169 } 170 171 dlog.DefaultLog.Write(rec) 172 173 t := rec.Timestamp.Format("2006-01-02 15:04:05") 174 fmt.Printf("%s %s %v\n", t, metadata, rec.Message) 175 } 176 177 func (l *defaultLogger) Options() Options { 178 // not guard against options Context values 179 l.RLock() 180 opts := l.opts 181 opts.Fields = copyFields(l.opts.Fields) 182 l.RUnlock() 183 return opts 184 } 185 186 // NewLogger builds a new logger based on options 187 func NewLogger(opts ...Option) Logger { 188 // Default options 189 options := Options{ 190 Level: InfoLevel, 191 Fields: make(map[string]interface{}), 192 Out: os.Stderr, 193 CallerSkipCount: 2, 194 Context: context.Background(), 195 } 196 197 l := &defaultLogger{opts: options} 198 if err := l.Init(opts...); err != nil { 199 l.Log(FatalLevel, err) 200 } 201 202 return l 203 }