github.com/matrixorigin/matrixone@v0.7.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  // Named adds a new path segment to the logger's name. Segments are joined by
    51  // periods. By default, Loggers are unnamed.
    52  func (l *MOLogger) Named(name string) *MOLogger {
    53  	return newMOLogger(l.logger.Named(name), l.ctx)
    54  }
    55  
    56  func (l *MOLogger) WithContext(ctx context.Context) *MOLogger {
    57  	if ctx == nil || ctx == context.TODO() || ctx == context.Background() {
    58  		panic("nil, context.TODO() and context.Background() are not supported")
    59  	}
    60  	if sc := trace.SpanFromContext(ctx).SpanContext(); trace.IsEnable() && sc.IsEmpty() {
    61  		panic("context with empty SpanContext are not supported")
    62  	}
    63  	return newMOLogger(l.logger, ctx)
    64  }
    65  
    66  func newMOLogger(logger *zap.Logger, ctx context.Context) *MOLogger {
    67  	return &MOLogger{
    68  		logger: logger,
    69  		ctx:    ctx,
    70  		m: map[int]*zap.Logger{
    71  			1: logger.WithOptions(zap.AddCallerSkip(1)),
    72  			2: logger.WithOptions(zap.AddCallerSkip(2)),
    73  			3: logger.WithOptions(zap.AddCallerSkip(3)),
    74  		},
    75  	}
    76  }
    77  
    78  // WithProcess if the current log belongs to a certain process, the process name and process ID
    79  // can be recorded. When analyzing the log, all related logs can be retrieved according to the
    80  // process ID.
    81  func (l *MOLogger) WithProcess(process Process) *MOLogger {
    82  	return l.With(zap.String(FieldNameProcess, string(process)))
    83  }
    84  
    85  // Enabled returns true if the level is enabled
    86  func (l *MOLogger) Enabled(level zapcore.Level) bool {
    87  	return l.logger.Core().Enabled(level)
    88  }
    89  
    90  // RawLogger returns the raw zap logger
    91  func (l *MOLogger) RawLogger() *zap.Logger {
    92  	return l.logger
    93  }
    94  
    95  // Info shortcuts to print info log
    96  func (l *MOLogger) Info(msg string, fields ...zap.Field) bool {
    97  	return l.Log(msg, DefaultLogOptions().WithLevel(zap.InfoLevel).AddCallerSkip(1), fields...)
    98  }
    99  
   100  // InfoAction shortcuts to print info action log
   101  func (l *MOLogger) InfoAction(msg string, fields ...zap.Field) func() {
   102  	return l.LogAction(msg, DefaultLogOptions().WithLevel(zap.InfoLevel).AddCallerSkip(1), fields...)
   103  }
   104  
   105  // Debug shortcuts to  print debug log
   106  func (l *MOLogger) Debug(msg string, fields ...zap.Field) bool {
   107  	return l.Log(msg, DefaultLogOptions().WithLevel(zap.DebugLevel).AddCallerSkip(1), fields...)
   108  }
   109  
   110  // InfoDebugAction shortcuts to print debug action log
   111  func (l *MOLogger) InfoDebugAction(msg string, fields ...zap.Field) func() {
   112  	return l.LogAction(msg, DefaultLogOptions().WithLevel(zap.DebugLevel).AddCallerSkip(1), fields...)
   113  }
   114  
   115  // Error shortcuts to  print error log
   116  func (l *MOLogger) Error(msg string, fields ...zap.Field) bool {
   117  	return l.Log(msg, DefaultLogOptions().WithLevel(zap.ErrorLevel).AddCallerSkip(1), fields...)
   118  }
   119  
   120  // Warn shortcuts to  print warn log
   121  func (l *MOLogger) Warn(msg string, fields ...zap.Field) bool {
   122  	return l.Log(msg, DefaultLogOptions().WithLevel(zap.WarnLevel).AddCallerSkip(1), fields...)
   123  }
   124  
   125  // Panic shortcuts to  print panic log
   126  func (l *MOLogger) Panic(msg string, fields ...zap.Field) bool {
   127  	return l.Log(msg, DefaultLogOptions().WithLevel(zap.PanicLevel).AddCallerSkip(1), fields...)
   128  }
   129  
   130  // Fatal shortcuts to print fatal log
   131  func (l *MOLogger) Fatal(msg string, fields ...zap.Field) bool {
   132  	return l.Log(msg, DefaultLogOptions().WithLevel(zap.FatalLevel).AddCallerSkip(1), fields...)
   133  }
   134  
   135  // Log is the entry point for mo log printing. Return true to indicate that the log
   136  // is being recorded by the current LogContext.
   137  func (l *MOLogger) Log(msg string, opts LogOptions, fields ...zap.Field) bool {
   138  	if l.logger == nil {
   139  		panic("missing logger")
   140  	}
   141  
   142  	for _, fiter := range filters {
   143  		if !fiter(opts) {
   144  			return false
   145  		}
   146  	}
   147  
   148  	if opts.ctx == nil {
   149  		opts.ctx = l.ctx
   150  	}
   151  
   152  	logger, has := l.m[opts.callerSkip+1]
   153  	if !has {
   154  		logger = l.logger
   155  	}
   156  	if ce := logger.Check(opts.level, msg); ce != nil {
   157  		if len(opts.fields) > 0 {
   158  			fields = append(fields, opts.fields...)
   159  		}
   160  		if opts.ctx != nil {
   161  			fields = append(fields, trace.ContextField(opts.ctx))
   162  		}
   163  
   164  		ce.Write(fields...)
   165  		return true
   166  	}
   167  	return false
   168  }
   169  
   170  // LogAction used to log an action, or generate 2 logs, the first log occurring
   171  // at the place where the call is made and the second log occurring at the end
   172  // of the function where the LogAction is called, with the additional time consuming.
   173  // e.g.:
   174  //
   175  //	func LogActionExample() {
   176  //	    defer log.Info(zapLogger).LogAction("example action")()
   177  //	}
   178  //
   179  // This method should often be used to log the elapsed time of a function and, as the
   180  // logs appear in pairs, can also be used to check whether a function has been executed.
   181  func (l *MOLogger) LogAction(action string, opts LogOptions, fields ...zap.Field) func() {
   182  	startAt := time.Now()
   183  	if !l.Log(action, opts.AddCallerSkip(1), fields...) {
   184  		return nothing
   185  	}
   186  	return func() {
   187  		fields = append(fields, zap.Duration(FieldNameCost, time.Since(startAt)))
   188  		l.Log(action, opts, fields...)
   189  	}
   190  }
   191  
   192  func wrap(logger *zap.Logger) *MOLogger {
   193  	return wrapWithContext(logger, nil)
   194  }
   195  
   196  func wrapWithContext(logger *zap.Logger, ctx context.Context) *MOLogger {
   197  	if logger == nil {
   198  		panic("zap logger is nil")
   199  	}
   200  	if ctx != nil &&
   201  		(ctx == context.TODO() || ctx == context.Background()) {
   202  		panic("TODO and Background are not supported")
   203  	}
   204  
   205  	return newMOLogger(
   206  		logger.WithOptions(zap.AddStacktrace(zap.ErrorLevel)),
   207  		ctx,
   208  	)
   209  }
   210  
   211  func nothing() {}
   212  
   213  // DefaultLogOptions default log options
   214  func DefaultLogOptions() LogOptions {
   215  	return LogOptions{}
   216  }
   217  
   218  // WithContext set log trace context.
   219  func (opts LogOptions) WithContext(ctx context.Context) LogOptions {
   220  	if ctx == nil {
   221  		panic("context is nil")
   222  	}
   223  	if ctx == context.TODO() || ctx == context.Background() {
   224  		panic("TODO and Background contexts are not supported")
   225  	}
   226  	if sc := trace.SpanFromContext(ctx).SpanContext(); trace.IsEnable() && sc.IsEmpty() {
   227  		panic("context with empty SpanContext are not supported")
   228  	}
   229  
   230  	opts.ctx = ctx
   231  	return opts
   232  }
   233  
   234  // WithLevel set log print level
   235  func (opts LogOptions) WithLevel(level zapcore.Level) LogOptions {
   236  	opts.level = level
   237  	return opts
   238  }
   239  
   240  // WithSample sample print the log, using log counts as sampling frequency. First time must output.
   241  func (opts LogOptions) WithSample(sampleType SampleType) LogOptions {
   242  	opts.sampleType = sampleType
   243  	return opts
   244  }
   245  
   246  // AddCallerSkip help to show the logger real caller, by skip n call stack
   247  func (opts LogOptions) AddCallerSkip(n int) LogOptions {
   248  	opts.callerSkip += n
   249  	return opts
   250  }
   251  
   252  // WithProcess if the current log belongs to a certain process, the process name and process ID
   253  // can be recorded. When analyzing the log, all related logs can be retrieved according to the
   254  // process ID.
   255  func (opts LogOptions) WithProcess(process Process, processID string) LogOptions {
   256  	opts.fields = append(opts.fields,
   257  		zap.String(FieldNameProcess, string(process)),
   258  		zap.String(FieldNameProcessID, processID))
   259  	return opts
   260  }