github.com/wfusion/gofusion@v1.1.14/metrics/construct.go (about) 1 package metrics 2 3 import ( 4 "context" 5 "log" 6 "reflect" 7 "syscall" 8 "time" 9 10 "github.com/pkg/errors" 11 12 "github.com/wfusion/gofusion/common/infra/metrics" 13 "github.com/wfusion/gofusion/common/utils" 14 "github.com/wfusion/gofusion/common/utils/inspect" 15 "github.com/wfusion/gofusion/config" 16 17 fusLog "github.com/wfusion/gofusion/log" 18 19 _ "github.com/wfusion/gofusion/log/customlogger" 20 ) 21 22 func Construct(ctx context.Context, confs map[string]*Conf, opts ...utils.OptionExtender) func() { 23 opt := utils.ApplyOptions[config.InitOption](opts...) 24 optU := utils.ApplyOptions[useOption](opts...) 25 if opt.AppName == "" { 26 opt.AppName = optU.appName 27 } 28 for name, conf := range confs { 29 addConfig(ctx, name, conf, opt) 30 } 31 32 return func() { 33 rwlock.Lock() 34 defer rwlock.Unlock() 35 36 pid := syscall.Getpid() 37 app := config.Use(opt.AppName).AppName() 38 if appInstances != nil { 39 for _, sinks := range appInstances[opt.AppName] { 40 for name, sink := range sinks { 41 log.Printf("%v [Gofusion] %s %s %s router exiting...", 42 pid, app, config.ComponentMetrics, name) 43 sink.shutdown() 44 log.Printf("%v [Gofusion] %s %s %s router exited", 45 pid, app, config.ComponentMetrics, name) 46 } 47 } 48 delete(appInstances, opt.AppName) 49 } 50 51 if cfgsMap != nil { 52 delete(cfgsMap, opt.AppName) 53 } 54 } 55 } 56 57 func addConfig(ctx context.Context, name string, conf *Conf, opt *config.InitOption) { 58 var ( 59 err error 60 interval time.Duration 61 ) 62 if utils.IsStrNotBlank(conf.Interval) { 63 interval, err = time.ParseDuration(conf.Interval) 64 if err != nil { 65 panic(errors.Errorf("metrics component parse %s interval failed: %s", name, err)) 66 } 67 } 68 69 rwlock.Lock() 70 defer rwlock.Unlock() 71 if cfgsMap == nil { 72 cfgsMap = make(map[string]map[string]*cfg) 73 } 74 if cfgsMap[opt.AppName] == nil { 75 cfgsMap[opt.AppName] = make(map[string]*cfg) 76 } 77 if _, ok := cfgsMap[opt.AppName][name]; ok { 78 panic(ErrDuplicatedName) 79 } 80 81 var logger metrics.Logger 82 if utils.IsStrNotBlank(conf.Logger) { 83 loggerType := inspect.TypeOf(conf.Logger) 84 loggerValue := reflect.New(loggerType) 85 if loggerValue.Type().Implements(customLoggerType) { 86 logger := fusLog.Use(conf.LogInstance, fusLog.AppName(opt.AppName)) 87 loggerValue.Interface().(customLogger).Init(logger, opt.AppName, name) 88 } 89 logger = loggerValue.Convert(metricsLoggerType).Interface().(metrics.Logger) 90 } 91 92 cfgsMap[opt.AppName][name] = &cfg{ 93 c: conf, 94 ctx: ctx, 95 name: name, 96 appName: opt.AppName, 97 interval: interval, 98 initOption: opt, 99 logger: logger, 100 } 101 } 102 103 type useOption struct { 104 appName string 105 } 106 107 func AppName(name string) utils.OptionFunc[useOption] { 108 return func(o *useOption) { 109 o.appName = name 110 } 111 } 112 113 func NewDI(name, job string, opts ...utils.OptionExtender) func() Sink { 114 return func() Sink { 115 return Use(name, job, opts...) 116 } 117 } 118 119 func Use(name, job string, opts ...utils.OptionExtender) Sink { 120 opt := utils.ApplyOptions[useOption](opts...) 121 rwlock.Lock() 122 defer rwlock.Unlock() 123 cfgs, ok := cfgsMap[opt.appName] 124 if !ok { 125 panic(errors.Errorf("app metrics config not found: %s", opt.appName)) 126 } 127 cfg, ok := cfgs[name] 128 if !ok { 129 panic(errors.Errorf("metrics config not found: %s", name)) 130 } 131 132 return use(job, cfg) 133 } 134 135 func use(job string, conf *cfg) (sink Sink) { 136 if appInstances == nil { 137 appInstances = make(map[string]map[string]map[string]Sink) 138 } 139 instances, ok := appInstances[conf.appName] 140 if !ok { 141 instances = make(map[string]map[string]Sink) 142 appInstances[conf.appName] = instances 143 } 144 145 jobs, ok := instances[conf.name] 146 if !ok { 147 jobs = make(map[string]Sink) 148 instances[conf.name] = jobs 149 } 150 sink, ok = jobs[job] 151 if ok { 152 return 153 } 154 155 switch conf.c.Type { 156 case metricsTypePrometheus: 157 switch conf.c.Mode { 158 case modePull: 159 sink = newPrometheusPull(conf.ctx, conf.appName, conf.name, job, conf) 160 case modePush: 161 sink = newPrometheusPush(conf.ctx, conf.appName, conf.name, job, conf.interval, conf) 162 } 163 case metricsTypeMock: 164 sink = newMock(conf.ctx, conf.appName, conf.name, job, conf) 165 default: 166 panic(errors.Errorf("unknown metrics type: %s", conf.c.Type)) 167 } 168 169 if sink == nil { 170 panic(errors.Errorf("unknown metrics mode: %s", conf.c.Mode)) 171 } 172 173 jobs[job] = sink 174 return 175 } 176 177 func Internal(opts ...utils.OptionExtender) (sinks []Sink) { 178 opt := utils.ApplyOptions[useOption](opts...) 179 appName := config.Use(opt.appName).AppName() 180 rwlock.Lock() 181 defer rwlock.Unlock() 182 cfgs, ok := cfgsMap[opt.appName] 183 if !ok { 184 return 185 } 186 for _, cfg := range cfgs { 187 if cfg.c.EnableInternalMetrics { 188 sinks = append(sinks, use(appName, cfg)) 189 } 190 } 191 return 192 } 193 194 func init() { 195 config.AddComponent(config.ComponentMetrics, Construct, config.WithFlag(&flagString)) 196 }