stats.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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. users map[string]bool
  29. access sync.Mutex
  30. counters map[string]*atomic.Int64
  31. }
  32. func NewStatsService(options option.V2RayStatsServiceOptions) *StatsService {
  33. if !options.Enabled {
  34. return nil
  35. }
  36. inbounds := make(map[string]bool)
  37. outbounds := make(map[string]bool)
  38. users := make(map[string]bool)
  39. for _, inbound := range options.Inbounds {
  40. inbounds[inbound] = true
  41. }
  42. for _, outbound := range options.Outbounds {
  43. outbounds[outbound] = true
  44. }
  45. for _, user := range options.Users {
  46. users[user] = true
  47. }
  48. return &StatsService{
  49. createdAt: time.Now(),
  50. inbounds: inbounds,
  51. outbounds: outbounds,
  52. users: users,
  53. counters: make(map[string]*atomic.Int64),
  54. }
  55. }
  56. func (s *StatsService) RoutedConnection(inbound string, outbound string, user string, conn net.Conn) net.Conn {
  57. var readCounter []*atomic.Int64
  58. var writeCounter []*atomic.Int64
  59. countInbound := inbound != "" && s.inbounds[inbound]
  60. countOutbound := outbound != "" && s.outbounds[outbound]
  61. countUser := user != "" && s.users[user]
  62. if !countInbound && !countOutbound && !countUser {
  63. return conn
  64. }
  65. s.access.Lock()
  66. if countInbound {
  67. readCounter = append(readCounter, s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>uplink"))
  68. writeCounter = append(writeCounter, s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>downlink"))
  69. }
  70. if countOutbound {
  71. readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink"))
  72. writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
  73. }
  74. if countUser {
  75. readCounter = append(readCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>uplink"))
  76. writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
  77. }
  78. s.access.Unlock()
  79. return trackerconn.New(conn, readCounter, writeCounter)
  80. }
  81. func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, user string, conn N.PacketConn) N.PacketConn {
  82. var readCounter []*atomic.Int64
  83. var writeCounter []*atomic.Int64
  84. countInbound := inbound != "" && s.inbounds[inbound]
  85. countOutbound := outbound != "" && s.outbounds[outbound]
  86. countUser := user != "" && s.users[user]
  87. if !countInbound && !countOutbound && !countUser {
  88. return conn
  89. }
  90. s.access.Lock()
  91. if countInbound {
  92. readCounter = append(readCounter, s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>uplink"))
  93. writeCounter = append(writeCounter, s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>downlink"))
  94. }
  95. if countOutbound {
  96. readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink"))
  97. writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
  98. }
  99. if countUser {
  100. readCounter = append(readCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>uplink"))
  101. writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
  102. }
  103. s.access.Unlock()
  104. return trackerconn.NewPacket(conn, readCounter, writeCounter)
  105. }
  106. func (s *StatsService) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
  107. s.access.Lock()
  108. counter, loaded := s.counters[request.Name]
  109. s.access.Unlock()
  110. if !loaded {
  111. return nil, E.New(request.Name, " not found.")
  112. }
  113. var value int64
  114. if request.Reset_ {
  115. value = counter.Swap(0)
  116. } else {
  117. value = counter.Load()
  118. }
  119. return &GetStatsResponse{Stat: &Stat{Name: request.Name, Value: value}}, nil
  120. }
  121. func (s *StatsService) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) {
  122. var response QueryStatsResponse
  123. s.access.Lock()
  124. defer s.access.Unlock()
  125. if len(request.Patterns) == 0 {
  126. for name, counter := range s.counters {
  127. var value int64
  128. if request.Reset_ {
  129. value = counter.Swap(0)
  130. } else {
  131. value = counter.Load()
  132. }
  133. response.Stat = append(response.Stat, &Stat{Name: name, Value: value})
  134. }
  135. } else if request.Regexp {
  136. matchers := make([]*regexp.Regexp, 0, len(request.Patterns))
  137. for _, pattern := range request.Patterns {
  138. matcher, err := regexp.Compile(pattern)
  139. if err != nil {
  140. return nil, err
  141. }
  142. matchers = append(matchers, matcher)
  143. }
  144. for name, counter := range s.counters {
  145. for _, matcher := range matchers {
  146. if matcher.MatchString(name) {
  147. var value int64
  148. if request.Reset_ {
  149. value = counter.Swap(0)
  150. } else {
  151. value = counter.Load()
  152. }
  153. response.Stat = append(response.Stat, &Stat{Name: name, Value: value})
  154. }
  155. }
  156. }
  157. } else {
  158. for name, counter := range s.counters {
  159. for _, matcher := range request.Patterns {
  160. if strings.Contains(name, matcher) {
  161. var value int64
  162. if request.Reset_ {
  163. value = counter.Swap(0)
  164. } else {
  165. value = counter.Load()
  166. }
  167. response.Stat = append(response.Stat, &Stat{Name: name, Value: value})
  168. }
  169. }
  170. }
  171. }
  172. return &response, nil
  173. }
  174. func (s *StatsService) GetSysStats(ctx context.Context, request *SysStatsRequest) (*SysStatsResponse, error) {
  175. var rtm runtime.MemStats
  176. runtime.ReadMemStats(&rtm)
  177. response := &SysStatsResponse{
  178. Uptime: uint32(time.Now().Sub(s.createdAt).Seconds()),
  179. NumGoroutine: uint32(runtime.NumGoroutine()),
  180. Alloc: rtm.Alloc,
  181. TotalAlloc: rtm.TotalAlloc,
  182. Sys: rtm.Sys,
  183. Mallocs: rtm.Mallocs,
  184. Frees: rtm.Frees,
  185. LiveObjects: rtm.Mallocs - rtm.Frees,
  186. NumGC: rtm.NumGC,
  187. PauseTotalNs: rtm.PauseTotalNs,
  188. }
  189. return response, nil
  190. }
  191. func (s *StatsService) mustEmbedUnimplementedStatsServiceServer() {
  192. }
  193. //nolint:staticcheck
  194. func (s *StatsService) loadOrCreateCounter(name string) *atomic.Int64 {
  195. counter, loaded := s.counters[name]
  196. if loaded {
  197. return counter
  198. }
  199. counter = atomic.NewInt64(0)
  200. s.counters[name] = counter
  201. return counter
  202. }