Merge pull request #107 from 9seconds/statsd

Correct metric types for statsd
This commit is contained in:
Sergey Arkhipov
2019-11-12 12:21:33 +03:00
committed by GitHub
8 changed files with 162 additions and 116 deletions
-1
View File
@@ -181,7 +181,6 @@ supported environment variables:
| `MTG_STATS_NAMESPACE` | `--stats-namespace` | `mtg` | Which namespace should be used for prometheus metrics. |
| `MTG_STATSD_ADDR` | `--statsd-addr` | | IP:host addresses of statsd service. No defaults, by defaults we do not send anything there. |
| `MTG_STATSD_PORT` | `--statsd-port` | `8125` | Which port should we use to work with statsd. |
| `MTG_STATSD_NETWORK` | `--statsd-network` | `udp` | Which protocol should we use to work with statsd. Possible options are `udp` and `tcp`. |
| `MTG_STATSD_PREFIX` | `--statsd-prefix` | `mtg` | Which bucket prefix we should use. For example, if you set `mtg`, then metric `traffic.ingress` would be send as `mtg.traffic.ingress`. |
| `MTG_STATSD_TAGS_FORMAT` | `--statsd-tags-format` | | Which tags format we should use. By default, we are using default vanilla statsd tags format but if you want to send directly to InfluxDB or Datadog, please specify it there. Possible options are `influxdb` and `datadog`. |
| `MTG_STATSD_TAGS` | `--statsd-tags` | | Which tags should we send to statsd with our metrics. Please specify them as `key=value` pairs. |
+12 -22
View File
@@ -9,8 +9,8 @@ import (
"net"
"github.com/alecthomas/units"
statsd "github.com/smira/go-statsd"
"go.uber.org/zap"
statsd "gopkg.in/alexcesaro/statsd.v2"
)
type SecretMode uint8
@@ -47,7 +47,6 @@ const (
OptionTypeStatsBind
OptionTypeStatsNamespace
OptionTypeStatsdAddress
OptionTypeStatsdNetwork
OptionTypeStatsdTagsFormat
OptionTypeStatsdTags
@@ -65,14 +64,14 @@ const (
)
type Config struct {
Bind *net.TCPAddr `json:"bind"`
PublicIPv4 *net.TCPAddr `json:"public_ipv4"`
PublicIPv6 *net.TCPAddr `json:"public_ipv6"`
StatsBind *net.TCPAddr `json:"stats_bind"`
StatsdAddr *net.TCPAddr `json:"stats_addr"`
Bind *net.TCPAddr `json:"bind"`
PublicIPv4 *net.TCPAddr `json:"public_ipv4"`
PublicIPv6 *net.TCPAddr `json:"public_ipv6"`
StatsBind *net.TCPAddr `json:"stats_bind"`
StatsdAddr *net.TCPAddr `json:"stats_addr"`
StatsdTagsFormat *statsd.TagFormat `json:"statsd_tags_format"`
StatsNamespace string `json:"stats_namespace"`
StatsdNetwork string `json:"statsd_network"`
CloakHost string `json:"cloak_host"`
StatsdTags map[string]string `json:"statsd_tags"`
@@ -84,10 +83,9 @@ type Config struct {
MultiplexPerConnection int `json:"multiplex_per_connection"`
Debug bool `json:"debug"`
Verbose bool `json:"verbose"`
StatsdTagsFormat statsd.TagFormat `json:"statsd_tags_format"`
SecretMode SecretMode `json:"secret_mode"`
Debug bool `json:"debug"`
Verbose bool `json:"verbose"`
SecretMode SecretMode `json:"secret_mode"`
Secret []byte `json:"secret"`
AdTag []byte `json:"adtag"`
@@ -125,21 +123,13 @@ func Init(options ...Opt) error { // nolint: gocyclo, funlen
C.StatsNamespace = opt.Value.(string)
case OptionTypeStatsdAddress:
C.StatsdAddr = opt.Value.(*net.TCPAddr)
case OptionTypeStatsdNetwork:
value := opt.Value.(string)
switch value {
case "udp", "tcp":
C.StatsdNetwork = value
default:
return fmt.Errorf("unknown statsd network %v", value)
}
case OptionTypeStatsdTagsFormat:
value := opt.Value.(string)
switch value {
case "datadog":
C.StatsdTagsFormat = statsd.Datadog
C.StatsdTagsFormat = statsd.TagFormatDatadog
case "influxdb":
C.StatsdTagsFormat = statsd.InfluxDB
C.StatsdTagsFormat = statsd.TagFormatInfluxDB
default:
return fmt.Errorf("incorrect statsd tag %s", value)
}
+4 -3
View File
@@ -7,12 +7,13 @@ require (
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d
github.com/beevik/ntp v0.2.0
github.com/prometheus/client_golang v1.2.1
github.com/prometheus/procfs v0.0.6 // indirect
github.com/smira/go-statsd v1.3.1
go.uber.org/multierr v1.4.0 // indirect
go.uber.org/zap v1.12.0
golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a
golang.org/x/crypto v0.0.0-20191111213947-16651526fdb4
golang.org/x/net v0.0.0-20191109021931-daa7c04131f5 // indirect
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4
golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 // indirect
golang.org/x/tools v0.0.0-20191112005509-a3f652f18032 // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/alexcesaro/statsd.v2 v2.0.0
)
+11 -6
View File
@@ -49,6 +49,8 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@@ -95,9 +97,13 @@ github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNG
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.0.6 h1:0qbH+Yqu/cj1ViVLvEWCP6qMQ4efWUj6bQqOEA0V0U4=
github.com/prometheus/procfs v0.0.6/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smira/go-statsd v1.3.1 h1:JalGiHNdK7GqVAPpg7j0Kwp2jZrz/fCg/B4ZuNuBY2w=
github.com/smira/go-statsd v1.3.1/go.mod h1:1srXJ9/pbnN04G8f4F1jUzsGOnwkPKXciyqpewGlkC4=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -121,8 +127,8 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a h1:R/qVym5WAxsZWQqZCwDY/8sdVKV1m1WgU4/S5IRQAzc=
golang.org/x/crypto v0.0.0-20191108234033-bd318be0434a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191111213947-16651526fdb4 h1:AGVXd+IAyeAb3FuQvYDYQ9+WR2JHm0+C0oYJaU1C4rs=
golang.org/x/crypto v0.0.0-20191111213947-16651526fdb4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -137,6 +143,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -152,13 +159,11 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 h1:LCmXVkvpQCDj724eX6irUTPCJP5GelFHxqGSWL2D1R0=
golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112005509-a3f652f18032 h1:Hp/Ke3YMUvqiVTvaoElioq98ROVmHsouhctlE8sVGpo=
golang.org/x/tools v0.0.0-20191112005509-a3f652f18032/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/statsd.v2 v2.0.0 h1:FXkZSCZIH17vLCO5sO2UucTHsH9pc+17F6pl3JVCwMc=
gopkg.in/alexcesaro/statsd.v2 v2.0.0/go.mod h1:i0ubccKGzBVNBpdGV5MocxyA/XlLUJzA7SLonnE4drU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-6
View File
@@ -72,11 +72,6 @@ var (
"Host:port of statsd server").
Envar("MTG_STATSD_ADDR").
TCP()
runStatsdNetwork = runCommand.Flag("statsd-network",
"Which network is used to work with statsd. Only 'tcp' and 'udp' are supported.").
Envar("MTG_STATSD_NETWORK").
Default("udp").
Enum("udp", "tcp")
runStatsdTagsFormat = runCommand.Flag("statsd-tags-format",
"Which tag format should we use to send stats metrics. Valid options are 'datadog' and 'influxdb'.").
Envar("MTG_STATSD_TAGS_FORMAT").
@@ -139,7 +134,6 @@ func main() {
config.Opt{Option: config.OptionTypeStatsBind, Value: *runStatsBind},
config.Opt{Option: config.OptionTypeStatsNamespace, Value: *runStatsNamespace},
config.Opt{Option: config.OptionTypeStatsdAddress, Value: *runStatsdAddress},
config.Opt{Option: config.OptionTypeStatsdNetwork, Value: *runStatsdNetwork},
config.Opt{Option: config.OptionTypeStatsdTagsFormat, Value: *runStatsdTagsFormat},
config.Opt{Option: config.OptionTypeStatsdTags, Value: *runStatsdTags},
config.Opt{Option: config.OptionTypeWriteBufferSize, Value: *runWriteBufferSize},
+2 -13
View File
@@ -14,20 +14,9 @@ var Stats Interface
func Init(ctx context.Context) error {
mux := http.NewServeMux()
instancePrometheus, err := newStatsPrometheus(mux)
if err != nil {
return fmt.Errorf("cannot initialize prometheus: %w", err)
}
stats := []Interface{instancePrometheus}
stats := []Interface{newStatsPrometheus(mux)}
if config.C.StatsdAddr != nil {
instanceStatsd, err := newStatsStatsd()
if err != nil {
return fmt.Errorf("cannot inialize statsd: %w", err)
}
stats = append(stats, instanceStatsd)
stats = append(stats, newStatsStatsd())
}
listener, err := net.Listen("tcp", config.C.StatsBind.String())
+9 -24
View File
@@ -1,7 +1,6 @@
package stats
import (
"fmt"
"net"
"net/http"
"strconv"
@@ -17,7 +16,7 @@ type statsPrometheus struct {
connections *prometheus.GaugeVec
telegramConnections *prometheus.GaugeVec
traffic *prometheus.GaugeVec
crashes prometheus.Gauge
crashes prometheus.Counter
replayAttacks prometheus.Counter
}
@@ -88,7 +87,7 @@ func (s *statsPrometheus) ReplayDetected() {
s.replayAttacks.Inc()
}
func newStatsPrometheus(mux *http.ServeMux) (Interface, error) {
func newStatsPrometheus(mux *http.ServeMux) Interface {
registry := prometheus.NewPedanticRegistry()
instance := &statsPrometheus{
@@ -107,7 +106,7 @@ func newStatsPrometheus(mux *http.ServeMux) (Interface, error) {
Name: "traffic",
Help: "Traffic passed through the proxy in bytes.",
}, []string{"direction"}),
crashes: prometheus.NewGauge(prometheus.GaugeOpts{
crashes: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: config.C.StatsNamespace,
Name: "crashes",
Help: "How many crashes happened.",
@@ -119,28 +118,14 @@ func newStatsPrometheus(mux *http.ServeMux) (Interface, error) {
}),
}
if err := registry.Register(instance.connections); err != nil {
return nil, fmt.Errorf("cannot register metrics for connections: %w", err)
}
if err := registry.Register(instance.telegramConnections); err != nil {
return nil, fmt.Errorf("cannot register metrics for telegram connections: %w", err)
}
if err := registry.Register(instance.traffic); err != nil {
return nil, fmt.Errorf("cannot register metrics for traffic: %w", err)
}
if err := registry.Register(instance.crashes); err != nil {
return nil, fmt.Errorf("cannot register metrics for crashes: %w", err)
}
if err := registry.Register(instance.replayAttacks); err != nil {
return nil, fmt.Errorf("cannot register metrics for replays: %w", err)
}
registry.MustRegister(instance.connections)
registry.MustRegister(instance.telegramConnections)
registry.MustRegister(instance.traffic)
registry.MustRegister(instance.crashes)
registry.MustRegister(instance.replayAttacks)
handler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
mux.Handle("/", handler)
return instance, nil
return instance
}
+124 -41
View File
@@ -5,23 +5,74 @@ import (
"net"
"strconv"
"strings"
"sync"
"time"
"gopkg.in/alexcesaro/statsd.v2"
statsd "github.com/smira/go-statsd"
"go.uber.org/zap"
"mtg/config"
"mtg/conntypes"
)
var (
tagTrafficIngress = &statsStatsdTag{
name: "ingress",
tag: statsd.StringTag("type", "ingress"),
}
tagTrafficEgress = &statsStatsdTag{
name: "egress",
tag: statsd.StringTag("type", "egress"),
}
tagConnectionTypeAbridged = &statsStatsdTag{
name: "abridged",
tag: statsd.StringTag("type", "abridged"),
}
tagConnectionTypeIntermediate = &statsStatsdTag{
name: "intermediate",
tag: statsd.StringTag("type", "intermediate"),
}
tagConnectionTypeSecured = &statsStatsdTag{
name: "secured",
tag: statsd.StringTag("type", "secured"),
}
tagConnectionProtocol4 = &statsStatsdTag{
name: "ipv4",
tag: statsd.StringTag("protocol", "ipv4"),
}
tagConnectionProtocol6 = &statsStatsdTag{
name: "ipv6",
tag: statsd.StringTag("protocol", "ipv6"),
}
)
type statsStatsdTag struct {
tag statsd.Tag
name string
}
type statsStatsdLogger struct {
log *zap.SugaredLogger
}
func (s *statsStatsdLogger) Printf(msg string, args ...interface{}) {
s.log.Debugw(fmt.Sprintf(msg, args...))
}
type statsStatsd struct {
client *statsd.Client
seen map[string]struct{}
seenMutex sync.RWMutex
client *statsd.Client
}
func (s *statsStatsd) IngressTraffic(traffic int) {
s.client.Count("traffic.ingress", traffic)
s.gauge("traffic", int64(traffic), tagTrafficIngress)
}
func (s *statsStatsd) EgressTraffic(traffic int) {
s.client.Count("traffic.egress", traffic)
s.gauge("traffic", int64(traffic), tagTrafficEgress)
}
func (s *statsStatsd) ClientConnected(connectionType conntypes.ConnectionType, addr *net.TCPAddr) {
@@ -32,25 +83,25 @@ func (s *statsStatsd) ClientDisconnected(connectionType conntypes.ConnectionType
s.changeConnections(connectionType, addr, -1)
}
func (s *statsStatsd) changeConnections(connectionType conntypes.ConnectionType, addr *net.TCPAddr, value int) {
labels := [...]string{
"connections",
"intermediate",
"ipv4",
}
func (s *statsStatsd) changeConnections(connectionType conntypes.ConnectionType, addr *net.TCPAddr, increment int64) {
tags := make([]*statsStatsdTag, 0, 2)
switch connectionType {
case conntypes.ConnectionTypeAbridged:
labels[1] = "abridged"
case conntypes.ConnectionTypeSecure:
labels[1] = "secured"
tags = append(tags, tagConnectionTypeAbridged)
case conntypes.ConnectionTypeIntermediate:
tags = append(tags, tagConnectionTypeIntermediate)
default:
tags = append(tags, tagConnectionTypeSecured)
}
if addr.IP.To4() == nil {
labels[2] = "ipv6"
tags = append(tags, tagConnectionProtocol6)
} else {
tags = append(tags, tagConnectionProtocol4)
}
s.client.Count(strings.Join(labels[:], "."), value)
s.gauge("connections", increment, tags...)
}
func (s *statsStatsd) TelegramConnected(dc conntypes.DC, addr *net.TCPAddr) {
@@ -61,51 +112,83 @@ func (s *statsStatsd) TelegramDisconnected(dc conntypes.DC, addr *net.TCPAddr) {
s.changeTelegramConnections(dc, addr, -1)
}
func (s *statsStatsd) changeTelegramConnections(dc conntypes.DC, addr *net.TCPAddr, value int) {
labels := [...]string{
"telegram_connections",
strconv.Itoa(int(dc)),
"ipv4",
func (s *statsStatsd) changeTelegramConnections(dc conntypes.DC, addr *net.TCPAddr, increment int64) {
tags := []*statsStatsdTag{
{
name: "dc" + strconv.Itoa(int(dc)),
tag: statsd.IntTag("dc", int(dc)),
},
}
if addr.IP.To4() == nil {
labels[2] = "ipv6"
tags = append(tags, tagConnectionProtocol6)
} else {
tags = append(tags, tagConnectionProtocol4)
}
s.client.Count(strings.Join(labels[:], "."), value)
s.gauge("telegram_connections", increment, tags...)
}
func (s *statsStatsd) Crash() {
s.client.Increment("crashes")
s.gauge("crashes", 1)
}
func (s *statsStatsd) ReplayDetected() {
s.client.Increment("replay_attacks")
s.gauge("replay_attacks", 1)
}
func newStatsStatsd() (Interface, error) {
options := []statsd.Option{
statsd.Prefix(config.C.StatsNamespace),
statsd.Network(config.C.StatsdNetwork),
statsd.Address(config.C.StatsdAddr.String()),
statsd.TagsFormat(config.C.StatsdTagsFormat),
func (s *statsStatsd) gauge(metric string, value int64, tags ...*statsStatsdTag) {
key, tagList := s.prepareVals(metric, tags)
s.initGauge(metric, key, tagList)
s.client.GaugeDelta(metric, value, tagList...)
}
func (s *statsStatsd) prepareVals(metric string, tags []*statsStatsdTag) (string, []statsd.Tag) {
tagList := make([]statsd.Tag, len(tags))
builder := strings.Builder{}
builder.WriteString(metric)
for i, v := range tags {
builder.WriteRune('.')
builder.WriteString(v.name)
tagList[i] = v.tag
}
if len(config.C.StatsdTags) > 0 {
tags := make([]string, len(config.C.StatsdTags)*2)
for k, v := range config.C.StatsdTags {
tags = append(tags, k, v)
}
return builder.String(), tagList
}
options = append(options, statsd.Tags(tags...))
func (s *statsStatsd) initGauge(metric, key string, tags []statsd.Tag) {
s.seenMutex.RLock()
_, ok := s.seen[key]
s.seenMutex.RUnlock()
if ok {
return
}
client, err := statsd.New(options...)
if err != nil {
return nil, fmt.Errorf("cannot initialize a client: %w", err)
s.seenMutex.Lock()
defer s.seenMutex.Unlock()
if _, ok = s.seen[key]; !ok {
s.seen[key] = struct{}{}
s.client.Gauge(metric, 0, tags...)
}
}
func newStatsStatsd() Interface {
prefix := strings.TrimSuffix(config.C.StatsNamespace, ".") + "."
logger := &statsStatsdLogger{
log: zap.S().Named("stats").Named("statsd"),
}
return &statsStatsd{
client: client,
}, nil
seen: make(map[string]struct{}),
client: statsd.NewClient(config.C.StatsdAddr.String(),
statsd.SendLoopCount(2),
statsd.ReconnectInterval(10*time.Second),
statsd.Logger(logger),
statsd.MetricPrefix(prefix),
statsd.TagStyle(config.C.StatsdTagsFormat),
),
}
}