github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/zlog/trace.go (about)

     1  package zlog
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  
     9  	"go.uber.org/zap"
    10  
    11  	"github.com/jxskiss/gopkg/v2/internal/logfilter"
    12  )
    13  
    14  const TraceFilterRuleEnvName = "ZLOG_TRACE_FILTER_RULE"
    15  
    16  func (p *Properties) compileTraceFilter() {
    17  	if p.cfg.TraceFilterRule == "" {
    18  		envRule := os.Getenv(TraceFilterRuleEnvName)
    19  		if envRule != "" {
    20  			S().Infof("zlog: using trace filter rule from env: %q", envRule)
    21  			p.cfg.TraceFilterRule = envRule
    22  		}
    23  	}
    24  	if p.cfg.TraceFilterRule != "" {
    25  		var errs []error
    26  		p.traceFilter, errs = logfilter.NewFileNameFilter(p.cfg.TraceFilterRule)
    27  		for _, err := range errs {
    28  			S().Warnf("zlog: %v", err)
    29  		}
    30  	}
    31  }
    32  
    33  // Trace logs a message at TraceLevel if it's enabled.
    34  //
    35  // If trace messages are disabled globally, calling this function is
    36  // a no-op.
    37  func (l Logger) Trace(msg string, fields ...zap.Field) {
    38  	if globals.Level.Load() <= int32(TraceLevel) {
    39  		l.slowPathTrace(msg, fields)
    40  	}
    41  }
    42  
    43  func (l Logger) slowPathTrace(msg string, fields []zap.Field) {
    44  	logger := l.Logger.WithOptions(zap.AddCallerSkip(3))
    45  	checkAndWriteTraceMessage(logger, msg, fields...)
    46  }
    47  
    48  // Tracef uses fmt.Sprintf to log a message at TraceLevel if it's enabled.
    49  //
    50  // If trace messages are disabled globally, calling this function is
    51  // a no-op.
    52  func (l Logger) Tracef(format string, args ...any) {
    53  	if globals.Level.Load() <= int32(TraceLevel) {
    54  		l.slowPathTracef(format, args)
    55  	}
    56  }
    57  
    58  func (l Logger) slowPathTracef(format string, args []any) {
    59  	logger := l.Logger.WithOptions(zap.AddCallerSkip(3))
    60  	msg := formatMessage(format, args)
    61  	checkAndWriteTraceMessage(logger, msg)
    62  }
    63  
    64  // Tracef uses fmt.Sprintf to log a message at TraceLevel if it's enabled.
    65  //
    66  // If trace messages are disabled globally, calling this function is
    67  // a no-op.
    68  func (s SugaredLogger) Tracef(format string, args ...any) {
    69  	if globals.Level.Load() <= int32(TraceLevel) {
    70  		s.slowPathTracef(format, args)
    71  	}
    72  }
    73  
    74  func (s SugaredLogger) slowPathTracef(format string, args []any) {
    75  	logger := s.SugaredLogger.Desugar().WithOptions(zap.AddCallerSkip(3))
    76  	msg := formatMessage(format, args)
    77  	checkAndWriteTraceMessage(logger, msg)
    78  }
    79  
    80  func checkAndWriteTraceMessage(l *zap.Logger, msg string, fields ...zap.Field) {
    81  	if ce := l.Check(TraceLevel, msg); ce != nil {
    82  		fileName := ce.Caller.File
    83  		if fileName != "" {
    84  			_, fileName, _, _, _ = getCaller(3)
    85  		}
    86  		if fileName != "" && globals.Props.traceFilter != nil &&
    87  			!globals.Props.traceFilter.Allow(fileName) {
    88  			return
    89  		}
    90  		ce.Write(fields...)
    91  	}
    92  }
    93  
    94  // TRACE logs a message at TraceLevel if it's enabled.
    95  //
    96  // TRACE accepts flexible arguments to help development, it trys to get a
    97  // logger from the first argument, if the first argument is a *zap.Logger or
    98  // *zap.SugaredLogger, the logger will be used, else if the first argument
    99  // is a context.Context, the context will be used to build a logger using
   100  // Builder, else it uses the global logger.
   101  //
   102  // The other arguments may be of type zap.Field or any ordinary type,
   103  // the type will be detected and the arguments will be formatted in a most
   104  // reasonable way. See example code for detailed usage examples.
   105  //
   106  // If trace messages are disabled globally, calling this function is
   107  // a no-op.
   108  func TRACE(args ...any) {
   109  	if globals.Level.Load() <= int32(TraceLevel) {
   110  		_slowPathTrace(0, nil, args)
   111  	}
   112  }
   113  
   114  // TRACESkip is similar to TRACE, but it has an extra skip argument to get
   115  // correct caller information. When you need to wrap TRACE, you will always
   116  // want to use this function instead of TRACE.
   117  //
   118  // If trace messages are disabled globally, calling this function is
   119  // a no-op.
   120  func TRACESkip(skip int, args ...any) {
   121  	if globals.Level.Load() <= int32(TraceLevel) {
   122  		_slowPathTrace(skip, nil, args)
   123  	}
   124  }
   125  
   126  // TRACE1 is same with TRACE, but it accepts an extra arg0 before args.
   127  func TRACE1(arg0 any, args ...any) {
   128  	if globals.Level.Load() <= int32(TraceLevel) {
   129  		_slowPathTrace(0, arg0, args)
   130  	}
   131  }
   132  
   133  // TRACESkip1 is same with TRACESkip, but it accepts an extra arg0 before args.
   134  func TRACESkip1(skip int, arg0 any, args ...any) {
   135  	if globals.Level.Load() <= int32(TraceLevel) {
   136  		_slowPathTrace(skip, arg0, args)
   137  	}
   138  }
   139  
   140  func _slowPathTrace(skip int, a0 any, args []any) {
   141  	caller, fullFileName, simpleFileName, line, _ := getCaller(skip + 2)
   142  	if fullFileName != "" && globals.Props.traceFilter != nil &&
   143  		!globals.Props.traceFilter.Allow(fullFileName) {
   144  		return
   145  	}
   146  	logger, msg, fields := parseLoggerAndParams(skip, a0, args)
   147  	if msg == "" {
   148  		msg = fmt.Sprintf("========  %s#L%d - %s  ========", simpleFileName, line, caller)
   149  	} else {
   150  		msg = fmt.Sprintf("[%s] %s", caller, msg)
   151  	}
   152  	if ce := logger.Check(TraceLevel, msg); ce != nil {
   153  		ce.Write(fields...)
   154  	}
   155  }
   156  
   157  func parseLoggerAndParams(skip int, a0 any, args []any) (Logger, string, []zap.Field) {
   158  	isArgs0 := false
   159  	if a0 == nil && len(args) > 0 {
   160  		a0 = args[0]
   161  		isArgs0 = true
   162  	}
   163  	trimArg0 := func(args []any) []any {
   164  		if isArgs0 {
   165  			args = args[1:]
   166  		}
   167  		return args
   168  	}
   169  	var logger = L()
   170  	if a0 != nil {
   171  		switch a0 := a0.(type) {
   172  		case context.Context:
   173  			logger = WithCtx(a0)
   174  			args = trimArg0(args)
   175  		case Logger:
   176  			logger = a0
   177  			args = trimArg0(args)
   178  		case SugaredLogger:
   179  			logger = a0.Desugar()
   180  			args = trimArg0(args)
   181  		case *zap.Logger:
   182  			logger = Logger{Logger: a0}
   183  			args = trimArg0(args)
   184  		case *zap.SugaredLogger:
   185  			logger = Logger{Logger: a0.Desugar()}
   186  			args = trimArg0(args)
   187  		default:
   188  			if !isArgs0 {
   189  				args = append([]any{a0}, args...)
   190  			}
   191  		}
   192  	}
   193  	logger = logger.WithOptions(zap.AddCallerSkip(skip + 2))
   194  	if len(args) == 0 {
   195  		return logger, "", nil
   196  	}
   197  
   198  	switch arg0 := args[0].(type) {
   199  	case string:
   200  		fields, ok := tryConvertFields(args[1:])
   201  		if ok {
   202  			return logger, arg0, fields
   203  		}
   204  	case zap.Field:
   205  		fields, ok := tryConvertFields(args)
   206  		if ok {
   207  			return logger, "", fields
   208  		}
   209  	}
   210  
   211  	template := ""
   212  	if s, ok := args[0].(string); ok && strings.IndexByte(s, '%') >= 0 {
   213  		template = s
   214  		args = args[1:]
   215  	}
   216  	return logger, formatMessage(template, args), nil
   217  }
   218  
   219  func tryConvertFields(args []any) ([]zap.Field, bool) {
   220  	if len(args) == 0 {
   221  		return nil, true
   222  	}
   223  	for i := 0; i < len(args); i++ {
   224  		if _, ok := args[i].(zap.Field); !ok {
   225  			return nil, false
   226  		}
   227  	}
   228  	fields := make([]zap.Field, len(args))
   229  	for i, f := range args {
   230  		fields[i] = f.(zap.Field)
   231  	}
   232  	return fields, true
   233  }
   234  
   235  func formatMessage(template string, fmtArgs []any) string {
   236  	if len(fmtArgs) == 0 {
   237  		return template
   238  	}
   239  	if template != "" {
   240  		return fmt.Sprintf(template, fmtArgs...)
   241  	}
   242  	if len(fmtArgs) == 1 {
   243  		if str, ok := fmtArgs[0].(string); ok {
   244  			return str
   245  		}
   246  	}
   247  	return fmt.Sprint(fmtArgs...)
   248  }