Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions app/dispatcher/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
}

if p.Stats.UserOnline {
name := "user>>>" + user.Email + ">>>online"
if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil {
userIP := sessionInbound.Source.Address.String()
om.AddIP(userIP)
context.AfterFunc(ctx, func() { om.RemoveIP(userIP) })
}
trackOnlineIP(ctx, d.stats, user.Email, sessionInbound.Source.Address.String())
}
}

Expand Down Expand Up @@ -220,18 +215,21 @@ func WrapLink(ctx context.Context, policyManager policy.Manager, statsManager st
}
}
if p.Stats.UserOnline {
name := "user>>>" + user.Email + ">>>online"
if om, _ := stats.GetOrRegisterOnlineMap(statsManager, name); om != nil {
userIP := sessionInbound.Source.Address.String()
om.AddIP(userIP)
context.AfterFunc(ctx, func() { om.RemoveIP(userIP) })
}
trackOnlineIP(ctx, statsManager, user.Email, sessionInbound.Source.Address.String())
}
}

return link
}

func trackOnlineIP(ctx context.Context, sm stats.Manager, email, ip string) {
name := "user>>>" + email + ">>>online"
if om, _ := stats.GetOrRegisterOnlineMap(sm, name); om != nil {
om.AddIP(ip)
context.AfterFunc(ctx, func() { om.RemoveIP(ip) })
}
}

func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
domain := result.Domain()
if domain == "" {
Expand Down
7 changes: 1 addition & 6 deletions app/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"strings"

"github.com/xtls/xray-core/app/observatory"
"github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
Expand Down Expand Up @@ -39,16 +38,12 @@ func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, er
c.ohm = om
}))
expvar.Publish("stats", expvar.Func(func() interface{} {
manager, ok := c.statsManager.(*stats.Manager)
if !ok {
return nil
}
resp := map[string]map[string]map[string]int64{
"inbound": {},
"outbound": {},
"user": {},
}
manager.VisitCounters(func(name string, counter feature_stats.Counter) bool {
c.statsManager.VisitCounters(func(name string, counter feature_stats.Counter) bool {
nameSplit := strings.Split(name, ">>>")
typeName, tagOrUser, direction := nameSplit[0], nameSplit[1], nameSplit[3]
if item, found := resp[typeName][tagOrUser]; found {
Expand Down
93 changes: 82 additions & 11 deletions app/stats/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ package command
import (
"context"
"runtime"
"strings"
"time"

"github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/core"
feature_stats "github.com/xtls/xray-core/features/stats"
Expand Down Expand Up @@ -70,9 +69,10 @@ func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStat
}

ips := make(map[string]int64)
for ip, t := range c.IPTimeMap() {
ips[ip] = t.Unix()
}
c.ForEach(func(ip string, lastSeen int64) bool {
ips[ip] = lastSeen
return true
})

return &GetStatsOnlineIpListResponse{
Name: request.Name,
Expand All @@ -86,6 +86,82 @@ func (s *statsServer) GetAllOnlineUsers(ctx context.Context, request *GetAllOnli
}, nil
}

func (s *statsServer) GetUsersStats(ctx context.Context, request *GetUsersStatsRequest) (*GetUsersStatsResponse, error) {
userMap := make(map[string]*UserStat)

s.stats.VisitOnlineMaps(func(name string, om feature_stats.OnlineMap) bool {
if om.Count() == 0 {
return true
}

_, rest, _ := strings.Cut(name, ">>>")
email, _, _ := strings.Cut(rest, ">>>")

user := &UserStat{Email: email}
om.ForEach(func(ip string, lastSeen int64) bool {
user.Ips = append(user.Ips, &OnlineIPEntry{
Ip: ip,
LastSeen: lastSeen,
})
return true
})
if len(user.Ips) > 0 {
userMap[email] = user
}
return true
})

if request.IncludeTraffic {
for _, u := range userMap {
u.Traffic = &TrafficUserStat{}
}
const (
prefixUser = "user>>>"
suffixUplink = ">>>traffic>>>uplink"
suffixDownlink = ">>>traffic>>>downlink"
)
s.stats.VisitCounters(func(name string, c feature_stats.Counter) bool {
var email string
var isUplink bool

if strings.HasSuffix(name, suffixUplink) {
email = name[len(prefixUser) : len(name)-len(suffixUplink)]
isUplink = true
} else if strings.HasSuffix(name, suffixDownlink) {
email = name[len(prefixUser) : len(name)-len(suffixDownlink)]
} else {
return true
}

u, ok := userMap[email]
if !ok {
return true
}

var value int64
if request.Reset_ {
value = c.Set(0)
} else {
value = c.Value()
}

if isUplink {
u.Traffic.Uplink = value
} else {
u.Traffic.Downlink = value
}
return true
})
}

resp := &GetUsersStatsResponse{}
for _, u := range userMap {
resp.Users = append(resp.Users, u)
}

return resp, nil
}

func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) {
matcher, err := strmatcher.Substr.New(request.Pattern)
if err != nil {
Expand All @@ -94,12 +170,7 @@ func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest

response := &QueryStatsResponse{}

manager, ok := s.stats.(*stats.Manager)
if !ok {
return nil, errors.New("QueryStats only works its own stats.Manager.")
}

manager.VisitCounters(func(name string, c feature_stats.Counter) bool {
s.stats.VisitCounters(func(name string, c feature_stats.Counter) bool {
if matcher.Match(name) {
var value int64
if request.Reset_ {
Expand Down
Loading