Skip to content

Commit d21ccb5

Browse files
authored
Add option to emit metrics and logs when players connect to the router (#391)
1 parent cc59052 commit d21ccb5

File tree

6 files changed

+59
-3
lines changed

6 files changed

+59
-3
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ Routes Minecraft client connections to backend servers based upon the requested
7878
Indicates if the webhook will only be called if a user is connecting rather than just server list/ping (env WEBHOOK_REQUIRE_USER)
7979
-webhook-url string
8080
If set, a POST request that contains connection status notifications will be sent to this HTTP address (env WEBHOOK_URL)
81+
-record-logins
82+
Log and generate metrics on player logins. Metrics only supported with influxdb or prometheus backend
8183
```
8284

8385
## Docker Multi-Architecture Image

cmd/mc-router/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type Config struct {
5454
UseProxyProtocol bool `default:"false" usage:"Send PROXY protocol to backend servers"`
5555
ReceiveProxyProtocol bool `default:"false" usage:"Receive PROXY protocol from backend servers, by default trusts every proxy header that it receives, combine with -trusted-proxies to specify a list of trusted proxies"`
5656
TrustedProxies []string `usage:"Comma delimited list of CIDR notation IP blocks to trust when receiving PROXY protocol"`
57+
RecordLogins bool `default:"false" usage:"Log and generate metrics on player logins. Metrics only supported with influxdb or prometheus backend"`
5758
MetricsBackendConfig MetricsBackendConfig
5859
RoutesConfig string `usage:"Name or full path to routes config file"`
5960
NgrokToken string `usage:"If set, an ngrok tunnel will be established. It is HIGHLY recommended to pass as an environment variable."`
@@ -142,7 +143,7 @@ func main() {
142143
trustedIpNets = append(trustedIpNets, ipNet)
143144
}
144145

145-
connector := server.NewConnector(metricsBuilder.BuildConnectorMetrics(), config.UseProxyProtocol, config.ReceiveProxyProtocol, trustedIpNets)
146+
connector := server.NewConnector(metricsBuilder.BuildConnectorMetrics(), config.UseProxyProtocol, config.ReceiveProxyProtocol, trustedIpNets, config.RecordLogins)
146147

147148
clientFilter, err := server.NewClientFilter(config.ClientsToAllow, config.ClientsToDeny)
148149
if err != nil {

cmd/mc-router/metrics.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ func (b expvarMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
6565
ConnectionsFrontend: c,
6666
ConnectionsBackend: c,
6767
ActiveConnections: expvarMetrics.NewGauge("active_connections"),
68+
ServerActivePlayer: expvarMetrics.NewGauge("server_active_player"),
69+
ServerLogins: expvarMetrics.NewCounter("server_logins"),
6870
}
6971
}
7072

@@ -83,6 +85,8 @@ func (b discardMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics
8385
ConnectionsFrontend: discardMetrics.NewCounter(),
8486
ConnectionsBackend: discardMetrics.NewCounter(),
8587
ActiveConnections: discardMetrics.NewGauge(),
88+
ServerActivePlayer: discardMetrics.NewGauge(),
89+
ServerLogins: discardMetrics.NewCounter(),
8690
}
8791
}
8892

@@ -132,6 +136,8 @@ func (b *influxMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics
132136
ConnectionsFrontend: c.With("side", "frontend"),
133137
ConnectionsBackend: c.With("side", "backend"),
134138
ActiveConnections: metrics.NewGauge("mc_router_connections_active"),
139+
ServerActivePlayer: metrics.NewGauge("mc_router_server_player_active"),
140+
ServerLogins: metrics.NewCounter("mc_router_server_logins"),
135141
}
136142
}
137143

@@ -178,5 +184,15 @@ func (b prometheusMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetri
178184
Name: "active_connections",
179185
Help: "The number of active connections",
180186
}, nil)),
187+
ServerActivePlayer: prometheusMetrics.NewGauge(promauto.NewGaugeVec(prometheus.GaugeOpts{
188+
Namespace: "mc_router",
189+
Name: "server_active_player",
190+
Help: "Player is active on server",
191+
}, []string{"player_name", "player_uuid", "server_address"})),
192+
ServerLogins: prometheusMetrics.NewCounter(promauto.NewCounterVec(prometheus.CounterOpts{
193+
Namespace: "mc_router",
194+
Name: "server_logins",
195+
Help: "The total number of player logins",
196+
}, []string{"player_name", "player_uuid", "server_address"})),
181197
}
182198
}

mcproto/read.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ func ReadPacket(reader *bufio.Reader, addr net.Addr, state State) (*Packet, erro
4545
return nil, err
4646
}
4747

48-
packet := &Packet{Length: frame.Length}
48+
// Packet length is frame length (bytes for packetID and data) plus bytes used to store the frame length data
49+
packet := &Packet{Length: frame.Length + PacketLengthFieldBytes}
4950

5051
remainder := bytes.NewBuffer(frame.Payload)
5152

mcproto/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,7 @@ type LegacyServerListPing struct {
8080
type ByteReader interface {
8181
ReadByte() (byte, error)
8282
}
83+
84+
const (
85+
PacketLengthFieldBytes = 1
86+
)

server/connector.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ type ConnectorMetrics struct {
3434
ConnectionsFrontend metrics.Counter
3535
ConnectionsBackend metrics.Counter
3636
ActiveConnections metrics.Gauge
37+
ServerActivePlayer metrics.Gauge
38+
ServerLogins metrics.Counter
3739
}
3840

3941
type ClientInfo struct {
@@ -57,13 +59,14 @@ type PlayerInfo struct {
5759
Uuid uuid.UUID `json:"uuid"`
5860
}
5961

60-
func NewConnector(metrics *ConnectorMetrics, sendProxyProto bool, receiveProxyProto bool, trustedProxyNets []*net.IPNet) *Connector {
62+
func NewConnector(metrics *ConnectorMetrics, sendProxyProto bool, receiveProxyProto bool, trustedProxyNets []*net.IPNet, recordLogins bool) *Connector {
6163
return &Connector{
6264
metrics: metrics,
6365
sendProxyProto: sendProxyProto,
6466
connectionsCond: sync.NewCond(&sync.Mutex{}),
6567
receiveProxyProto: receiveProxyProto,
6668
trustedProxyNets: trustedProxyNets,
69+
recordLogins: recordLogins,
6770
}
6871
}
6972

@@ -72,6 +75,7 @@ type Connector struct {
7275
metrics *ConnectorMetrics
7376
sendProxyProto bool
7477
receiveProxyProto bool
78+
recordLogins bool
7579
trustedProxyNets []*net.IPNet
7680

7781
activeConnections int32
@@ -383,9 +387,37 @@ func (c *Connector) findAndConnectBackend(ctx context.Context, frontendConn net.
383387

384388
c.metrics.ActiveConnections.Set(float64(
385389
atomic.AddInt32(&c.activeConnections, 1)))
390+
if c.recordLogins && userInfo != nil {
391+
logrus.
392+
WithField("client", clientAddr).
393+
WithField("playerName", userInfo.Name).
394+
WithField("playerUUID", userInfo.Uuid).
395+
WithField("serverAddress", serverAddress).
396+
Info("Player attempted to login to server")
397+
398+
c.metrics.ServerActivePlayer.
399+
With("player_name", userInfo.Name).
400+
With("player_uuid", userInfo.Uuid.String()).
401+
With("server_address", serverAddress).
402+
Set(1)
403+
404+
c.metrics.ServerLogins.
405+
With("player_name", userInfo.Name).
406+
With("player_uuid", userInfo.Uuid.String()).
407+
With("server_address", serverAddress).
408+
Add(1)
409+
}
410+
386411
defer func() {
387412
c.metrics.ActiveConnections.Set(float64(
388413
atomic.AddInt32(&c.activeConnections, -1)))
414+
if c.recordLogins && userInfo != nil {
415+
c.metrics.ServerActivePlayer.
416+
With("player_name", userInfo.Name).
417+
With("player_uuid", userInfo.Uuid.String()).
418+
With("server_address", serverAddress).
419+
Set(0)
420+
}
389421
c.connectionsCond.Signal()
390422
}()
391423

0 commit comments

Comments
 (0)