github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/app/observatory/burst/burstobserver.go (about)

     1  package burst
     2  
     3  import (
     4  	"context"
     5  	
     6  	"github.com/xtls/xray-core/core"
     7  	"github.com/xtls/xray-core/app/observatory"
     8  	"github.com/xtls/xray-core/common"
     9  	"github.com/xtls/xray-core/common/signal/done"
    10  	"github.com/xtls/xray-core/features/extension"
    11  	"github.com/xtls/xray-core/features/outbound"
    12  	"google.golang.org/protobuf/proto"
    13  	"sync"
    14  )
    15  
    16  type Observer struct {
    17  	config *Config
    18  	ctx    context.Context
    19  
    20  	statusLock sync.Mutex
    21  	hp         *HealthPing
    22  
    23  	finished *done.Instance
    24  
    25  	ohm outbound.Manager
    26  }
    27  
    28  func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) {
    29  	return &observatory.ObservationResult{Status: o.createResult()}, nil
    30  }
    31  
    32  func (o *Observer) createResult() []*observatory.OutboundStatus {
    33  	var result []*observatory.OutboundStatus
    34  	o.hp.access.Lock()
    35  	defer o.hp.access.Unlock()
    36  	for name, value := range o.hp.Results {
    37  		status := observatory.OutboundStatus{
    38  			Alive:           value.getStatistics().All != value.getStatistics().Fail,
    39  			Delay:           value.getStatistics().Average.Milliseconds(),
    40  			LastErrorReason: "",
    41  			OutboundTag:     name,
    42  			LastSeenTime:    0,
    43  			LastTryTime:     0,
    44  			HealthPing: &observatory.HealthPingMeasurementResult{
    45  				All:       int64(value.getStatistics().All),
    46  				Fail:      int64(value.getStatistics().Fail),
    47  				Deviation: int64(value.getStatistics().Deviation),
    48  				Average:   int64(value.getStatistics().Average),
    49  				Max:       int64(value.getStatistics().Max),
    50  				Min:       int64(value.getStatistics().Min),
    51  			},
    52  		}
    53  		result = append(result, &status)
    54  	}
    55  	return result
    56  }
    57  
    58  func (o *Observer) Type() interface{} {
    59  	return extension.ObservatoryType()
    60  }
    61  
    62  func (o *Observer) Start() error {
    63  	if o.config != nil && len(o.config.SubjectSelector) != 0 {
    64  		o.finished = done.New()
    65  		o.hp.StartScheduler(func() ([]string, error) {
    66  			hs, ok := o.ohm.(outbound.HandlerSelector)
    67  			if !ok {
    68  
    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  }