Skip to content

Commit 3329d16

Browse files
committed
refactor(metrics): make IO reporting and country lookup asynchronous
1 parent 7e099a4 commit 3329d16

File tree

8 files changed

+207
-156
lines changed

8 files changed

+207
-156
lines changed

cmd/cmd_run.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,7 @@ func create(configPath string, datacapURL string) (*box.Box, context.CancelFunc,
122122
if _, ok := mp.(noop.MeterProvider); ok {
123123
log.Info("Metrics not enabled, no meter provider configured")
124124
} else {
125-
metricsTracker, err := metrics.NewTracker()
126-
if err != nil {
127-
return nil, nil, fmt.Errorf("create metrics tracker: %w", err)
128-
}
125+
metricsTracker := metrics.NewTracker(ctx)
129126
instance.Router().AppendTracker(metricsTracker)
130127
log.Info("Metric Tracking Enabled")
131128
}

cmd/main.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ import (
55
"fmt"
66
"os"
77
"strings"
8+
"time"
89

9-
box "github.com/getlantern/lantern-box"
10-
"github.com/getlantern/lantern-box/otel"
11-
"github.com/getlantern/lantern-box/tracker/metrics"
10+
"github.com/getlantern/geo"
1211
"github.com/spf13/cobra"
1312
sdkotel "go.opentelemetry.io/otel"
1413
"go.opentelemetry.io/otel/metric/noop"
14+
15+
box "github.com/getlantern/lantern-box"
16+
"github.com/getlantern/lantern-box/otel"
17+
"github.com/getlantern/lantern-box/tracker/metrics"
1518
)
1619

1720
type ProxyInfo struct {
@@ -81,7 +84,8 @@ func preRun(cmd *cobra.Command, args []string) {
8184
ProxyProtocol: proxyInfo.Protocol,
8285
})
8386

84-
metrics.SetupMetricsManager(geoCityURL, cityDatabaseName)
87+
geolookup := geo.FromWeb(geoCityURL, cityDatabaseName, 24*time.Hour, cityDatabaseName, geo.CountryCode)
88+
metrics.SetupMetricsManager(geolookup)
8589
}
8690

8791
func main() {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/getlantern/lantern-box
22

3-
go 1.24.1
3+
go 1.25.1
44

55
replace github.com/sagernet/sing => github.com/getlantern/sing v0.7.18-lantern
66

tracker/metrics/conn.go

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,33 @@
11
package metrics
22

33
import (
4-
"context"
54
"net"
65
"time"
7-
8-
"github.com/sagernet/sing-box/adapter"
9-
"go.opentelemetry.io/otel/attribute"
10-
"go.opentelemetry.io/otel/metric"
116
)
127

138
// Conn wraps a net.Conn and tracks metrics such as bytes sent and received.
149
type Conn struct {
1510
net.Conn
16-
attributes []attribute.KeyValue
17-
startTime time.Time
11+
attrs *attributes
12+
tracker *MetricsTracker
13+
startTime time.Time
1814
}
1915

2016
// NewConn creates a new Conn instance.
21-
func NewConn(conn net.Conn, metadata *adapter.InboundContext) net.Conn {
22-
attributes := metadataToAttributes(metadata)
23-
metrics.Connections.Add(context.Background(), 1, metric.WithAttributes(attributes...))
17+
func NewConn(conn net.Conn, attrs *attributes, tracker *MetricsTracker) net.Conn {
2418
return &Conn{
25-
Conn: conn,
26-
attributes: attributes,
27-
startTime: time.Now(),
19+
Conn: conn,
20+
attrs: attrs,
21+
tracker: tracker,
22+
startTime: time.Now(),
2823
}
2924
}
3025

3126
// Read overrides net.Conn's Read method to track received bytes.
3227
func (c *Conn) Read(b []byte) (n int, err error) {
3328
n, err = c.Conn.Read(b)
3429
if n > 0 {
35-
attrs := append(c.attributes, attribute.KeyValue{Key: "direction", Value: attribute.StringValue("receive")})
36-
metrics.ProxyIO.Add(context.Background(), int64(n), metric.WithAttributes(attrs...))
30+
c.tracker.TrackIO(rx, n, c.attrs)
3731
}
3832
return
3933
}
@@ -42,17 +36,15 @@ func (c *Conn) Read(b []byte) (n int, err error) {
4236
func (c *Conn) Write(b []byte) (n int, err error) {
4337
n, err = c.Conn.Write(b)
4438
if n > 0 {
45-
attrs := append(c.attributes, attribute.KeyValue{Key: "direction", Value: attribute.StringValue("transmit")})
46-
metrics.ProxyIO.Add(context.Background(), int64(n), metric.WithAttributes(attrs...))
39+
c.tracker.TrackIO(tx, n, c.attrs)
4740
}
4841
return
4942
}
5043

5144
// Close overrides net.Conn's Close method to track connection duration.
5245
func (c *Conn) Close() error {
5346
duration := time.Since(c.startTime).Milliseconds()
54-
metrics.duration.Record(context.Background(), duration, metric.WithAttributes(c.attributes...))
55-
metrics.conns.Add(context.Background(), -1, metric.WithAttributes(c.attributes...))
47+
c.tracker.Leave(duration, c.attrs)
5648
return c.Conn.Close()
5749
}
5850

tracker/metrics/metrics.go

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,8 @@
66
package metrics
77

88
import (
9-
"time"
10-
119
"github.com/getlantern/geo"
12-
"github.com/sagernet/sing-box/adapter"
13-
"github.com/sagernet/sing-box/log"
1410
"go.opentelemetry.io/otel"
15-
"go.opentelemetry.io/otel/attribute"
1611
"go.opentelemetry.io/otel/metric"
1712
"go.opentelemetry.io/otel/metric/noop"
1813
)
@@ -27,55 +22,39 @@ type metricsManager struct {
2722
countryLookup geo.CountryLookup
2823
}
2924

30-
var metrics = &metricsManager{}
25+
var metrics = &metricsManager{
26+
ProxyIO: &noop.Int64Counter{},
27+
Connections: &noop.Int64Counter{},
28+
conns: &noop.Int64UpDownCounter{},
29+
duration: &noop.Int64Histogram{},
30+
countryLookup: geo.NoLookup{},
31+
}
3132

32-
func SetupMetricsManager(geolite2CityURL, cityDBFile string) {
33+
func SetupMetricsManager(countryLookup geo.CountryLookup) {
3334
meter := otel.GetMeterProvider().Meter("lantern-box")
3435

35-
pIO, err := meter.Int64Counter("proxy.io", metric.WithUnit("bytes"))
36-
if err != nil {
37-
pIO = &noop.Int64Counter{}
36+
if pIO, err := meter.Int64Counter("proxy.io", metric.WithUnit("bytes")); err == nil {
37+
metrics.ProxyIO = pIO
3838
}
3939

40-
connections, err := meter.Int64Counter("proxy.connections")
41-
if err != nil {
42-
connections = &noop.Int64Counter{}
40+
if connections, err := meter.Int64Counter("proxy.connections"); err == nil {
41+
metrics.Connections = connections
4342
}
4443

4544
// Track the number of connections.
4645
conns, err := meter.Int64UpDownCounter("sing.connections", metric.WithDescription("Number of connections"))
47-
if err != nil {
48-
conns = &noop.Int64UpDownCounter{}
46+
if err == nil {
47+
metrics.conns = conns
4948
}
5049

5150
// Track connection duration.
5251
duration, err := meter.Int64Histogram("sing.connection_duration", metric.WithDescription("Connection duration"))
53-
if err != nil {
54-
duration = &noop.Int64Histogram{}
52+
if err == nil {
53+
metrics.duration = duration
5554
}
5655

57-
metrics.meter = meter
58-
metrics.ProxyIO = pIO
59-
metrics.duration = duration
60-
metrics.Connections = connections
61-
metrics.conns = conns
62-
63-
metrics.countryLookup = geo.FromWeb(geolite2CityURL, cityDBFile, 24*time.Hour, cityDBFile, geo.CountryCode)
64-
if metrics.countryLookup == nil {
65-
metrics.countryLookup = geo.NoLookup{}
66-
}
67-
68-
log.Info("metrics manager set up completed")
69-
}
70-
71-
func metadataToAttributes(metadata *adapter.InboundContext) []attribute.KeyValue {
72-
// Convert metadata to attributes
73-
fromCountry := metrics.countryLookup.CountryCode(metadata.Source.IPAddr().IP)
74-
return []attribute.KeyValue{
75-
attribute.String("country", fromCountry),
76-
attribute.String("protocol", metadata.Protocol),
77-
attribute.String("inbound", metadata.Inbound),
78-
attribute.String("inbound_type", metadata.InboundType),
79-
attribute.String("outbound", metadata.Outbound),
56+
if countryLookup != nil {
57+
metrics.countryLookup = countryLookup
8058
}
59+
metrics.meter = meter
8160
}

tracker/metrics/packet_conn.go

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,27 @@
11
package metrics
22

33
import (
4-
"context"
54
"time"
65

7-
"github.com/sagernet/sing-box/adapter"
86
"github.com/sagernet/sing/common/buf"
97
M "github.com/sagernet/sing/common/metadata"
108
N "github.com/sagernet/sing/common/network"
11-
"go.opentelemetry.io/otel/attribute"
12-
"go.opentelemetry.io/otel/metric"
139
)
1410

1511
// PacketConn wraps a sing-box network.PacketConn and tracks metrics such as bytes sent and received.
1612
type PacketConn struct {
1713
N.PacketConn
18-
attributes []attribute.KeyValue
19-
startTime time.Time
14+
attrs *attributes
15+
tracker *MetricsTracker
16+
startTime time.Time
2017
}
2118

2219
// NewPacketConn creates a new PacketConn instance.
23-
func NewPacketConn(conn N.PacketConn, metadata *adapter.InboundContext) N.PacketConn {
24-
attributes := metadataToAttributes(metadata)
25-
metrics.Connections.Add(context.Background(), 1, metric.WithAttributes(attributes...))
26-
20+
func NewPacketConn(conn N.PacketConn, attrs *attributes, tracker *MetricsTracker) N.PacketConn {
2721
return &PacketConn{
2822
PacketConn: conn,
29-
attributes: attributes,
23+
attrs: attrs,
24+
tracker: tracker,
3025
startTime: time.Now(),
3126
}
3227
}
@@ -38,26 +33,23 @@ func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, er
3833
return dest, err
3934
}
4035
if buffer.Len() > 0 {
41-
attrs := append(c.attributes, attribute.KeyValue{Key: "direction", Value: attribute.StringValue("receive")})
42-
metrics.ProxyIO.Add(context.Background(), int64(buffer.Len()), metric.WithAttributes(attrs...))
36+
c.tracker.TrackIO(rx, buffer.Len(), c.attrs)
4337
}
4438
return dest, nil
4539
}
4640

4741
// WritePacket overrides network.PacketConn's WritePacket method to track sent bytes.
4842
func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
4943
if buffer.Len() > 0 {
50-
attrs := append(c.attributes, attribute.KeyValue{Key: "direction", Value: attribute.StringValue("transmit")})
51-
metrics.ProxyIO.Add(context.Background(), int64(buffer.Len()), metric.WithAttributes(attrs...))
44+
c.tracker.TrackIO(tx, buffer.Len(), c.attrs)
5245
}
5346
return c.PacketConn.WritePacket(buffer, destination)
5447
}
5548

5649
// Close overrides net.PacketConn's Close method to track connection duration.
5750
func (c *PacketConn) Close() error {
5851
duration := time.Since(c.startTime).Milliseconds()
59-
metrics.duration.Record(context.Background(), duration, metric.WithAttributes(c.attributes...))
60-
metrics.conns.Add(context.Background(), -1, metric.WithAttributes(c.attributes...))
52+
c.tracker.Leave(duration, c.attrs)
6153
return c.PacketConn.Close()
6254
}
6355

0 commit comments

Comments
 (0)