github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/observatory/burst/burstobserver.go (about) 1 package burst 2 3 import ( 4 "context" 5 "sync" 6 7 "github.com/golang/protobuf/proto" 8 9 core "github.com/v2fly/v2ray-core/v5" 10 "github.com/v2fly/v2ray-core/v5/app/observatory" 11 "github.com/v2fly/v2ray-core/v5/common" 12 "github.com/v2fly/v2ray-core/v5/common/signal/done" 13 "github.com/v2fly/v2ray-core/v5/features/extension" 14 "github.com/v2fly/v2ray-core/v5/features/outbound" 15 ) 16 17 type Observer struct { 18 config *Config 19 ctx context.Context 20 21 statusLock sync.Mutex // nolint: structcheck 22 hp *HealthPing 23 24 finished *done.Instance 25 26 ohm outbound.Manager 27 } 28 29 func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) { 30 return &observatory.ObservationResult{Status: o.createResult()}, nil 31 } 32 33 func (o *Observer) createResult() []*observatory.OutboundStatus { 34 var result []*observatory.OutboundStatus 35 o.hp.access.Lock() 36 defer o.hp.access.Unlock() 37 for name, value := range o.hp.Results { 38 status := observatory.OutboundStatus{ 39 Alive: value.getStatistics().All != value.getStatistics().Fail, 40 Delay: value.getStatistics().Average.Milliseconds(), 41 LastErrorReason: "", 42 OutboundTag: name, 43 LastSeenTime: 0, 44 LastTryTime: 0, 45 HealthPing: &observatory.HealthPingMeasurementResult{ 46 All: int64(value.getStatistics().All), 47 Fail: int64(value.getStatistics().Fail), 48 Deviation: int64(value.getStatistics().Deviation), 49 Average: int64(value.getStatistics().Average), 50 Max: int64(value.getStatistics().Max), 51 Min: int64(value.getStatistics().Min), 52 }, 53 } 54 result = append(result, &status) 55 } 56 return result 57 } 58 59 func (o *Observer) Type() interface{} { 60 return extension.ObservatoryType() 61 } 62 63 func (o *Observer) Start() error { 64 if o.config != nil && len(o.config.SubjectSelector) != 0 { 65 o.finished = done.New() 66 o.hp.StartScheduler(func() ([]string, error) { 67 hs, ok := o.ohm.(outbound.HandlerSelector) 68 if !ok { 69 return nil, newError("outbound.Manager is not a HandlerSelector") 70 } 71 72 outbounds := hs.Select(o.config.SubjectSelector) 73 return outbounds, nil 74 }) 75 } 76 return nil 77 } 78 79 func (o *Observer) Close() error { 80 if o.finished != nil { 81 o.hp.StopScheduler() 82 return o.finished.Close() 83 } 84 return nil 85 } 86 87 func New(ctx context.Context, config *Config) (*Observer, error) { 88 var outboundManager outbound.Manager 89 err := core.RequireFeatures(ctx, func(om outbound.Manager) { 90 outboundManager = om 91 }) 92 if err != nil { 93 return nil, newError("Cannot get depended features").Base(err) 94 } 95 hp := NewHealthPing(ctx, config.PingConfig) 96 return &Observer{ 97 config: config, 98 ctx: ctx, 99 ohm: outboundManager, 100 hp: hp, 101 }, nil 102 } 103 104 func init() { 105 common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { 106 return New(ctx, config.(*Config)) 107 })) 108 }