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  }