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  }