github.com/wfusion/gofusion@v1.1.14/log/customlogger/mongo.go (about)

     1  package customlogger
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  	"strings"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/spf13/cast"
    11  	"go.mongodb.org/mongo-driver/event"
    12  
    13  	"github.com/wfusion/gofusion/common/utils"
    14  	"github.com/wfusion/gofusion/config"
    15  	"github.com/wfusion/gofusion/log"
    16  )
    17  
    18  var (
    19  	// MongoLoggerType FIXME: should not be deleted to avoid compiler optimized
    20  	MongoLoggerType = reflect.TypeOf(mongoLogger{})
    21  	mongoFields     = log.Fields{"component": strings.ToLower(config.ComponentMongo)}
    22  )
    23  
    24  type mongoLogger struct {
    25  	*event.CommandMonitor
    26  
    27  	log                log.Loggable
    28  	enabled            bool
    29  	appName            string
    30  	confName           string
    31  	loggableCommandSet *utils.Set[string]
    32  
    33  	mutex sync.RWMutex
    34  	// requestMap The key is the monotonically atomic incrementing RequestID from the Mongo command event,
    35  	// used to associate the command with the duration and only print one line of log.
    36  	requestMap map[int64]struct {
    37  		commandString string
    38  	}
    39  }
    40  
    41  func DefaultMongoLogger() (logger *mongoLogger) {
    42  	logger = &mongoLogger{
    43  		enabled:    true,
    44  		requestMap: make(map[int64]struct{ commandString string }),
    45  		loggableCommandSet: utils.NewSet[string](
    46  			"ping",
    47  			"insert",
    48  			"find",
    49  			"update",
    50  			"delete",
    51  			"aggregate",
    52  			"distinct",
    53  			"count",
    54  			"findAndModify",
    55  			"getMore",
    56  			"killCursors",
    57  			"create",
    58  			"drop",
    59  			"listDatabases",
    60  			"dropDatabase",
    61  			"createIndexes",
    62  			"listIndexes",
    63  			"dropIndexes",
    64  			"listCollections",
    65  		),
    66  	}
    67  	logger.CommandMonitor = &event.CommandMonitor{
    68  		Started:   logger.started,
    69  		Succeeded: logger.succeeded,
    70  		Failed:    logger.failed,
    71  	}
    72  	return
    73  }
    74  
    75  func (m *mongoLogger) Init(log log.Loggable, appName, name string) {
    76  	m.log = log
    77  	m.appName = appName
    78  	m.confName = name
    79  	m.requestMap = make(map[int64]struct{ commandString string })
    80  	m.loggableCommandSet = utils.NewSet[string](
    81  		"ping",
    82  		"insert",
    83  		"find",
    84  		"update",
    85  		"delete",
    86  		"aggregate",
    87  		"distinct",
    88  		"count",
    89  		"findAndModify",
    90  		"getMore",
    91  		"killCursors",
    92  		"create",
    93  		"drop",
    94  		"listDatabases",
    95  		"dropDatabase",
    96  		"createIndexes",
    97  		"listIndexes",
    98  		"dropIndexes",
    99  		"listCollections",
   100  	)
   101  }
   102  
   103  func (m *mongoLogger) GetMonitor() *event.CommandMonitor {
   104  	return &event.CommandMonitor{
   105  		Started:   m.started,
   106  		Succeeded: m.succeeded,
   107  		Failed:    m.failed,
   108  	}
   109  }
   110  
   111  func (m *mongoLogger) started(ctx context.Context, evt *event.CommandStartedEvent) {
   112  	if !m.isLoggableCommandName(evt.CommandName) {
   113  		return
   114  	}
   115  	m.pushCommandString(evt.RequestID, evt.Command.String())
   116  }
   117  
   118  func (m *mongoLogger) succeeded(ctx context.Context, evt *event.CommandSucceededEvent) {
   119  	if !m.isLoggableCommandName(evt.CommandName) {
   120  		return
   121  	}
   122  	m.logger().Info(ctx, "%s succeeded: %s [request[%v] command[%s]]",
   123  		evt.CommandName, evt.Reply, evt.RequestID, m.popCommandString(evt.RequestID),
   124  		m.fields(log.Fields{"latency": int64(evt.Duration) / int64(time.Millisecond)}))
   125  }
   126  
   127  func (m *mongoLogger) failed(ctx context.Context, evt *event.CommandFailedEvent) {
   128  	if !m.isLoggableCommandName(evt.CommandName) {
   129  		return
   130  	}
   131  
   132  	m.logger().Warn(ctx, "%s failed: %s [request[%v] command[%s]]",
   133  		evt.CommandName, evt.Failure, evt.RequestID, m.popCommandString(evt.RequestID),
   134  		m.fields(log.Fields{"latency": int64(evt.Duration) / int64(time.Millisecond)}))
   135  }
   136  
   137  func (m *mongoLogger) pushCommandString(requestID int64, commandString string) {
   138  	m.mutex.Lock()
   139  	defer m.mutex.Unlock()
   140  	m.requestMap[requestID] = struct{ commandString string }{commandString: commandString}
   141  }
   142  
   143  func (m *mongoLogger) popCommandString(requestID int64) string {
   144  	m.mutex.Lock()
   145  	defer m.mutex.Unlock()
   146  	reqCtx, ok := m.requestMap[requestID]
   147  	if ok {
   148  		delete(m.requestMap, requestID)
   149  		return reqCtx.commandString
   150  	}
   151  	return ""
   152  }
   153  
   154  func (m *mongoLogger) logger() log.Loggable {
   155  	if m.log != nil {
   156  		return m.log
   157  	}
   158  	return log.Use(config.DefaultInstanceKey, log.AppName(m.appName))
   159  }
   160  
   161  func (m *mongoLogger) fields(fields log.Fields) log.Fields {
   162  	return utils.MapMerge(fields, mongoFields)
   163  }
   164  
   165  func (m *mongoLogger) isLoggableCommandName(commandName string) bool {
   166  	if m.confName == "" {
   167  		return true
   168  	}
   169  	if m.reloadConfig(); !m.enabled {
   170  		return false
   171  	}
   172  	return m.loggableCommandSet.Contains(commandName)
   173  }
   174  
   175  func (m *mongoLogger) reloadConfig() {
   176  	var cfgs map[string]map[string]any
   177  	_ = config.Use(m.appName).LoadComponentConfig(config.ComponentMongo, &cfgs)
   178  	if len(cfgs) == 0 {
   179  		return
   180  	}
   181  
   182  	cfg, ok := cfgs[m.confName]
   183  	if !ok {
   184  		return
   185  	}
   186  	m.enabled = cast.ToBool(cfg["enable_logger"])
   187  	logConfigObj, ok1 := cfg["logger_config"]
   188  	logCfg, ok2 := logConfigObj.(map[string]any)
   189  	if !ok1 || !ok2 {
   190  		return
   191  	}
   192  	loggableCommandList, ok := logCfg["loggable_commands"].([]string)
   193  	if !ok {
   194  		return
   195  	}
   196  
   197  	m.loggableCommandSet = utils.NewSet(loggableCommandList...)
   198  }