stats.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. package v2rayapi
  2. import (
  3. "context"
  4. "net"
  5. "regexp"
  6. "runtime"
  7. "strings"
  8. "sync"
  9. "time"
  10. "github.com/sagernet/sing-box/adapter"
  11. "github.com/sagernet/sing-box/experimental/trackerconn"
  12. "github.com/sagernet/sing-box/option"
  13. E "github.com/sagernet/sing/common/exceptions"
  14. N "github.com/sagernet/sing/common/network"
  15. "go.uber.org/atomic"
  16. )
  17. func init() {
  18. StatsService_ServiceDesc.ServiceName = "v2ray.core.app.stats.command.StatsService"
  19. }
  20. var (
  21. _ adapter.V2RayStatsService = (*StatsService)(nil)
  22. _ StatsServiceServer = (*StatsService)(nil)
  23. )
  24. type StatsService struct {
  25. createdAt time.Time
  26. inbounds map[string]bool
  27. outbounds map[string]bool
  28. access sync.Mutex
  29. counters map[string]*atomic.Int64
  30. }
  31. func NewStatsService(options option.V2RayStatsServiceOptions) *StatsService {
  32. if !options.Enabled {
  33. return nil
  34. }
  35. inbounds := make(map[string]bool)
  36. outbounds := make(map[string]bool)
  37. for _, inbound := range options.Inbounds {
  38. inbounds[inbound] = true
  39. }
  40. for _, outbound := range options.Outbounds {
  41. outbounds[outbound] = true
  42. }
  43. return &StatsService{
  44. createdAt: time.Now(),
  45. inbounds: inbounds,
  46. outbounds: outbounds,
  47. counters: make(map[string]*atomic.Int64),
  48. }
  49. }
  50. func (s *StatsService) RoutedConnection(inbound string, outbound string, conn net.Conn) net.Conn {
  51. var readCounter []*atomic.Int64
  52. var writeCounter []*atomic.Int64
  53. countInbound := inbound != "" && s.inbounds[inbound]
  54. countOutbound := outbound != "" && s.outbounds[outbound]
  55. if !countInbound && !countOutbound {
  56. return conn
  57. }
  58. s.access.Lock()
  59. if countInbound {
  60. readCounter = append(readCounter, s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>uplink"))
  61. writeCounter = append(writeCounter, s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>downlink"))
  62. }
  63. if countOutbound {
  64. readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink"))
  65. writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
  66. }
  67. s.access.Unlock()
  68. return trackerconn.New(conn, readCounter, writeCounter)
  69. }
  70. func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, conn N.PacketConn) N.PacketConn {
  71. var readCounter []*atomic.Int64
  72. var writeCounter []*atomic.Int64
  73. countInbound := inbound != "" && s.inbounds[inbound]
  74. countOutbound := outbound != "" && s.outbounds[outbound]
  75. if !countInbound && !countOutbound {
  76. return conn
  77. }
  78. s.access.Lock()
  79. if countInbound {
  80. readCounter = append(readCounter, s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>uplink"))
  81. writeCounter = append(writeCounter, s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>downlink"))
  82. }
  83. if countOutbound {
  84. readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink"))
  85. writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
  86. }
  87. s.access.Unlock()
  88. return trackerconn.NewPacket(conn, readCounter, writeCounter)
  89. }
  90. func (s *StatsService) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
  91. s.access.Lock()
  92. counter, loaded := s.counters[request.Name]
  93. s.access.Unlock()
  94. if !loaded {
  95. return nil, E.New(request.Name, " not found.")
  96. }
  97. var value int64
  98. if request.Reset_ {
  99. value = counter.Swap(0)
  100. } else {
  101. value = counter.Load()
  102. }
  103. return &GetStatsResponse{Stat: &Stat{Name: request.Name, Value: value}}, nil
  104. }
  105. func (s *StatsService) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) {
  106. var response QueryStatsResponse
  107. s.access.Lock()
  108. defer s.access.Unlock()
  109. if len(request.Patterns) == 0 {
  110. for name, counter := range s.counters {
  111. var value int64
  112. if request.Reset_ {
  113. value = counter.Swap(0)
  114. } else {
  115. value = counter.Load()
  116. }
  117. response.Stat = append(response.Stat, &Stat{Name: name, Value: value})
  118. }
  119. } else if request.Regexp {
  120. matchers := make([]*regexp.Regexp, 0, len(request.Patterns))
  121. for _, pattern := range request.Patterns {
  122. matcher, err := regexp.Compile(pattern)
  123. if err != nil {
  124. return nil, err
  125. }
  126. matchers = append(matchers, matcher)
  127. }
  128. for name, counter := range s.counters {
  129. for _, matcher := range matchers {
  130. if matcher.MatchString(name) {
  131. var value int64
  132. if request.Reset_ {
  133. value = counter.Swap(0)
  134. } else {
  135. value = counter.Load()
  136. }
  137. response.Stat = append(response.Stat, &Stat{Name: name, Value: value})
  138. }
  139. }
  140. }
  141. } else {
  142. for name, counter := range s.counters {
  143. for _, matcher := range request.Patterns {
  144. if strings.Contains(name, matcher) {
  145. var value int64
  146. if request.Reset_ {
  147. value = counter.Swap(0)
  148. } else {
  149. value = counter.Load()
  150. }
  151. response.Stat = append(response.Stat, &Stat{Name: name, Value: value})
  152. }
  153. }
  154. }
  155. }
  156. return &response, nil
  157. }
  158. func (s *StatsService) GetSysStats(ctx context.Context, request *SysStatsRequest) (*SysStatsResponse, error) {
  159. var rtm runtime.MemStats
  160. runtime.ReadMemStats(&rtm)
  161. response := &SysStatsResponse{
  162. Uptime: uint32(time.Now().Sub(s.createdAt).Seconds()),
  163. NumGoroutine: uint32(runtime.NumGoroutine()),
  164. Alloc: rtm.Alloc,
  165. TotalAlloc: rtm.TotalAlloc,
  166. Sys: rtm.Sys,
  167. Mallocs: rtm.Mallocs,
  168. Frees: rtm.Frees,
  169. LiveObjects: rtm.Mallocs - rtm.Frees,
  170. NumGC: rtm.NumGC,
  171. PauseTotalNs: rtm.PauseTotalNs,
  172. }
  173. return response, nil
  174. }
  175. func (s *StatsService) mustEmbedUnimplementedStatsServiceServer() {
  176. }
  177. //nolint:staticcheck
  178. func (s *StatsService) loadOrCreateCounter(name string) *atomic.Int64 {
  179. counter, loaded := s.counters[name]
  180. if loaded {
  181. return counter
  182. }
  183. counter = atomic.NewInt64(0)
  184. s.counters[name] = counter
  185. return counter
  186. }