github.com/matrixorigin/matrixone@v1.2.0/pkg/common/log/logger.go (about) 1 // Copyright 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package log 16 17 import ( 18 "context" 19 "fmt" 20 "strings" 21 "time" 22 23 "github.com/matrixorigin/matrixone/pkg/pb/metadata" 24 "github.com/matrixorigin/matrixone/pkg/util/trace" 25 "go.uber.org/zap" 26 "go.uber.org/zap/zapcore" 27 ) 28 29 // GetServiceLogger returns service logger, it will using the service as the logger name, and 30 // append FieldNameServiceUUID field to the logger 31 func GetServiceLogger(logger *zap.Logger, service metadata.ServiceType, uuid string) *MOLogger { 32 return wrap(logger. 33 Named(fmt.Sprintf("%s-service", strings.ToLower(service.String()))). 34 With(zap.String(FieldNameServiceUUID, uuid))) 35 } 36 37 // GetModuleLogger returns the module logger, it will add ".module" to logger name. 38 // e.g. if the logger's name is cn-service, module is txn, the new logger's name is 39 // "cn-service.txn". 40 func GetModuleLogger(logger *MOLogger, module Module) *MOLogger { 41 return wrap(logger.logger.Named(string(module))) 42 } 43 44 // With creates a child logger and adds structured context to it. Fields added 45 // to the child don't affect the parent, and vice versa. 46 func (l *MOLogger) With(fields ...zap.Field) *MOLogger { 47 return newMOLogger(l.logger.With(fields...), l.ctx) 48 } 49 50 // WithOptions creates a child logger with zap options. 51 func (l *MOLogger) WithOptions(opts ...zap.Option) *MOLogger { 52 return newMOLogger(l.logger.WithOptions(opts...), l.ctx) 53 } 54 55 // Named adds a new path segment to the logger's name. Segments are joined by 56 // periods. By default, Loggers are unnamed. 57 func (l *MOLogger) Named(name string) *MOLogger { 58 return newMOLogger(l.logger.Named(name), l.ctx) 59 } 60 61 func (l *MOLogger) WithContext(ctx context.Context) *MOLogger { 62 if ctx == nil || ctx == context.TODO() || ctx == context.Background() { 63 panic("nil, context.TODO() and context.Background() are not supported") 64 } 65 if sc := trace.SpanFromContext(ctx).SpanContext(); trace.IsEnable() && sc.IsEmpty() { 66 panic("context with empty SpanContext are not supported") 67 } 68 return newMOLogger(l.logger, ctx) 69 } 70 71 func newMOLogger(logger *zap.Logger, ctx context.Context) *MOLogger { 72 return &MOLogger{ 73 logger: logger, 74 ctx: ctx, 75 m: map[int]*zap.Logger{ 76 1: logger.WithOptions(zap.AddCallerSkip(1)), 77 2: logger.WithOptions(zap.AddCallerSkip(2)), 78 3: logger.WithOptions(zap.AddCallerSkip(3)), 79 }, 80 } 81 } 82 83 // WithProcess if the current log belongs to a certain process, the process name and process ID 84 // can be recorded. When analyzing the log, all related logs can be retrieved according to the 85 // process ID. 86 func (l *MOLogger) WithProcess(process Process) *MOLogger { 87 return l.With(zap.String(FieldNameProcess, string(process))) 88 } 89 90 // Enabled returns true if the level is enabled 91 func (l *MOLogger) Enabled(level zapcore.Level) bool { 92 return l.logger.Core().Enabled(level) 93 } 94 95 // RawLogger returns the raw zap logger 96 func (l *MOLogger) RawLogger() *zap.Logger { 97 return l.logger 98 } 99 100 // Info shortcuts to print info log 101 func (l *MOLogger) Info(msg string, fields ...zap.Field) bool { 102 return l.Log(msg, DefaultLogOptions().WithLevel(zap.InfoLevel).AddCallerSkip(1), fields...) 103 } 104 105 // InfoAction shortcuts to print info action log 106 func (l *MOLogger) InfoAction(msg string, fields ...zap.Field) func() { 107 return l.LogAction(msg, DefaultLogOptions().WithLevel(zap.InfoLevel).AddCallerSkip(1), fields...) 108 } 109 110 // Debug shortcuts to print debug log 111 func (l *MOLogger) Debug(msg string, fields ...zap.Field) bool { 112 return l.Log(msg, DefaultLogOptions().WithLevel(zap.DebugLevel).AddCallerSkip(1), fields...) 113 } 114 115 // InfoDebugAction shortcuts to print debug action log 116 func (l *MOLogger) DebugAction(msg string, fields ...zap.Field) func() { 117 return l.LogAction(msg, DefaultLogOptions().WithLevel(zap.DebugLevel).AddCallerSkip(1), fields...) 118 } 119 120 // Error shortcuts to print error log 121 func (l *MOLogger) Error(msg string, fields ...zap.Field) bool { 122 return l.Log(msg, DefaultLogOptions().WithLevel(zap.ErrorLevel).AddCallerSkip(1), fields...) 123 } 124 125 // Warn shortcuts to print warn log 126 func (l *MOLogger) Warn(msg string, fields ...zap.Field) bool { 127 return l.Log(msg, DefaultLogOptions().WithLevel(zap.WarnLevel).AddCallerSkip(1), fields...) 128 } 129 130 // Panic shortcuts to print panic log 131 func (l *MOLogger) Panic(msg string, fields ...zap.Field) bool { 132 return l.Log(msg, DefaultLogOptions().WithLevel(zap.PanicLevel).AddCallerSkip(1), fields...) 133 } 134 135 // Fatal shortcuts to print fatal log 136 func (l *MOLogger) Fatal(msg string, fields ...zap.Field) bool { 137 return l.Log(msg, DefaultLogOptions().WithLevel(zap.FatalLevel).AddCallerSkip(1), fields...) 138 } 139 140 // Log is the entry point for mo log printing. Return true to indicate that the log 141 // is being recorded by the current LogContext. 142 func (l *MOLogger) Log(msg string, opts LogOptions, fields ...zap.Field) bool { 143 if l.logger == nil { 144 panic("missing logger") 145 } 146 147 for _, fiter := range filters { 148 if !fiter(opts) { 149 return false 150 } 151 } 152 153 if opts.ctx == nil { 154 opts.ctx = l.ctx 155 } 156 157 logger, has := l.m[opts.callerSkip+1] 158 if !has { 159 logger = l.logger 160 } 161 if ce := logger.Check(opts.level, msg); ce != nil { 162 if len(opts.fields) > 0 { 163 fields = append(fields, opts.fields...) 164 } 165 if opts.ctx != nil { 166 fields = append(fields, trace.ContextField(opts.ctx)) 167 } 168 169 ce.Write(fields...) 170 return true 171 } 172 return false 173 } 174 175 // LogAction used to log an action, or generate 2 logs, the first log occurring 176 // at the place where the call is made and the second log occurring at the end 177 // of the function where the LogAction is called, with the additional time consuming. 178 // e.g.: 179 // 180 // func LogActionExample() { 181 // defer log.Info(zapLogger).LogAction("example action")() 182 // } 183 // 184 // This method should often be used to log the elapsed time of a function and, as the 185 // logs appear in pairs, can also be used to check whether a function has been executed. 186 func (l *MOLogger) LogAction(action string, opts LogOptions, fields ...zap.Field) func() { 187 if !l.Log(action, opts.AddCallerSkip(1), fields...) { 188 return nothing 189 } 190 startAt := time.Now() 191 return func() { 192 fields = append(fields, zap.Duration(FieldNameCost, time.Since(startAt))) 193 l.Log(action, opts, fields...) 194 } 195 } 196 197 func wrap(logger *zap.Logger) *MOLogger { 198 return wrapWithContext(logger, nil) 199 } 200 201 func wrapWithContext(logger *zap.Logger, ctx context.Context) *MOLogger { 202 if logger == nil { 203 panic("zap logger is nil") 204 } 205 if ctx != nil && 206 (ctx == context.TODO() || ctx == context.Background()) { 207 panic("TODO and Background are not supported") 208 } 209 210 return newMOLogger( 211 logger, 212 ctx, 213 ) 214 } 215 216 func nothing() {} 217 218 // DefaultLogOptions default log options 219 func DefaultLogOptions() LogOptions { 220 return LogOptions{} 221 } 222 223 // WithContext set log trace context. 224 func (opts LogOptions) WithContext(ctx context.Context) LogOptions { 225 if ctx == nil { 226 panic("context is nil") 227 } 228 if ctx == context.TODO() || ctx == context.Background() { 229 panic("TODO and Background contexts are not supported") 230 } 231 if sc := trace.SpanFromContext(ctx).SpanContext(); trace.IsEnable() && sc.IsEmpty() { 232 panic("context with empty SpanContext are not supported") 233 } 234 235 opts.ctx = ctx 236 return opts 237 } 238 239 // WithLevel set log print level 240 func (opts LogOptions) WithLevel(level zapcore.Level) LogOptions { 241 opts.level = level 242 return opts 243 } 244 245 // WithSample sample print the log, using log counts as sampling frequency. First time must output. 246 func (opts LogOptions) WithSample(sampleType SampleType) LogOptions { 247 opts.sampleType = sampleType 248 return opts 249 } 250 251 // AddCallerSkip help to show the logger real caller, by skip n call stack 252 func (opts LogOptions) AddCallerSkip(n int) LogOptions { 253 opts.callerSkip += n 254 return opts 255 } 256 257 // WithProcess if the current log belongs to a certain process, the process name and process ID 258 // can be recorded. When analyzing the log, all related logs can be retrieved according to the 259 // process ID. 260 func (opts LogOptions) WithProcess(process Process, processID string) LogOptions { 261 opts.fields = append(opts.fields, 262 zap.String(FieldNameProcess, string(process)), 263 zap.String(FieldNameProcessID, processID)) 264 return opts 265 }