@@ -26,16 +26,15 @@ import (
26
26
"github.com/prometheus/client_golang/prometheus"
27
27
)
28
28
29
- const (
30
- // How often to report the active IP key TunnelTime.
31
- activeIPKeyTrackerReportingInterval = 5 * time .Second
32
- )
29
+ // How often to report the active IP key TunnelTime.
30
+ const tunnelTimeTrackerReportingInterval = 5 * time .Second
33
31
34
- var since = time .Since
32
+ // Now is stubbable for testing.
33
+ var Now = time .Now
35
34
36
35
type outlineMetrics struct {
37
36
ipinfo.IPInfoMap
38
- activeIPKeyTracker
37
+ tunnelTimeTracker
39
38
40
39
buildInfo * prometheus.GaugeVec
41
40
accessKeys prometheus.Gauge
@@ -45,8 +44,8 @@ type outlineMetrics struct {
45
44
timeToCipherMs * prometheus.HistogramVec
46
45
// TODO: Add time to first byte.
47
46
48
- IPKeyTimePerKey * prometheus.CounterVec
49
- IPKeyTimePerLocation * prometheus.CounterVec
47
+ TunnelTimePerKey * prometheus.CounterVec
48
+ TunnelTimePerLocation * prometheus.CounterVec
50
49
51
50
tcpProbes * prometheus.HistogramVec
52
51
tcpOpenConnections * prometheus.CounterVec
@@ -61,93 +60,89 @@ type outlineMetrics struct {
61
60
var _ service.TCPMetrics = (* outlineMetrics )(nil )
62
61
var _ service.UDPMetrics = (* outlineMetrics )(nil )
63
62
63
+ type ReportTunnelTimeFunc func (IPKey , ipinfo.IPInfo , time.Duration )
64
+
64
65
type activeClient struct {
65
66
IPKey IPKey
67
+ clientInfo ipinfo.IPInfo
66
68
connectionCount int
67
69
startTime time.Time
68
70
}
69
71
70
- func (c * activeClient ) IsActive () bool {
71
- return c .connectionCount > 0
72
- }
73
-
74
72
type IPKey struct {
75
73
ip string
76
74
accessKey string
77
75
}
78
76
79
- type activeIPKeyTracker struct {
80
- activeClients map [IPKey ]activeClient
81
- metricsCallback func ( IPKey , time. Duration )
77
+ type tunnelTimeTracker struct {
78
+ activeClients map [IPKey ]activeClient
79
+ reportTunnelTime ReportTunnelTimeFunc
82
80
}
83
81
84
82
// Reports time connected for all active clients, called at a regular interval.
85
- func (t * activeIPKeyTracker ) reportAll () {
83
+ func (t * tunnelTimeTracker ) reportAll (now time. Time ) {
86
84
if len (t .activeClients ) == 0 {
87
85
logger .Debugf ("No active clients. No IPKey activity to report." )
88
86
return
89
87
}
90
88
for _ , c := range t .activeClients {
91
- t .reportDuration (c )
89
+ t .reportDuration (c , now )
92
90
}
93
91
}
94
92
95
93
// Reports time connected for a given active client.
96
- func (t * activeIPKeyTracker ) reportDuration (c activeClient ) {
97
- connDuration := since (c .startTime )
94
+ func (t * tunnelTimeTracker ) reportDuration (c activeClient , now time. Time ) {
95
+ connDuration := now . Sub (c .startTime )
98
96
logger .Debugf ("Reporting activity for key `%v`, duration: %v" , c .IPKey .accessKey , connDuration )
99
- t .metricsCallback (c .IPKey , connDuration )
97
+ t .reportTunnelTime (c .IPKey , c . clientInfo , connDuration )
100
98
101
99
// Reset the start time now that it's been reported.
102
- c .startTime = time . Now ()
100
+ c .startTime = Now ()
103
101
t .activeClients [c .IPKey ] = c
104
102
}
105
103
106
104
// Registers a new active connection for a client [net.Addr] and access key.
107
- func (t * activeIPKeyTracker ) startConnection (addr net.Addr , accessKey string ) {
108
- hostname , _ , _ := net .SplitHostPort (addr .String ())
105
+ func (t * tunnelTimeTracker ) startConnection (clientInfo ipinfo. IPInfo , clientAddr net.Addr , accessKey string ) {
106
+ hostname , _ , _ := net .SplitHostPort (clientAddr .String ())
109
107
ipKey := IPKey {ip : hostname , accessKey : accessKey }
110
108
111
109
c , exists := t .activeClients [ipKey ]
112
110
if ! exists {
113
- c = activeClient {ipKey , 0 , time . Now ()}
111
+ c = activeClient {ipKey , clientInfo , 0 , Now ()}
114
112
}
115
113
c .connectionCount ++
116
114
t .activeClients [ipKey ] = c
117
115
}
118
116
119
117
// Removes an active connection for a client [net.Addr] and access key.
120
- func (t * activeIPKeyTracker ) stopConnection (addr net.Addr , accessKey string ) {
121
- hostname , _ , _ := net .SplitHostPort (addr .String ())
118
+ func (t * tunnelTimeTracker ) stopConnection (clientAddr net.Addr , accessKey string ) {
119
+ hostname , _ , _ := net .SplitHostPort (clientAddr .String ())
122
120
ipKey := IPKey {ip : hostname , accessKey : accessKey }
123
121
124
122
c := t .activeClients [ipKey ]
123
+ c , exists := t .activeClients [ipKey ]
124
+ if ! exists {
125
+ logger .Warningf ("Failed to find active client" )
126
+ return
127
+ }
125
128
c .connectionCount --
126
- if ! c . IsActive () {
127
- t .reportDuration (c )
129
+ if c . connectionCount <= 0 {
130
+ t .reportDuration (c , Now () )
128
131
delete (t .activeClients , ipKey )
129
132
return
130
133
}
131
134
t .activeClients [ipKey ] = c
132
135
}
133
136
134
- func newActiveIPKeyTracker (callback func (IPKey , time.Duration )) * activeIPKeyTracker {
135
- t := & activeIPKeyTracker {activeClients : make (map [IPKey ]activeClient ), metricsCallback : callback }
136
- ticker := time .NewTicker (activeIPKeyTrackerReportingInterval )
137
- done := make (chan struct {})
137
+ func newTunnelTimeTracker (report ReportTunnelTimeFunc ) * tunnelTimeTracker {
138
+ tracker := & tunnelTimeTracker {activeClients : make (map [IPKey ]activeClient ), reportTunnelTime : report }
139
+ ticker := time .NewTicker (tunnelTimeTrackerReportingInterval )
138
140
go func () {
139
- for {
140
- select {
141
- case <- ticker .C :
142
- t .reportAll ()
143
- case <- done :
144
- logger .Debugf ("done channel %p closed" , done )
145
- ticker .Stop ()
146
- return
147
- }
141
+ for t := range ticker .C {
142
+ tracker .reportAll (t )
148
143
}
149
144
}()
150
- return t
145
+ return tracker
151
146
}
152
147
153
148
// newPrometheusOutlineMetrics constructs a metrics object that uses
@@ -205,14 +200,14 @@ func newPrometheusOutlineMetrics(ip2info ipinfo.IPInfoMap, registerer prometheus
205
200
float64 (7 * 24 * time .Hour .Milliseconds ()), // Week
206
201
},
207
202
}, []string {"status" }),
208
- IPKeyTimePerKey : prometheus .NewCounterVec (prometheus.CounterOpts {
203
+ TunnelTimePerKey : prometheus .NewCounterVec (prometheus.CounterOpts {
209
204
Namespace : "shadowsocks" ,
210
- Name : "ip_key_connectivity_seconds " ,
205
+ Name : "tunnel_time_seconds " ,
211
206
Help : "Time at least 1 connection was open for a (IP, access key) pair, per key" ,
212
207
}, []string {"access_key" }),
213
- IPKeyTimePerLocation : prometheus .NewCounterVec (prometheus.CounterOpts {
208
+ TunnelTimePerLocation : prometheus .NewCounterVec (prometheus.CounterOpts {
214
209
Namespace : "shadowsocks" ,
215
- Name : "ip_key_connectivity_seconds_per_location " ,
210
+ Name : "tunnel_time_seconds_per_location " ,
216
211
Help : "Time at least 1 connection was open for a (IP, access key) pair, per location" ,
217
212
}, []string {"location" , "asn" }),
218
213
dataBytes : prometheus .NewCounterVec (
@@ -256,12 +251,12 @@ func newPrometheusOutlineMetrics(ip2info ipinfo.IPInfoMap, registerer prometheus
256
251
Help : "Entries removed from the UDP NAT table" ,
257
252
}),
258
253
}
259
- m .activeIPKeyTracker = * newActiveIPKeyTracker (m .reportIPKeyActivity )
254
+ m .tunnelTimeTracker = * newTunnelTimeTracker (m .addTunnelTime )
260
255
261
256
// TODO: Is it possible to pass where to register the collectors?
262
257
registerer .MustRegister (m .buildInfo , m .accessKeys , m .ports , m .tcpProbes , m .tcpOpenConnections , m .tcpClosedConnections , m .tcpConnectionDurationMs ,
263
258
m .dataBytes , m .dataBytesPerLocation , m .timeToCipherMs , m .udpPacketsFromClientPerLocation , m .udpAddedNatEntries , m .udpRemovedNatEntries ,
264
- m .IPKeyTimePerKey , m .IPKeyTimePerLocation )
259
+ m .TunnelTimePerKey , m .TunnelTimePerLocation )
265
260
return m
266
261
}
267
262
@@ -274,28 +269,18 @@ func (m *outlineMetrics) SetNumAccessKeys(numKeys int, ports int) {
274
269
m .ports .Set (float64 (ports ))
275
270
}
276
271
277
- func (m * outlineMetrics ) AddOpenTCPConnection (addr net.Addr ) {
278
- clientInfo , err := ipinfo .GetIPInfoFromAddr (m .IPInfoMap , addr )
279
- if err != nil {
280
- logger .Warningf ("Failed client info lookup: %v" , err )
281
- }
282
- logger .Debugf ("Got info \" %#v\" for IP %v" , clientInfo , addr .String ())
272
+ func (m * outlineMetrics ) AddOpenTCPConnection (clientInfo ipinfo.IPInfo ) {
283
273
m .tcpOpenConnections .WithLabelValues (clientInfo .CountryCode .String (), asnLabel (clientInfo .ASN )).Inc ()
284
274
}
285
275
286
- // Reports total time connected, by access key and by country.
287
- func (m * outlineMetrics ) reportIPKeyActivity (ipKey IPKey , duration time.Duration ) {
288
- m .IPKeyTimePerKey .WithLabelValues (ipKey .accessKey ).Add (duration .Seconds ())
289
- ip := net .ParseIP (ipKey .ip )
290
- clientInfo , err := ipinfo .GetIPInfoFromIP (m .IPInfoMap , ip )
291
- if err != nil {
292
- logger .Warningf ("Failed client info lookup: %v" , err )
293
- }
294
- m .IPKeyTimePerLocation .WithLabelValues (clientInfo .CountryCode .String (), asnLabel (clientInfo .ASN )).Add (duration .Seconds ())
276
+ // Reports total time connected (i.e. TunnelTime), by access key and by country.
277
+ func (m * outlineMetrics ) addTunnelTime (ipKey IPKey , clientInfo ipinfo.IPInfo , duration time.Duration ) {
278
+ m .TunnelTimePerKey .WithLabelValues (ipKey .accessKey ).Add (duration .Seconds ())
279
+ m .TunnelTimePerLocation .WithLabelValues (clientInfo .CountryCode .String (), asnLabel (clientInfo .ASN )).Add (duration .Seconds ())
295
280
}
296
281
297
- func (m * outlineMetrics ) AddAuthenticatedTCPConnection (addr net.Addr , accessKey string ) {
298
- m .activeIPKeyTracker .startConnection (addr , accessKey )
282
+ func (m * outlineMetrics ) AddAuthenticatedTCPConnection (clientInfo ipinfo. IPInfo , clientAddr net.Addr , accessKey string ) {
283
+ m .tunnelTimeTracker .startConnection (clientInfo , clientAddr , accessKey )
299
284
}
300
285
301
286
// addIfNonZero helps avoid the creation of series that are always zero.
@@ -312,12 +297,7 @@ func asnLabel(asn int) string {
312
297
return fmt .Sprint (asn )
313
298
}
314
299
315
- func (m * outlineMetrics ) AddClosedTCPConnection (addr net.Addr , accessKey , status string , data metrics.ProxyMetrics , duration time.Duration ) {
316
- clientInfo , err := ipinfo .GetIPInfoFromAddr (m .IPInfoMap , addr )
317
- if err != nil {
318
- logger .Warningf ("Failed client info lookup: %v" , err )
319
- }
320
- logger .Debugf ("Got info \" %#v\" for IP %v" , clientInfo , addr .String ())
300
+ func (m * outlineMetrics ) AddClosedTCPConnection (clientInfo ipinfo.IPInfo , clientAddr net.Addr , accessKey , status string , data metrics.ProxyMetrics , duration time.Duration ) {
321
301
m .tcpClosedConnections .WithLabelValues (clientInfo .CountryCode .String (), asnLabel (clientInfo .ASN ), status , accessKey ).Inc ()
322
302
m .tcpConnectionDurationMs .WithLabelValues (status ).Observe (duration .Seconds () * 1000 )
323
303
addIfNonZero (data .ClientProxy , m .dataBytes , "c>p" , "tcp" , accessKey )
@@ -329,7 +309,7 @@ func (m *outlineMetrics) AddClosedTCPConnection(addr net.Addr, accessKey, status
329
309
addIfNonZero (data .ProxyClient , m .dataBytes , "c<p" , "tcp" , accessKey )
330
310
addIfNonZero (data .ProxyClient , m .dataBytesPerLocation , "c<p" , "tcp" , clientInfo .CountryCode .String (), asnLabel (clientInfo .ASN ))
331
311
332
- m .activeIPKeyTracker .stopConnection (addr , accessKey )
312
+ m .tunnelTimeTracker .stopConnection (clientAddr , accessKey )
333
313
}
334
314
335
315
func (m * outlineMetrics ) AddUDPPacketFromClient (clientInfo ipinfo.IPInfo , accessKey , status string , clientProxyBytes , proxyTargetBytes int ) {
@@ -347,16 +327,16 @@ func (m *outlineMetrics) AddUDPPacketFromTarget(clientInfo ipinfo.IPInfo, access
347
327
addIfNonZero (int64 (proxyClientBytes ), m .dataBytesPerLocation , "c<p" , "udp" , clientInfo .CountryCode .String (), asnLabel (clientInfo .ASN ))
348
328
}
349
329
350
- func (m * outlineMetrics ) AddUDPNatEntry (addr net.Addr , accessKey string ) {
330
+ func (m * outlineMetrics ) AddUDPNatEntry (clientInfo ipinfo. IPInfo , clientAddr net.Addr , accessKey string ) {
351
331
m .udpAddedNatEntries .Inc ()
352
332
353
- m .activeIPKeyTracker .startConnection (addr , accessKey )
333
+ m .tunnelTimeTracker .startConnection (clientInfo , clientAddr , accessKey )
354
334
}
355
335
356
- func (m * outlineMetrics ) RemoveUDPNatEntry (addr net.Addr , accessKey string ) {
336
+ func (m * outlineMetrics ) RemoveUDPNatEntry (clientInfo ipinfo. IPInfo , clientAddr net.Addr , accessKey string ) {
357
337
m .udpRemovedNatEntries .Inc ()
358
338
359
- m .activeIPKeyTracker .stopConnection (addr , accessKey )
339
+ m .tunnelTimeTracker .stopConnection (clientAddr , accessKey )
360
340
}
361
341
362
342
func (m * outlineMetrics ) AddTCPProbe (status , drainResult string , port int , clientProxyBytes int64 ) {
0 commit comments