Revert code cleanup for now (#428)
* Revert "Code cleanup in and around connector (#427)" This reverts commitb3e88db48c. * Revert "Refactored server setup and run out of main (#425)" This reverts commit05c57c3b85. * Revert "Code cleanup of routes config loader and API server (#424)" This reverts commit1ee3eb4de3.
This commit is contained in:
+232
-34
@@ -3,14 +3,82 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime/pprof"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/itzg/go-flagsfiller"
|
||||
"github.com/itzg/mc-router/server"
|
||||
"github.com/sirupsen/logrus"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type MetricsBackendConfig struct {
|
||||
Influxdb struct {
|
||||
Interval time.Duration `default:"1m"`
|
||||
Tags map[string]string `usage:"any extra tags to be included with all reported metrics"`
|
||||
Addr string
|
||||
Username string
|
||||
Password string
|
||||
Database string
|
||||
RetentionPolicy string
|
||||
}
|
||||
}
|
||||
|
||||
type WebhookConfig struct {
|
||||
Url string `usage:"If set, a POST request that contains connection status notifications will be sent to this HTTP address"`
|
||||
RequireUser bool `default:"false" usage:"Indicates if the webhook will only be called if a user is connecting rather than just server list/ping"`
|
||||
}
|
||||
|
||||
type AutoScale struct {
|
||||
Up bool `usage:"Increase Kubernetes StatefulSet Replicas (only) from 0 to 1 on respective backend servers when accessed"`
|
||||
Down bool `default:"false" usage:"Decrease Kubernetes StatefulSet Replicas (only) from 1 to 0 on respective backend servers after there are no connections"`
|
||||
DownAfter string `default:"10m" usage:"Server scale down delay after there are no connections"`
|
||||
AllowDeny string `usage:"Path to config for server allowlists and denylists. If a global/server entry is specified, only players allowed to connect to the server will be able to trigger a scale up when -auto-scale-up is enabled or cancel active down scalers when -auto-scale-down is enabled"`
|
||||
}
|
||||
|
||||
type RoutesConfig struct {
|
||||
Config string `usage:"Name or full [path] to routes config file"`
|
||||
ConfigWatch bool `usage:"Watch for config file changes"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Port int `default:"25565" usage:"The [port] bound to listen for Minecraft client connections"`
|
||||
Default string `usage:"host:port of a default Minecraft server to use when mapping not found"`
|
||||
Mapping map[string]string `usage:"Comma or newline delimited or repeated mappings of externalHostname=host:port"`
|
||||
ApiBinding string `usage:"The [host:port] bound for servicing API requests"`
|
||||
Version bool `usage:"Output version and exit"`
|
||||
CpuProfile string `usage:"Enables CPU profiling and writes to given path"`
|
||||
Debug bool `usage:"Enable debug logs"`
|
||||
ConnectionRateLimit int `default:"1" usage:"Max number of connections to allow per second"`
|
||||
InKubeCluster bool `usage:"Use in-cluster Kubernetes config"`
|
||||
KubeConfig string `usage:"The path to a Kubernetes configuration file"`
|
||||
InDocker bool `usage:"Use Docker service discovery"`
|
||||
InDockerSwarm bool `usage:"Use Docker Swarm service discovery"`
|
||||
DockerSocket string `default:"unix:///var/run/docker.sock" usage:"Path to Docker socket to use"`
|
||||
DockerTimeout int `default:"0" usage:"Timeout configuration in seconds for the Docker integrations"`
|
||||
DockerRefreshInterval int `default:"15" usage:"Refresh interval in seconds for the Docker integrations"`
|
||||
MetricsBackend string `default:"discard" usage:"Backend to use for metrics exposure/publishing: discard,expvar,influxdb,prometheus"`
|
||||
UseProxyProtocol bool `default:"false" usage:"Send PROXY protocol to backend servers"`
|
||||
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"`
|
||||
TrustedProxies []string `usage:"Comma delimited list of CIDR notation IP blocks to trust when receiving PROXY protocol"`
|
||||
RecordLogins bool `default:"false" usage:"Log and generate metrics on player logins. Metrics only supported with influxdb or prometheus backend"`
|
||||
MetricsBackendConfig MetricsBackendConfig
|
||||
Routes RoutesConfig
|
||||
NgrokToken string `usage:"If set, an ngrok tunnel will be established. It is HIGHLY recommended to pass as an environment variable."`
|
||||
AutoScale AutoScale
|
||||
|
||||
ClientsToAllow []string `usage:"Zero or more client IP addresses or CIDRs to allow. Takes precedence over deny."`
|
||||
ClientsToDeny []string `usage:"Zero or more client IP addresses or CIDRs to deny. Ignored if any configured to allow"`
|
||||
|
||||
SimplifySRV bool `default:"false" usage:"Simplify fully qualified SRV records for mapping"`
|
||||
|
||||
Webhook WebhookConfig `usage:"Webhook configuration"`
|
||||
}
|
||||
|
||||
var (
|
||||
version = "dev"
|
||||
commit = "none"
|
||||
@@ -21,60 +89,190 @@ func showVersion() {
|
||||
fmt.Printf("%v, commit %v, built at %v", version, commit, date)
|
||||
}
|
||||
|
||||
type CliConfig struct {
|
||||
Version bool `usage:"Output version and exit"`
|
||||
Debug bool `usage:"Enable debug logs"`
|
||||
|
||||
ServerConfig server.Config `flatten:"true"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
var cliConfig CliConfig
|
||||
err := flagsfiller.Parse(&cliConfig, flagsfiller.WithEnv(""))
|
||||
var config Config
|
||||
err := flagsfiller.Parse(&config, flagsfiller.WithEnv(""))
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
if cliConfig.Version {
|
||||
if config.Version {
|
||||
showVersion()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if cliConfig.Debug {
|
||||
if config.Debug {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.Debug("Debug logs enabled")
|
||||
}
|
||||
|
||||
if config.CpuProfile != "" {
|
||||
cpuProfileFile, err := os.Create(config.CpuProfile)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("trying to create cpu profile file")
|
||||
}
|
||||
//goland:noinspection GoUnhandledErrorResult
|
||||
defer cpuProfileFile.Close()
|
||||
|
||||
logrus.WithField("file", config.CpuProfile).Info("Starting cpu profiling")
|
||||
err = pprof.StartCPUProfile(cpuProfileFile)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("trying to start cpu profile")
|
||||
}
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
var autoScaleAllowDenyConfig *server.AllowDenyConfig = nil
|
||||
if config.AutoScale.AllowDeny != "" {
|
||||
autoScaleAllowDenyConfig, err = server.ParseAllowDenyConfig(config.AutoScale.AllowDeny)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("trying to parse autoscale up allow-deny-list file")
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
|
||||
metricsBuilder := NewMetricsBuilder(config.MetricsBackend, &config.MetricsBackendConfig)
|
||||
|
||||
s, err := server.NewServer(ctx, &cliConfig.ServerConfig)
|
||||
downScalerEnabled := config.AutoScale.Down && (config.InKubeCluster || config.KubeConfig != "")
|
||||
downScalerDelay, err := time.ParseDuration(config.AutoScale.DownAfter)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Could not setup server")
|
||||
logrus.WithError(err).Fatal("Unable to parse auto scale down after duration")
|
||||
}
|
||||
// Only one instance should be created
|
||||
server.DownScaler = server.NewDownScaler(ctx, downScalerEnabled, downScalerDelay)
|
||||
|
||||
go s.Run()
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-s.Done():
|
||||
return
|
||||
if config.Routes.Config != "" {
|
||||
err := server.RoutesConfig.ReadRoutesConfig(config.Routes.Config)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Unable to load routes from config file")
|
||||
}
|
||||
|
||||
case sig := <-signals:
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
s.ReloadConfig()
|
||||
|
||||
case syscall.SIGINT, syscall.SIGTERM:
|
||||
cancel()
|
||||
// but wait for the server to be done
|
||||
|
||||
default:
|
||||
logrus.WithField("signal", sig).Warn("Received unexpected signal")
|
||||
if config.Routes.ConfigWatch {
|
||||
err := server.RoutesConfig.WatchForChanges(ctx)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Unable to watch for changes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server.Routes.RegisterAll(config.Mapping)
|
||||
if config.Default != "" {
|
||||
server.Routes.SetDefaultRoute(config.Default)
|
||||
}
|
||||
|
||||
if config.ConnectionRateLimit < 1 {
|
||||
config.ConnectionRateLimit = 1
|
||||
}
|
||||
|
||||
trustedIpNets := make([]*net.IPNet, 0)
|
||||
for _, ip := range config.TrustedProxies {
|
||||
_, ipNet, err := net.ParseCIDR(ip)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Unable to parse trusted proxy CIDR block")
|
||||
}
|
||||
trustedIpNets = append(trustedIpNets, ipNet)
|
||||
}
|
||||
|
||||
connector := server.NewConnector(metricsBuilder.BuildConnectorMetrics(), config.UseProxyProtocol, config.ReceiveProxyProtocol, trustedIpNets, config.RecordLogins, autoScaleAllowDenyConfig)
|
||||
|
||||
clientFilter, err := server.NewClientFilter(config.ClientsToAllow, config.ClientsToDeny)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Unable to create client filter")
|
||||
}
|
||||
connector.SetClientFilter(clientFilter)
|
||||
|
||||
if config.Webhook.Url != "" {
|
||||
logrus.
|
||||
WithField("url", config.Webhook.Url).
|
||||
WithField("require-user", config.Webhook.RequireUser).
|
||||
Info("Using webhook for connection status notifications")
|
||||
connector.SetConnectionNotifier(
|
||||
server.NewWebhookNotifier(config.Webhook.Url, config.Webhook.RequireUser))
|
||||
}
|
||||
|
||||
if config.NgrokToken != "" {
|
||||
connector.UseNgrok(config.NgrokToken)
|
||||
}
|
||||
err = connector.StartAcceptingConnections(ctx,
|
||||
net.JoinHostPort("", strconv.Itoa(config.Port)),
|
||||
config.ConnectionRateLimit,
|
||||
)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
if config.ApiBinding != "" {
|
||||
server.StartApiServer(config.ApiBinding)
|
||||
}
|
||||
|
||||
if config.InKubeCluster {
|
||||
err = server.K8sWatcher.StartInCluster(config.AutoScale.Up, config.AutoScale.Down)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Unable to start k8s integration")
|
||||
} else {
|
||||
defer server.K8sWatcher.Stop()
|
||||
}
|
||||
} else if config.KubeConfig != "" {
|
||||
err := server.K8sWatcher.StartWithConfig(config.KubeConfig, config.AutoScale.Up, config.AutoScale.Down)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Unable to start k8s integration")
|
||||
} else {
|
||||
defer server.K8sWatcher.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
if config.InDocker {
|
||||
err = server.DockerWatcher.Start(config.DockerSocket, config.DockerTimeout, config.DockerRefreshInterval, config.AutoScale.Up, config.AutoScale.Down)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Unable to start docker integration")
|
||||
} else {
|
||||
defer server.DockerWatcher.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
if config.InDockerSwarm {
|
||||
err = server.DockerSwarmWatcher.Start(config.DockerSocket, config.DockerTimeout, config.DockerRefreshInterval, config.AutoScale.Up, config.AutoScale.Down)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Unable to start docker swarm integration")
|
||||
} else {
|
||||
defer server.DockerSwarmWatcher.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
server.Routes.SimplifySRV(config.SimplifySRV)
|
||||
|
||||
err = metricsBuilder.Start(ctx)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Unable to start metrics reporter")
|
||||
}
|
||||
|
||||
// handle signals
|
||||
for {
|
||||
sig := <-c
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
if config.Routes.Config != "" {
|
||||
logrus.Info("Received SIGHUP, reloading routes config...")
|
||||
if err := server.RoutesConfig.ReloadRoutesConfig(); err != nil {
|
||||
logrus.
|
||||
WithError(err).
|
||||
WithField("routesConfig", config.Routes.Config).
|
||||
Error("Could not re-read the routes config file")
|
||||
}
|
||||
}
|
||||
case syscall.SIGINT, syscall.SIGTERM:
|
||||
logrus.WithField("signal", sig).Info("Stopping. Waiting for connections to complete...")
|
||||
signal.Stop(c)
|
||||
connector.WaitForConnections()
|
||||
logrus.Info("Stopped")
|
||||
return
|
||||
default:
|
||||
logrus.WithField("signal", sig).Warn("Received unexpected signal")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
kitlogrus "github.com/go-kit/kit/log/logrus"
|
||||
discardMetrics "github.com/go-kit/kit/metrics/discard"
|
||||
expvarMetrics "github.com/go-kit/kit/metrics/expvar"
|
||||
kitinflux "github.com/go-kit/kit/metrics/influx"
|
||||
prometheusMetrics "github.com/go-kit/kit/metrics/prometheus"
|
||||
influx "github.com/influxdata/influxdb1-client/v2"
|
||||
"github.com/itzg/mc-router/server"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type MetricsBuilder interface {
|
||||
BuildConnectorMetrics() *server.ConnectorMetrics
|
||||
Start(ctx context.Context) error
|
||||
}
|
||||
|
||||
const (
|
||||
MetricsBackendExpvar = "expvar"
|
||||
MetricsBackendPrometheus = "prometheus"
|
||||
MetricsBackendInfluxDB = "influxdb"
|
||||
MetricsBackendDiscard = "discard"
|
||||
)
|
||||
|
||||
// NewMetricsBuilder creates a new MetricsBuilder based on the specified backend.
|
||||
// If the backend is not recognized, a discard builder is returned.
|
||||
// config can be nil if the backend is not influxdb.
|
||||
func NewMetricsBuilder(backend string, config *MetricsBackendConfig) MetricsBuilder {
|
||||
switch strings.ToLower(backend) {
|
||||
case MetricsBackendExpvar:
|
||||
return &expvarMetricsBuilder{}
|
||||
case MetricsBackendPrometheus:
|
||||
return &prometheusMetricsBuilder{}
|
||||
case MetricsBackendInfluxDB:
|
||||
return &influxMetricsBuilder{config: config}
|
||||
case MetricsBackendDiscard:
|
||||
return &discardMetricsBuilder{}
|
||||
default:
|
||||
return &discardMetricsBuilder{}
|
||||
}
|
||||
}
|
||||
|
||||
type expvarMetricsBuilder struct {
|
||||
}
|
||||
|
||||
func (b expvarMetricsBuilder) Start(ctx context.Context) error {
|
||||
// nothing needed
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b expvarMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
|
||||
c := expvarMetrics.NewCounter("connections")
|
||||
return &server.ConnectorMetrics{
|
||||
Errors: expvarMetrics.NewCounter("errors").With("subsystem", "connector"),
|
||||
BytesTransmitted: expvarMetrics.NewCounter("bytes"),
|
||||
ConnectionsFrontend: c,
|
||||
ConnectionsBackend: c,
|
||||
ActiveConnections: expvarMetrics.NewGauge("active_connections"),
|
||||
ServerActivePlayer: expvarMetrics.NewGauge("server_active_player"),
|
||||
ServerLogins: expvarMetrics.NewCounter("server_logins"),
|
||||
ServerActiveConnections: expvarMetrics.NewGauge("server_active_connections"),
|
||||
}
|
||||
}
|
||||
|
||||
type discardMetricsBuilder struct {
|
||||
}
|
||||
|
||||
func (b discardMetricsBuilder) Start(ctx context.Context) error {
|
||||
// nothing needed
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b discardMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
|
||||
return &server.ConnectorMetrics{
|
||||
Errors: discardMetrics.NewCounter(),
|
||||
BytesTransmitted: discardMetrics.NewCounter(),
|
||||
ConnectionsFrontend: discardMetrics.NewCounter(),
|
||||
ConnectionsBackend: discardMetrics.NewCounter(),
|
||||
ActiveConnections: discardMetrics.NewGauge(),
|
||||
ServerActivePlayer: discardMetrics.NewGauge(),
|
||||
ServerLogins: discardMetrics.NewCounter(),
|
||||
ServerActiveConnections: discardMetrics.NewGauge(),
|
||||
}
|
||||
}
|
||||
|
||||
type influxMetricsBuilder struct {
|
||||
config *MetricsBackendConfig
|
||||
metrics *kitinflux.Influx
|
||||
}
|
||||
|
||||
func (b *influxMetricsBuilder) Start(ctx context.Context) error {
|
||||
influxConfig := &b.config.Influxdb
|
||||
if influxConfig.Addr == "" {
|
||||
return errors.New("influx addr is required")
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(influxConfig.Interval)
|
||||
client, err := influx.NewHTTPClient(influx.HTTPConfig{
|
||||
Addr: influxConfig.Addr,
|
||||
Username: influxConfig.Username,
|
||||
Password: influxConfig.Password,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create influx http client: %w", err)
|
||||
}
|
||||
|
||||
go b.metrics.WriteLoop(ctx, ticker.C, client)
|
||||
|
||||
logrus.WithField("addr", influxConfig.Addr).
|
||||
Debug("reporting metrics to influxdb")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *influxMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
|
||||
influxConfig := &b.config.Influxdb
|
||||
|
||||
metrics := kitinflux.New(influxConfig.Tags, influx.BatchPointsConfig{
|
||||
Database: influxConfig.Database,
|
||||
RetentionPolicy: influxConfig.RetentionPolicy,
|
||||
}, kitlogrus.NewLogger(logrus.StandardLogger()))
|
||||
|
||||
b.metrics = metrics
|
||||
|
||||
c := metrics.NewCounter("mc_router_connections")
|
||||
return &server.ConnectorMetrics{
|
||||
Errors: metrics.NewCounter("mc_router_errors"),
|
||||
BytesTransmitted: metrics.NewCounter("mc_router_transmitted_bytes"),
|
||||
ConnectionsFrontend: c.With("side", "frontend"),
|
||||
ConnectionsBackend: c.With("side", "backend"),
|
||||
ActiveConnections: metrics.NewGauge("mc_router_connections_active"),
|
||||
ServerActivePlayer: metrics.NewGauge("mc_router_server_player_active"),
|
||||
ServerLogins: metrics.NewCounter("mc_router_server_logins"),
|
||||
ServerActiveConnections: metrics.NewGauge("mc_router_server_active_connections"),
|
||||
}
|
||||
}
|
||||
|
||||
type prometheusMetricsBuilder struct {
|
||||
}
|
||||
|
||||
var pcv *prometheusMetrics.Counter
|
||||
|
||||
func (b prometheusMetricsBuilder) Start(ctx context.Context) error {
|
||||
|
||||
// nothing needed
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b prometheusMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
|
||||
pcv = prometheusMetrics.NewCounter(promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "mc_router",
|
||||
Name: "errors",
|
||||
Help: "The total number of errors",
|
||||
}, []string{"type"}))
|
||||
return &server.ConnectorMetrics{
|
||||
Errors: pcv,
|
||||
BytesTransmitted: prometheusMetrics.NewCounter(promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "mc_router",
|
||||
Name: "bytes",
|
||||
Help: "The total number of bytes transmitted",
|
||||
}, nil)),
|
||||
ConnectionsFrontend: prometheusMetrics.NewCounter(promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "mc_router",
|
||||
Subsystem: "frontend",
|
||||
Name: "connections",
|
||||
Help: "The total number of connections",
|
||||
ConstLabels: prometheus.Labels{"side": "frontend"},
|
||||
}, nil)),
|
||||
ConnectionsBackend: prometheusMetrics.NewCounter(promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "mc_router",
|
||||
Subsystem: "backend",
|
||||
Name: "connections",
|
||||
Help: "The total number of backend connections",
|
||||
ConstLabels: prometheus.Labels{"side": "backend"},
|
||||
}, []string{"host"})),
|
||||
ActiveConnections: prometheusMetrics.NewGauge(promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: "mc_router",
|
||||
Name: "active_connections",
|
||||
Help: "The number of active connections",
|
||||
}, nil)),
|
||||
ServerActivePlayer: prometheusMetrics.NewGauge(promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: "mc_router",
|
||||
Name: "server_active_player",
|
||||
Help: "Player is active on server",
|
||||
}, []string{"player_name", "player_uuid", "server_address"})),
|
||||
ServerLogins: prometheusMetrics.NewCounter(promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "mc_router",
|
||||
Name: "server_logins",
|
||||
Help: "The total number of player logins",
|
||||
}, []string{"player_name", "player_uuid", "server_address"})),
|
||||
ServerActiveConnections: prometheusMetrics.NewGauge(promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: "mc_router",
|
||||
Name: "server_active_connections",
|
||||
Help: "The number of active connections per server",
|
||||
}, []string{"server_address"})),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user