github.com/wfusion/gofusion@v1.1.14/common/infra/watermill/log.go (about) 1 package watermill 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "reflect" 9 "sort" 10 "strings" 11 "sync" 12 ) 13 14 const ( 15 ContextKeyMessageUUID = "watermill:message_uuid" 16 ContextKeyRawMessageID = "watermill:raw_message_id" 17 ContextLogFieldKey = "watermill:context" 18 MessageRouterAck = "watermill:router_ack" 19 MessageHeaderAppID = "appid" 20 ) 21 22 // LogFields is the logger's key-value list of fields. 23 type LogFields map[string]any 24 25 // Add adds new fields to the list of LogFields. 26 func (l LogFields) Add(newFields LogFields) LogFields { 27 resultFields := make(LogFields, len(l)+len(newFields)) 28 29 for field, value := range l { 30 resultFields[field] = value 31 } 32 for field, value := range newFields { 33 resultFields[field] = value 34 } 35 36 return resultFields 37 } 38 39 // Copy copies the LogFields. 40 func (l LogFields) Copy() LogFields { 41 cpy := make(LogFields, len(l)) 42 for k, v := range l { 43 cpy[k] = v 44 } 45 46 return cpy 47 } 48 49 // LoggerAdapter is an interface, that you need to implement to support Watermill logging. 50 // You can use StdLoggerAdapter as a reference implementation. 51 type LoggerAdapter interface { 52 Error(msg string, err error, fields LogFields) 53 Info(msg string, fields LogFields) 54 Debug(msg string, fields LogFields) 55 Trace(msg string, fields LogFields) 56 With(fields LogFields) LoggerAdapter 57 } 58 59 // NopLogger is a logger which discards all logs. 60 type NopLogger struct{} 61 62 func (NopLogger) Error(msg string, err error, fields LogFields) {} 63 func (NopLogger) Info(msg string, fields LogFields) {} 64 func (NopLogger) Debug(msg string, fields LogFields) {} 65 func (NopLogger) Trace(msg string, fields LogFields) {} 66 func (l NopLogger) With(fields LogFields) LoggerAdapter { return l } 67 68 // StdLoggerAdapter is a logger implementation, which sends all logs to provided standard output. 69 type StdLoggerAdapter struct { 70 ErrorLogger *log.Logger 71 InfoLogger *log.Logger 72 DebugLogger *log.Logger 73 TraceLogger *log.Logger 74 75 fields LogFields 76 } 77 78 // NewStdLogger creates StdLoggerAdapter which sends all logs to stderr. 79 func NewStdLogger(debug, trace bool) LoggerAdapter { 80 return NewStdLoggerWithOut(os.Stderr, debug, trace) 81 } 82 83 // NewStdLoggerWithOut creates StdLoggerAdapter which sends all logs to provided io.Writer. 84 func NewStdLoggerWithOut(out io.Writer, debug bool, trace bool) LoggerAdapter { 85 l := log.New(out, "[watermill] ", log.LstdFlags|log.Lmicroseconds|log.Lshortfile) 86 a := &StdLoggerAdapter{InfoLogger: l, ErrorLogger: l} 87 88 if debug { 89 a.DebugLogger = l 90 } 91 if trace { 92 a.TraceLogger = l 93 } 94 95 return a 96 } 97 98 func (l *StdLoggerAdapter) Error(msg string, err error, fields LogFields) { 99 l.log(l.ErrorLogger, "ERROR", msg, fields.Add(LogFields{"err": err})) 100 } 101 102 func (l *StdLoggerAdapter) Info(msg string, fields LogFields) { 103 l.log(l.InfoLogger, "INFO ", msg, fields) 104 } 105 106 func (l *StdLoggerAdapter) Debug(msg string, fields LogFields) { 107 l.log(l.DebugLogger, "DEBUG", msg, fields) 108 } 109 110 func (l *StdLoggerAdapter) Trace(msg string, fields LogFields) { 111 l.log(l.TraceLogger, "TRACE", msg, fields) 112 } 113 114 func (l *StdLoggerAdapter) With(fields LogFields) LoggerAdapter { 115 return &StdLoggerAdapter{ 116 ErrorLogger: l.ErrorLogger, 117 InfoLogger: l.InfoLogger, 118 DebugLogger: l.DebugLogger, 119 TraceLogger: l.TraceLogger, 120 fields: l.fields.Add(fields), 121 } 122 } 123 124 func (l *StdLoggerAdapter) log(logger *log.Logger, level string, msg string, fields LogFields) { 125 if logger == nil { 126 return 127 } 128 129 fieldsStr := "" 130 131 allFields := l.fields.Add(fields) 132 133 keys := make([]string, len(allFields)) 134 i := 0 135 for field := range allFields { 136 keys[i] = field 137 i++ 138 } 139 140 sort.Strings(keys) 141 142 for _, key := range keys { 143 var valueStr string 144 value := allFields[key] 145 146 if stringer, ok := value.(fmt.Stringer); ok { 147 valueStr = stringer.String() 148 } else { 149 valueStr = fmt.Sprintf("%v", value) 150 } 151 152 if strings.Contains(valueStr, " ") { 153 valueStr = `"` + valueStr + `"` 154 } 155 156 fieldsStr += key + "=" + valueStr + " " 157 } 158 159 _ = logger.Output(3, fmt.Sprintf("\t"+`level=%s msg="%s" %s`, level, msg, fieldsStr)) 160 } 161 162 type LogLevel uint 163 164 const ( 165 TraceLogLevel LogLevel = iota + 1 166 DebugLogLevel 167 InfoLogLevel 168 ErrorLogLevel 169 ) 170 171 type CapturedMessage struct { 172 Level LogLevel 173 Fields LogFields 174 Msg string 175 Err error 176 } 177 178 // CaptureLoggerAdapter is a logger which captures all logs. 179 // This logger is mostly useful for testing logging. 180 type CaptureLoggerAdapter struct { 181 captured map[LogLevel][]CapturedMessage 182 fields LogFields 183 lock sync.Mutex 184 } 185 186 func NewCaptureLogger() *CaptureLoggerAdapter { 187 return &CaptureLoggerAdapter{ 188 captured: map[LogLevel][]CapturedMessage{}, 189 } 190 } 191 192 func (c *CaptureLoggerAdapter) With(fields LogFields) LoggerAdapter { 193 return &CaptureLoggerAdapter{captured: c.captured, fields: c.fields.Add(fields)} 194 } 195 196 func (c *CaptureLoggerAdapter) capture(msg CapturedMessage) { 197 c.lock.Lock() 198 defer c.lock.Unlock() 199 200 c.captured[msg.Level] = append(c.captured[msg.Level], msg) 201 } 202 203 func (c *CaptureLoggerAdapter) Captured() map[LogLevel][]CapturedMessage { 204 c.lock.Lock() 205 defer c.lock.Unlock() 206 207 return c.captured 208 } 209 210 func (c *CaptureLoggerAdapter) Has(msg CapturedMessage) bool { 211 c.lock.Lock() 212 defer c.lock.Unlock() 213 214 for _, capturedMsg := range c.captured[msg.Level] { 215 if reflect.DeepEqual(msg, capturedMsg) { 216 return true 217 } 218 } 219 return false 220 } 221 222 func (c *CaptureLoggerAdapter) HasError(err error) bool { 223 c.lock.Lock() 224 defer c.lock.Unlock() 225 226 for _, capturedMsg := range c.captured[ErrorLogLevel] { 227 if capturedMsg.Err == err { 228 return true 229 } 230 } 231 return false 232 } 233 234 func (c *CaptureLoggerAdapter) Error(msg string, err error, fields LogFields) { 235 c.capture(CapturedMessage{ 236 Level: ErrorLogLevel, 237 Fields: c.fields.Add(fields), 238 Msg: msg, 239 Err: err, 240 }) 241 } 242 243 func (c *CaptureLoggerAdapter) Info(msg string, fields LogFields) { 244 c.capture(CapturedMessage{ 245 Level: InfoLogLevel, 246 Fields: c.fields.Add(fields), 247 Msg: msg, 248 }) 249 } 250 251 func (c *CaptureLoggerAdapter) Debug(msg string, fields LogFields) { 252 c.capture(CapturedMessage{ 253 Level: DebugLogLevel, 254 Fields: c.fields.Add(fields), 255 Msg: msg, 256 }) 257 } 258 259 func (c *CaptureLoggerAdapter) Trace(msg string, fields LogFields) { 260 c.capture(CapturedMessage{ 261 Level: TraceLogLevel, 262 Fields: c.fields.Add(fields), 263 Msg: msg, 264 }) 265 }