github.com/jxskiss/gopkg@v0.17.3/zlog/trace.go (about)

     1  package zlog
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"go.uber.org/zap"
     9  )
    10  
    11  var disableTrace = false
    12  
    13  // Trace logs a message at TraceLevel if it's enabled.
    14  // It also adds a prefix "[TRACE] " to the message.
    15  //
    16  // If trace messages are disabled by GlobalConfig, calling this function
    17  // is a no-op.
    18  func Trace(msg string, fields ...zap.Field) {
    19  	if !disableTrace {
    20  		msg = TracePrefix + msg
    21  		if ce := _l().Check(TraceLevel.toZapLevel(), msg); ce != nil {
    22  			ce.Write(fields...)
    23  		}
    24  	}
    25  }
    26  
    27  // Tracef uses fmt.Sprintf to log a message at TraceLevel if it's enabled.
    28  // It also adds a prefix "[TRACE] " to the message.
    29  //
    30  // If trace messages are disabled by GlobalConfig, calling this function
    31  // is a no-op.
    32  func Tracef(format string, args ...interface{}) {
    33  	if !disableTrace {
    34  		msg := formatMessage(format, args)
    35  		msg = TracePrefix + msg
    36  		if ce := _l().Check(TraceLevel.toZapLevel(), msg); ce != nil {
    37  			ce.Write()
    38  		}
    39  	}
    40  }
    41  
    42  // TRACE logs a message at TraceLevel if it's enabled.
    43  // It also adds a prefix "[TRACE] " to the message.
    44  //
    45  // TRACE accepts flexible arguments to help development, it trys to get a
    46  // logger from the first argument, if the first argument is a *zap.Logger or
    47  // *zap.SugaredLogger, the logger will be used, else if the first argument
    48  // is a context.Context, the context will be used to build a logger using
    49  // Builder, else it uses the global logger.
    50  //
    51  // The other arguments may be of type zap.Field or any ordinary type,
    52  // the type will be detected and the arguments will be formatted in a most
    53  // reasonable way. See example code for detailed usage examples.
    54  //
    55  // If trace messages are disabled by GlobalConfig, calling this function
    56  // is a no-op.
    57  func TRACE(args ...interface{}) {
    58  	if !disableTrace {
    59  		_slowPathTRACE(0, args...)
    60  	}
    61  }
    62  
    63  // TRACESkip is similar to TRACE, but it has an extra skip argument to get
    64  // correct caller information. When you need to wrap TRACE, you will always
    65  // want to use this function instead of TRACE.
    66  //
    67  // If trace messages are disabled by GlobalConfig, calling this function
    68  // is a no-op.
    69  func TRACESkip(skip int, args ...interface{}) {
    70  	if !disableTrace {
    71  		_slowPathTRACE(skip, args...)
    72  	}
    73  }
    74  
    75  func _slowPathTRACE(skip int, args ...interface{}) {
    76  	logger, msg, fields := parseLoggerAndParams(skip, args)
    77  	msg = addCallerPrefix(skip, TracePrefix, msg)
    78  	if ce := logger.Check(TraceLevel.toZapLevel(), msg); ce != nil {
    79  		ce.Write(fields...)
    80  	}
    81  }
    82  
    83  func parseLoggerAndParams(skip int, args []interface{}) (*zap.Logger, string, []zap.Field) {
    84  	var logger = L()
    85  	if len(args) > 0 {
    86  		switch arg0 := args[0].(type) {
    87  		case context.Context:
    88  			logger = B(arg0).Build()
    89  			args = args[1:]
    90  		case *zap.Logger:
    91  			logger = arg0
    92  			args = args[1:]
    93  		case *zap.SugaredLogger:
    94  			logger = arg0.Desugar()
    95  			args = args[1:]
    96  		}
    97  	}
    98  	logger = logger.WithOptions(zap.AddCallerSkip(skip + 2))
    99  	if len(args) == 0 {
   100  		return logger, "", nil
   101  	}
   102  
   103  	switch arg0 := args[0].(type) {
   104  	case string:
   105  		fields, ok := tryConvertFields(args[1:])
   106  		if ok {
   107  			return logger, arg0, fields
   108  		}
   109  	case zap.Field:
   110  		fields, ok := tryConvertFields(args)
   111  		if ok {
   112  			return logger, "", fields
   113  		}
   114  	}
   115  
   116  	template := ""
   117  	if s, ok := args[0].(string); ok && strings.IndexByte(s, '%') >= 0 {
   118  		template = s
   119  		args = args[1:]
   120  	}
   121  	return logger, formatMessage(template, args), nil
   122  }
   123  
   124  func addCallerPrefix(skip int, prefix, msg string) string {
   125  	caller, file, line, _ := getCaller(skip + 3)
   126  	if msg == "" {
   127  		return fmt.Sprintf("%s========  %s#L%d - %s  ========", prefix, file, line, caller)
   128  	}
   129  	return fmt.Sprintf("%s[%s] %s", prefix, caller, msg)
   130  }
   131  
   132  func tryConvertFields(args []interface{}) ([]zap.Field, bool) {
   133  	if len(args) == 0 {
   134  		return nil, true
   135  	}
   136  	for i := 0; i < len(args); i++ {
   137  		if _, ok := args[i].(zap.Field); !ok {
   138  			return nil, false
   139  		}
   140  	}
   141  	fields := make([]zap.Field, len(args))
   142  	for i, f := range args {
   143  		fields[i] = f.(zap.Field)
   144  	}
   145  	return fields, true
   146  }
   147  
   148  func formatMessage(template string, fmtArgs []interface{}) string {
   149  	if len(fmtArgs) == 0 {
   150  		return template
   151  	}
   152  	if template != "" {
   153  		return fmt.Sprintf(template, fmtArgs...)
   154  	}
   155  	if len(fmtArgs) == 1 {
   156  		if str, ok := fmtArgs[0].(string); ok {
   157  			return str
   158  		}
   159  	}
   160  	return fmt.Sprint(fmtArgs...)
   161  }