github.com/wfusion/gofusion@v1.1.14/mongo/metrics.go (about) 1 package mongo 2 3 import ( 4 "context" 5 "log" 6 "sync" 7 "syscall" 8 "time" 9 10 "go.mongodb.org/mongo-driver/event" 11 "go.uber.org/atomic" 12 13 "github.com/wfusion/gofusion/common/utils" 14 "github.com/wfusion/gofusion/config" 15 "github.com/wfusion/gofusion/metrics" 16 ) 17 18 var ( 19 metricsPoolIdleKey = []string{"mongo", "idle"} 20 metricsPoolInUseKey = []string{"mongo", "inuse"} 21 metricsPoolTotalKey = []string{"mongo", "total"} 22 metricsPoolLocker = new(sync.RWMutex) 23 metricsPoolInUseCounter = map[string]map[string]*atomic.Int64{} 24 metricsPoolTotalCounter = map[string]map[string]*atomic.Int64{} 25 metricsLatencyKey = []string{"mongo", "latency"} 26 metricsLatencyBuckets = []float64{ 27 .1, .25, .5, .75, .90, .95, .99, 28 1, 2.5, 5, 7.5, 9, 9.5, 9.9, 29 10, 25, 50, 75, 90, 95, 99, 30 100, 250, 500, 750, 900, 950, 990, 31 } 32 ) 33 34 func startDaemonRoutines(ctx context.Context, appName, name string, conf *Conf) { 35 ticker := time.Tick(time.Second * 5) 36 app := config.Use(appName).AppName() 37 labels := []metrics.Label{ 38 {Key: "config", Value: name}, 39 {Key: "database", Value: conf.DB}, 40 } 41 42 log.Printf("%v [Gofusion] %s %s %s metrics start", syscall.Getpid(), app, config.ComponentMongo, name) 43 for { 44 select { 45 case <-ctx.Done(): 46 log.Printf("%v [Gofusion] %s %s %s metrics exited", 47 syscall.Getpid(), app, config.ComponentMongo, name) 48 return 49 case <-ticker: 50 go metricMongoStats(ctx, appName, name, labels) 51 go metricMongoLatency(ctx, appName, name, labels) 52 } 53 } 54 } 55 56 func metricMongoStats(ctx context.Context, appName, name string, labels []metrics.Label) { 57 select { 58 case <-ctx.Done(): 59 return 60 default: 61 } 62 63 _, _ = utils.Catch(func() { 64 var total, inuse int64 65 _, err := utils.Catch(func() { 66 metricsPoolLocker.RLock() 67 defer metricsPoolLocker.RUnlock() 68 inuse = metricsPoolInUseCounter[appName][name].Load() 69 total = metricsPoolTotalCounter[appName][name].Load() 70 }) 71 if err != nil { 72 return 73 } 74 75 app := config.Use(appName).AppName() 76 idleKey := append([]string{app}, metricsPoolIdleKey...) 77 inuseKey := append([]string{app}, metricsPoolInUseKey...) 78 totalKey := append([]string{app}, metricsPoolTotalKey...) 79 ide := total - inuse 80 for _, m := range metrics.Internal(metrics.AppName(appName)) { 81 select { 82 case <-ctx.Done(): 83 return 84 default: 85 if m.IsEnableServiceLabel() { 86 m.SetGauge(ctx, idleKey, float64(ide), metrics.Labels(labels)) 87 m.SetGauge(ctx, inuseKey, float64(inuse), metrics.Labels(labels)) 88 m.SetGauge(ctx, totalKey, float64(total), metrics.Labels(labels)) 89 } else { 90 m.SetGauge(ctx, metricsPoolIdleKey, float64(ide), metrics.Labels(labels)) 91 m.SetGauge(ctx, metricsPoolInUseKey, float64(inuse), metrics.Labels(labels)) 92 m.SetGauge(ctx, metricsPoolTotalKey, float64(total), metrics.Labels(labels)) 93 } 94 } 95 } 96 }) 97 } 98 99 func metricMongoLatency(ctx context.Context, appName, name string, labels []metrics.Label) { 100 select { 101 case <-ctx.Done(): 102 return 103 default: 104 } 105 106 _, _ = utils.Catch(func() { 107 rwlock.RLock() 108 defer rwlock.RUnlock() 109 instances, ok := appInstances[appName] 110 if !ok { 111 return 112 } 113 instance, ok := instances[name] 114 if !ok { 115 return 116 } 117 118 mgoCli := instance.GetProxy() 119 begin := time.Now() 120 if err := mgoCli.Ping(ctx, nil); err != nil { 121 return 122 } 123 124 latency := float64(time.Since(begin)) / float64(time.Millisecond) 125 latencyKey := append([]string{config.Use(appName).AppName()}, metricsLatencyKey...) 126 for _, m := range metrics.Internal(metrics.AppName(appName)) { 127 select { 128 case <-ctx.Done(): 129 return 130 default: 131 if m.IsEnableServiceLabel() { 132 m.AddSample(ctx, latencyKey, latency, 133 metrics.Labels(labels), 134 metrics.PrometheusBuckets(metricsLatencyBuckets), 135 ) 136 } else { 137 m.AddSample(ctx, metricsLatencyKey, latency, 138 metrics.Labels(labels), 139 metrics.PrometheusBuckets(metricsLatencyBuckets), 140 ) 141 } 142 } 143 } 144 }) 145 } 146 147 func metricsPoolMonitor(appName, name string) func(evt *event.PoolEvent) { 148 metricsPoolLocker.Lock() 149 defer metricsPoolLocker.Unlock() 150 if metricsPoolTotalCounter[appName] == nil { 151 metricsPoolTotalCounter[appName] = make(map[string]*atomic.Int64) 152 } 153 if metricsPoolTotalCounter[appName][name] == nil { 154 metricsPoolTotalCounter[appName][name] = atomic.NewInt64(0) 155 } 156 if metricsPoolInUseCounter[appName] == nil { 157 metricsPoolInUseCounter[appName] = make(map[string]*atomic.Int64) 158 } 159 if metricsPoolInUseCounter[appName][name] == nil { 160 metricsPoolInUseCounter[appName][name] = atomic.NewInt64(0) 161 } 162 163 inuse := metricsPoolInUseCounter[appName][name] 164 total := metricsPoolTotalCounter[appName][name] 165 return func(evt *event.PoolEvent) { 166 switch evt.Type { 167 case event.PoolCreated: 168 case event.PoolReady: 169 case event.PoolCleared: 170 case event.PoolClosedEvent: 171 case event.ConnectionCreated: 172 total.Add(1) 173 case event.ConnectionReady: 174 case event.ConnectionClosed: 175 total.Add(-1) 176 case event.GetStarted: 177 case event.GetFailed: 178 case event.GetSucceeded: 179 inuse.Add(1) 180 case event.ConnectionReturned: 181 inuse.Add(-1) 182 } 183 } 184 185 }