github.com/moqsien/xraycore@v1.8.5/app/metrics/metrics.go (about) 1 package metrics 2 3 import ( 4 "context" 5 "expvar" 6 "net/http" 7 _ "net/http/pprof" 8 "strings" 9 10 "github.com/moqsien/xraycore/app/observatory" 11 "github.com/moqsien/xraycore/app/stats" 12 "github.com/moqsien/xraycore/common" 13 "github.com/moqsien/xraycore/common/net" 14 "github.com/moqsien/xraycore/common/signal/done" 15 "github.com/moqsien/xraycore/core" 16 "github.com/moqsien/xraycore/features/extension" 17 "github.com/moqsien/xraycore/features/outbound" 18 feature_stats "github.com/moqsien/xraycore/features/stats" 19 ) 20 21 type MetricsHandler struct { 22 ohm outbound.Manager 23 statsManager feature_stats.Manager 24 observatory extension.Observatory 25 tag string 26 } 27 28 // NewMetricsHandler creates a new MetricsHandler based on the given config. 29 func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, error) { 30 c := &MetricsHandler{ 31 tag: config.Tag, 32 } 33 common.Must(core.RequireFeatures(ctx, func(om outbound.Manager, sm feature_stats.Manager) { 34 c.statsManager = sm 35 c.ohm = om 36 })) 37 expvar.Publish("stats", expvar.Func(func() interface{} { 38 manager, ok := c.statsManager.(*stats.Manager) 39 if !ok { 40 return nil 41 } 42 resp := map[string]map[string]map[string]int64{ 43 "inbound": {}, 44 "outbound": {}, 45 "user": {}, 46 } 47 manager.VisitCounters(func(name string, counter feature_stats.Counter) bool { 48 nameSplit := strings.Split(name, ">>>") 49 typeName, tagOrUser, direction := nameSplit[0], nameSplit[1], nameSplit[3] 50 if item, found := resp[typeName][tagOrUser]; found { 51 item[direction] = counter.Value() 52 } else { 53 resp[typeName][tagOrUser] = map[string]int64{ 54 direction: counter.Value(), 55 } 56 } 57 return true 58 }) 59 return resp 60 })) 61 expvar.Publish("observatory", expvar.Func(func() interface{} { 62 if c.observatory == nil { 63 common.Must(core.RequireFeatures(ctx, func(observatory extension.Observatory) error { 64 c.observatory = observatory 65 return nil 66 })) 67 if c.observatory == nil { 68 return nil 69 } 70 } 71 resp := map[string]*observatory.OutboundStatus{} 72 if o, err := c.observatory.GetObservation(context.Background()); err != nil { 73 return err 74 } else { 75 for _, x := range o.(*observatory.ObservationResult).GetStatus() { 76 resp[x.OutboundTag] = x 77 } 78 } 79 return resp 80 })) 81 return c, nil 82 } 83 84 func (p *MetricsHandler) Type() interface{} { 85 return (*MetricsHandler)(nil) 86 } 87 88 func (p *MetricsHandler) Start() error { 89 listener := &OutboundListener{ 90 buffer: make(chan net.Conn, 4), 91 done: done.New(), 92 } 93 94 go func() { 95 if err := http.Serve(listener, http.DefaultServeMux); err != nil { 96 newError("failed to start metrics server").Base(err).AtError().WriteToLog() 97 } 98 }() 99 100 if err := p.ohm.RemoveHandler(context.Background(), p.tag); err != nil { 101 newError("failed to remove existing handler").WriteToLog() 102 } 103 104 return p.ohm.AddHandler(context.Background(), &Outbound{ 105 tag: p.tag, 106 listener: listener, 107 }) 108 } 109 110 func (p *MetricsHandler) Close() error { 111 return nil 112 } 113 114 func init() { 115 common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { 116 return NewMetricsHandler(ctx, cfg.(*Config)) 117 })) 118 }