Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/data/
/dist/
**/.DS_Store
16 changes: 16 additions & 0 deletions cmd/cmd_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ import (
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/service"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric/noop"

"gopkg.in/ini.v1"

"github.com/getlantern/lantern-box/adapter"
"github.com/getlantern/lantern-box/tracker/clientcontext"
"github.com/getlantern/lantern-box/tracker/datacap"
"github.com/getlantern/lantern-box/tracker/metrics"
)

func init() {
Expand Down Expand Up @@ -114,6 +118,18 @@ func create(configPath string, datacapURL string) (*box.Box, context.CancelFunc,
log.Warn("Datacap URL not provided, datacap tracking disabled")
}

mp := otel.GetMeterProvider()
if _, ok := mp.(noop.MeterProvider); ok {
log.Info("Metrics not enabled, no meter provider configured")
} else {
metricsTracker, err := metrics.NewTracker()
if err != nil {
return nil, nil, fmt.Errorf("create metrics tracker: %w", err)
}
instance.Router().AppendTracker(metricsTracker)
log.Info("Metric Tracking Enabled")
}

osSignals := make(chan os.Signal, 1)
signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
defer func() {
Expand Down
11 changes: 7 additions & 4 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import (
"os"
"strings"

"github.com/spf13/cobra"

box "github.com/getlantern/lantern-box"
"github.com/getlantern/lantern-box/metrics"
"github.com/getlantern/lantern-box/otel"
"github.com/getlantern/lantern-box/tracker/metrics"
"github.com/spf13/cobra"
sdkotel "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric/noop"
)

type ProxyInfo struct {
Expand Down Expand Up @@ -40,6 +41,9 @@ var rootCmd = &cobra.Command{
func preRun(cmd *cobra.Command, args []string) {
globalCtx = box.BaseContext()

// Default to not report metrics
sdkotel.SetMeterProvider(noop.NewMeterProvider())

path, err := cmd.Flags().GetString("config")
if err != nil {
return
Expand All @@ -61,7 +65,6 @@ func preRun(cmd *cobra.Command, args []string) {
}

proxyInfoPath := strings.Replace(path, ".json", ".ini", 1)

proxyInfo, err := readProxyInfoFile(proxyInfoPath)
if err != nil {
return
Expand Down
19 changes: 12 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ require (
github.com/spf13/cobra v1.10.1
github.com/stretchr/testify v1.11.1
github.com/tetratelabs/wazero v1.7.3
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel/metric v1.38.0
go.opentelemetry.io/otel/trace v1.38.0
go.opentelemetry.io/otel v1.39.0
go.opentelemetry.io/otel/metric v1.39.0
go.opentelemetry.io/otel/trace v1.39.0
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201 // indirect
github.com/getlantern/golog v0.0.0-20230503153817-8e72de7e0a65 // indirect
Expand All @@ -50,11 +51,15 @@ require (
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/mholt/archiver/v3 v3.5.1 // indirect
github.com/nwaples/rardecode v1.1.2 // indirect
github.com/ormanli/otel-metric-mocks v0.0.0-20231210142449-f7870276828c // indirect
github.com/oschwald/geoip2-golang v1.9.0 // indirect
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.39.0 // indirect
go.opentelemetry.io/otel/log v0.14.0 // indirect
go.opentelemetry.io/otel/sdk/log v0.14.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846 // indirect
Expand Down Expand Up @@ -215,10 +220,10 @@ require (
github.com/x448/float16 v0.8.4 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0
go.opentelemetry.io/otel/sdk v1.38.0
go.opentelemetry.io/otel/sdk/metric v1.38.0
go.opentelemetry.io/otel/sdk v1.39.0
go.opentelemetry.io/otel/sdk/metric v1.39.0
go.uber.org/mock v0.5.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
Expand All @@ -229,7 +234,7 @@ require (
golang.org/x/mod v0.27.0 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/term v0.34.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/time v0.9.0 // indirect
Expand Down
26 changes: 26 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/X
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
Expand Down Expand Up @@ -408,6 +410,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/ormanli/otel-metric-mocks v0.0.0-20231210142449-f7870276828c h1:R4zAvQ5tyreskBUJACF6pKDFit614OqCn7/N1cCBxhw=
github.com/ormanli/otel-metric-mocks v0.0.0-20231210142449-f7870276828c/go.mod h1:gdSnRpwpEvrBkn5Z/6gFsVYnFwXXOtDpDxY+uo8nQOo=
github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=
github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
Expand Down Expand Up @@ -637,20 +641,40 @@ go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.15.0 h1:0BSddrtQqLEylcErkeFrJBmwFzcqfQq9+/uxfTZq+HE=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.15.0/go.mod h1:87sjYuAPzaRCtdd09GU5gM1U9wQLrrcYrm77mh5EBoc=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.39.0 h1:5gn2urDL/FBnK8OkCfD1j3/ER79rUuTYmCvlXBKeYL8=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.39.0/go.mod h1:0fBG6ZJxhqByfFZDwSwpZGzJU671HkwpWaNe2t4VUPI=
go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM=
go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg=
go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
Expand Down Expand Up @@ -792,6 +816,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
Expand Down
2 changes: 0 additions & 2 deletions protocol/algeneva/inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/gobwas/ws"

"github.com/getlantern/lantern-box/constant"
"github.com/getlantern/lantern-box/metrics"
"github.com/getlantern/lantern-box/option"

"github.com/sagernet/sing-box/adapter"
Expand Down Expand Up @@ -48,7 +47,6 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
func (a *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose network.CloseHandlerFunc) {
metadata.Inbound = a.Tag()
metadata.InboundType = a.Type()
conn = metrics.NewConn(conn, &metadata)
conn, err := a.newConnectionEx(ctx, conn)
if err != nil {
network.CloseOnHandshakeFailure(conn, onClose, err)
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
25 changes: 25 additions & 0 deletions tracker/metrics/tracker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package metrics

import (
"context"
"net"

"github.com/sagernet/sing-box/adapter"
N "github.com/sagernet/sing/common/network"
)

var _ (adapter.ConnectionTracker) = (*MetricsTracker)(nil)

type MetricsTracker struct{}

func NewTracker() (*MetricsTracker, error) {
return &MetricsTracker{}, nil
}

func (t *MetricsTracker) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule, matchOutbound adapter.Outbound) net.Conn {
return NewConn(conn, &metadata)
}

func (t *MetricsTracker) RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, matchedRule adapter.Rule, matchOutbound adapter.Outbound) N.PacketConn {
return NewPacketConn(conn, &metadata)
}
102 changes: 102 additions & 0 deletions tracker/metrics/tracker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package metrics

import (
"context"
"net"
"strings"
"sync"
"testing"

sdkotel "go.opentelemetry.io/otel"

"github.com/sagernet/sing-box/adapter"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/metricdata"

"github.com/stretchr/testify/assert"
)

func TestTracker(t *testing.T) {
var wg sync.WaitGroup
reader := metric.NewManualReader()
provider := metric.NewMeterProvider(metric.WithReader(reader))
sdkotel.SetMeterProvider(provider)

SetupMetricsManager("", "")

metricsTracker, err := NewTracker()
assert.NoError(t, err)

client, server := net.Pipe()
defer client.Close()
defer server.Close()

ctx := context.Background()
serverTracked := metricsTracker.RoutedConnection(ctx, server, adapter.InboundContext{}, nil, nil)

clientSentMessage := []byte("A client sent a short request...")
wg.Add(1)
serverReceive := 0
go func() {
defer wg.Done()
buf := make([]byte, len(clientSentMessage))
serverReceive, err = serverTracked.Read(buf)
assert.NoError(t, err)
}()

_, err = client.Write(clientSentMessage)
assert.NoError(t, err)

serverSentMessage := []byte("...and the server sent a short response.")
wg.Add(1)
go func() {
defer wg.Done()
buf := make([]byte, len(serverSentMessage))
_, err = client.Read(buf)
assert.NoError(t, err)
}()

serverTransmit, err := serverTracked.Write(serverSentMessage)
assert.NoError(t, err)

wg.Wait()
var rm metricdata.ResourceMetrics
reader.Collect(ctx, &rm)

ioCounter := extractCountersByAttribute(rm, "proxy.io")
results := map[string]int64{}
for k, v := range ioCounter {
if strings.Contains(k, "direction=transmit") {
results["transmit"] += v
} else if strings.Contains(k, "direction=receive") {
results["receive"] += v
}
}
for k, v := range results {
t.Logf("%s: %d\n", k, v)
}
if results["transmit"] != int64(serverTransmit) {
t.Errorf("transmit bytes did not match, got %d, want %d", results["transmit"], serverTransmit)
}
if results["receive"] != int64(serverReceive) {
t.Errorf("receive bytes did not match, got %d, want %d", results["receive"], serverReceive)
}

}

func extractCountersByAttribute(rm metricdata.ResourceMetrics, name string) map[string]int64 {
result := make(map[string]int64)
for _, sm := range rm.ScopeMetrics {
for _, m := range sm.Metrics {
if m.Name == name {
sum := m.Data.(metricdata.Sum[int64])
for _, dp := range sum.DataPoints {
key := dp.Attributes.Encoded(attribute.DefaultEncoder())
result[string(key)] = dp.Value
}
}
}
}
return result
}
Loading