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 }