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 }