Rework stats

This commit is contained in:
9seconds
2021-03-27 22:04:30 +03:00
parent 5eca6ecb05
commit bc2bd4510a
19 changed files with 273 additions and 232 deletions
+2 -2
View File
@@ -73,8 +73,8 @@ func eventStreamProcessor(ctx context.Context, eventChan <-chan mtglib.Event, ob
observer.EventStart(typedEvt) observer.EventStart(typedEvt)
case mtglib.EventConnectedToDC: case mtglib.EventConnectedToDC:
observer.EventConnectedToDC(typedEvt) observer.EventConnectedToDC(typedEvt)
case mtglib.EventTraffic: case mtglib.EventTelegramTraffic:
observer.EventTraffic(typedEvt) observer.EventTelegramTraffic(typedEvt)
case mtglib.EventFinish: case mtglib.EventFinish:
observer.EventFinish(typedEvt) observer.EventFinish(typedEvt)
case mtglib.EventIPBlocklisted: case mtglib.EventIPBlocklisted:
+4 -4
View File
@@ -90,8 +90,8 @@ func (suite *EventStreamTestSuite) TestEventConnectedToDC() {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
} }
func (suite *EventStreamTestSuite) TestEventTraffic() { func (suite *EventStreamTestSuite) TestEventTelegramTraffic() {
evt := mtglib.EventTraffic{ evt := mtglib.EventTelegramTraffic{
CreatedAt: time.Now(), CreatedAt: time.Now(),
ConnID: "connID", ConnID: "connID",
Traffic: 1024, Traffic: 1024,
@@ -100,10 +100,10 @@ func (suite *EventStreamTestSuite) TestEventTraffic() {
for _, v := range []*ObserverMock{suite.observerMock1, suite.observerMock2} { for _, v := range []*ObserverMock{suite.observerMock1, suite.observerMock2} {
v. v.
On("EventTraffic", mock.Anything). On("EventTelegramTraffic", mock.Anything).
Once(). Once().
Run(func(args mock.Arguments) { Run(func(args mock.Arguments) {
caught := args.Get(0).(mtglib.EventTraffic) caught := args.Get(0).(mtglib.EventTelegramTraffic)
suite.Equal(evt.CreatedAt, caught.CreatedAt) suite.Equal(evt.CreatedAt, caught.CreatedAt)
suite.Equal(evt.ConnID, caught.ConnID) suite.Equal(evt.ConnID, caught.ConnID)
+1 -1
View File
@@ -6,7 +6,7 @@ type Observer interface {
EventStart(mtglib.EventStart) EventStart(mtglib.EventStart)
EventFinish(mtglib.EventFinish) EventFinish(mtglib.EventFinish)
EventConnectedToDC(mtglib.EventConnectedToDC) EventConnectedToDC(mtglib.EventConnectedToDC)
EventTraffic(mtglib.EventTraffic) EventTelegramTraffic(mtglib.EventTelegramTraffic)
EventConcurrencyLimited(mtglib.EventConcurrencyLimited) EventConcurrencyLimited(mtglib.EventConcurrencyLimited)
EventIPBlocklisted(mtglib.EventIPBlocklisted) EventIPBlocklisted(mtglib.EventIPBlocklisted)
+1 -1
View File
@@ -17,7 +17,7 @@ func (o *ObserverMock) EventConnectedToDC(evt mtglib.EventConnectedToDC) {
o.Called(evt) o.Called(evt)
} }
func (o *ObserverMock) EventTraffic(evt mtglib.EventTraffic) { func (o *ObserverMock) EventTelegramTraffic(evt mtglib.EventTelegramTraffic) {
o.Called(evt) o.Called(evt)
} }
+2 -2
View File
@@ -40,7 +40,7 @@ func (m multiObserver) EventConnectedToDC(evt mtglib.EventConnectedToDC) {
wg.Wait() wg.Wait()
} }
func (m multiObserver) EventTraffic(evt mtglib.EventTraffic) { func (m multiObserver) EventTelegramTraffic(evt mtglib.EventTelegramTraffic) {
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
wg.Add(len(m.observers)) wg.Add(len(m.observers))
@@ -48,7 +48,7 @@ func (m multiObserver) EventTraffic(evt mtglib.EventTraffic) {
go func(obs Observer) { go func(obs Observer) {
defer wg.Done() defer wg.Done()
obs.EventTraffic(evt) obs.EventTelegramTraffic(evt)
}(v) }(v)
} }
+1 -1
View File
@@ -19,7 +19,7 @@ type noopObserver struct{}
func (n noopObserver) EventStart(_ mtglib.EventStart) {} func (n noopObserver) EventStart(_ mtglib.EventStart) {}
func (n noopObserver) EventConnectedToDC(_ mtglib.EventConnectedToDC) {} func (n noopObserver) EventConnectedToDC(_ mtglib.EventConnectedToDC) {}
func (n noopObserver) EventTraffic(_ mtglib.EventTraffic) {} func (n noopObserver) EventTelegramTraffic(_ mtglib.EventTelegramTraffic) {}
func (n noopObserver) EventFinish(_ mtglib.EventFinish) {} func (n noopObserver) EventFinish(_ mtglib.EventFinish) {}
func (n noopObserver) EventConcurrencyLimited(_ mtglib.EventConcurrencyLimited) {} func (n noopObserver) EventConcurrencyLimited(_ mtglib.EventConcurrencyLimited) {}
func (n noopObserver) EventIPBlocklisted(_ mtglib.EventIPBlocklisted) {} func (n noopObserver) EventIPBlocklisted(_ mtglib.EventIPBlocklisted) {}
+1 -1
View File
@@ -31,7 +31,7 @@ func (suite *NoopTestSuite) SetupSuite() {
RemoteIP: net.ParseIP("127.1.0.1"), RemoteIP: net.ParseIP("127.1.0.1"),
DC: 2, DC: 2,
}, },
"traffic": mtglib.EventTraffic{ "telegram-traffic": mtglib.EventTelegramTraffic{
CreatedAt: time.Now(), CreatedAt: time.Now(),
ConnID: "connID", ConnID: "connID",
Traffic: 1000, Traffic: 1000,
+2 -2
View File
@@ -21,7 +21,7 @@ func (c connTelegramTraffic) Read(b []byte) (int, error) {
n, err := c.Conn.Read(b) n, err := c.Conn.Read(b)
if n > 0 { if n > 0 {
c.stream.Send(c.ctx, EventTraffic{ c.stream.Send(c.ctx, EventTelegramTraffic{
CreatedAt: time.Now(), CreatedAt: time.Now(),
ConnID: c.connID, ConnID: c.connID,
Traffic: uint(n), Traffic: uint(n),
@@ -36,7 +36,7 @@ func (c connTelegramTraffic) Write(b []byte) (int, error) {
n, err := c.Conn.Write(b) n, err := c.Conn.Write(b)
if n > 0 { if n > 0 {
c.stream.Send(c.ctx, EventTraffic{ c.stream.Send(c.ctx, EventTelegramTraffic{
CreatedAt: time.Now(), CreatedAt: time.Now(),
ConnID: c.connID, ConnID: c.connID,
Traffic: uint(n), Traffic: uint(n),
+26 -2
View File
@@ -15,6 +15,10 @@ func (e EventStart) StreamID() string {
return e.ConnID return e.ConnID
} }
func (e EventStart) Timestamp() time.Time {
return e.CreatedAt
}
type EventConnectedToDC struct { type EventConnectedToDC struct {
CreatedAt time.Time CreatedAt time.Time
ConnID string ConnID string
@@ -26,17 +30,25 @@ func (e EventConnectedToDC) StreamID() string {
return e.ConnID return e.ConnID
} }
type EventTraffic struct { func (e EventConnectedToDC) Timestamp() time.Time {
return e.CreatedAt
}
type EventTelegramTraffic struct {
CreatedAt time.Time CreatedAt time.Time
ConnID string ConnID string
Traffic uint Traffic uint
IsRead bool IsRead bool
} }
func (e EventTraffic) StreamID() string { func (e EventTelegramTraffic) StreamID() string {
return e.ConnID return e.ConnID
} }
func (e EventTelegramTraffic) Timestamp() time.Time {
return e.CreatedAt
}
type EventFinish struct { type EventFinish struct {
CreatedAt time.Time CreatedAt time.Time
ConnID string ConnID string
@@ -46,6 +58,10 @@ func (e EventFinish) StreamID() string {
return e.ConnID return e.ConnID
} }
func (e EventFinish) Timestamp() time.Time {
return e.CreatedAt
}
type EventConcurrencyLimited struct { type EventConcurrencyLimited struct {
CreatedAt time.Time CreatedAt time.Time
} }
@@ -54,6 +70,10 @@ func (e EventConcurrencyLimited) StreamID() string {
return "" return ""
} }
func (e EventConcurrencyLimited) Timestamp() time.Time {
return e.CreatedAt
}
type EventIPBlocklisted struct { type EventIPBlocklisted struct {
CreatedAt time.Time CreatedAt time.Time
RemoteIP net.IP RemoteIP net.IP
@@ -62,3 +82,7 @@ type EventIPBlocklisted struct {
func (e EventIPBlocklisted) StreamID() string { func (e EventIPBlocklisted) StreamID() string {
return "" return ""
} }
func (e EventIPBlocklisted) Timestamp() time.Time {
return e.CreatedAt
}
+19 -4
View File
@@ -21,6 +21,7 @@ func (suite *EventsTestSuite) TestEventStart() {
} }
suite.Equal("CONNID", evt.StreamID()) suite.Equal("CONNID", evt.StreamID())
suite.WithinDuration(time.Now(), evt.Timestamp(), 10*time.Millisecond)
} }
func (suite *EventsTestSuite) TestEventFinish() { func (suite *EventsTestSuite) TestEventFinish() {
@@ -30,6 +31,7 @@ func (suite *EventsTestSuite) TestEventFinish() {
} }
suite.Equal("CONNID", evt.StreamID()) suite.Equal("CONNID", evt.StreamID())
suite.WithinDuration(time.Now(), evt.Timestamp(), 10*time.Millisecond)
} }
func (suite *EventsTestSuite) TestEventConnectedToDC() { func (suite *EventsTestSuite) TestEventConnectedToDC() {
@@ -41,10 +43,11 @@ func (suite *EventsTestSuite) TestEventConnectedToDC() {
} }
suite.Equal("CONNID", evt.StreamID()) suite.Equal("CONNID", evt.StreamID())
suite.WithinDuration(time.Now(), evt.Timestamp(), 10*time.Millisecond)
} }
func (suite *EventsTestSuite) TestEventTraffic() { func (suite *EventsTestSuite) TestEventTelegramTraffic() {
evt := mtglib.EventTraffic{ evt := mtglib.EventTelegramTraffic{
CreatedAt: time.Now(), CreatedAt: time.Now(),
ConnID: "CONNID", ConnID: "CONNID",
Traffic: 3, Traffic: 3,
@@ -52,14 +55,26 @@ func (suite *EventsTestSuite) TestEventTraffic() {
} }
suite.Equal("CONNID", evt.StreamID()) suite.Equal("CONNID", evt.StreamID())
suite.WithinDuration(time.Now(), evt.Timestamp(), 10*time.Millisecond)
} }
func (suite *EventsTestSuite) TestEventConcurrencyLimited() { func (suite *EventsTestSuite) TestEventConcurrencyLimited() {
suite.Empty(mtglib.EventConcurrencyLimited{}.StreamID()) evt := mtglib.EventConcurrencyLimited{
CreatedAt: time.Now(),
}
suite.Empty(evt.StreamID())
suite.WithinDuration(time.Now(), evt.Timestamp(), 10*time.Millisecond)
} }
func (suite *EventsTestSuite) TestEventIPBlocklisted() { func (suite *EventsTestSuite) TestEventIPBlocklisted() {
suite.Empty(mtglib.EventIPBlocklisted{}.StreamID()) evt := mtglib.EventIPBlocklisted{
CreatedAt: time.Now(),
RemoteIP: net.ParseIP("10.0.0.10"),
}
suite.Empty(evt.StreamID())
suite.WithinDuration(time.Now(), evt.Timestamp(), 10*time.Millisecond)
} }
func TestEvents(t *testing.T) { func TestEvents(t *testing.T) {
+1
View File
@@ -45,6 +45,7 @@ type IPBlocklist interface {
type Event interface { type Event interface {
StreamID() string StreamID() string
Timestamp() time.Time
} }
type EventStream interface { type EventStream interface {
+6
View File
@@ -95,6 +95,9 @@ func (p *Proxy) Serve(listener net.Listener) error {
if addr := conn.RemoteAddr().(*net.TCPAddr).IP; p.ipBlocklist.Contains(addr) { if addr := conn.RemoteAddr().(*net.TCPAddr).IP; p.ipBlocklist.Contains(addr) {
conn.Close() conn.Close()
p.logger.
BindStr("ip", conn.RemoteAddr().(*net.TCPAddr).IP.String()).
Info("ip was blacklisted")
p.eventStream.Send(p.ctx, EventIPBlocklisted{ p.eventStream.Send(p.ctx, EventIPBlocklisted{
CreatedAt: time.Now(), CreatedAt: time.Now(),
RemoteIP: addr, RemoteIP: addr,
@@ -110,6 +113,9 @@ func (p *Proxy) Serve(listener net.Listener) error {
case errors.Is(err, ants.ErrPoolClosed): case errors.Is(err, ants.ErrPoolClosed):
return nil return nil
case errors.Is(err, ants.ErrPoolOverload): case errors.Is(err, ants.ErrPoolOverload):
p.logger.
BindStr("ip", conn.RemoteAddr().(*net.TCPAddr).IP.String()).
Info("connection was concurrency limited")
p.eventStream.Send(p.ctx, EventConcurrencyLimited{ p.eventStream.Send(p.ctx, EventConcurrencyLimited{
CreatedAt: time.Now(), CreatedAt: time.Now(),
}) })
+21 -15
View File
@@ -6,21 +6,27 @@ const (
DefaultStatsdMetricPrefix = DefaultMetricPrefix + "." DefaultStatsdMetricPrefix = DefaultMetricPrefix + "."
DefaultStatsdTagFormat = "datadog" DefaultStatsdTagFormat = "datadog"
MetricClientConnections = "client_connections" MetricClientConnections = "client_connections"
MetricTelegramConnections = "telegram_connections" MetricTelegramConnections = "telegram_connections"
MetricTraffic = "traffic" MetricDomainDisguisingConnections = "domain_disguising_connections"
MetricSessionDuration = "session_duration"
MetricSessionTraffic = "session_traffic"
MetricConcurrencyLimited = "concurrency_limited"
MetricIPBlocklisted = "ip_blocklisted"
TagIPType = "ip_type" MetricTelegramTraffic = "telegram_traffic"
TagTelegramIP = "ip" MetricDomainDisguisingTraffic = "domain_disguising_traffic"
TagDC = "dc"
TagDirection = "direction"
TagIPTypeIPv4 = "ipv4" MetricDomainDisguising = "domain_disguising"
TagIPTypeIPv6 = "ipv6" MetricConcurrencyLimited = "concurrency_limited"
TagDirectionTelegram = "telegram" MetricIPBlocklisted = "ip_blocklisted"
TagDirectionClient = "client" MetricReplayAttacks = "replay_attacks"
TagIPFamily = "ip_family"
TagIPFamilyIPv4 = "ipv4"
TagIPFamilyIPv6 = "ipv6"
TagTelegramIP = "telegram_ip"
TagDC = "dc"
TagDirection = "direction"
TagDirectionToClient = "to_client"
TagDirectionFromClient = "from_client"
) )
+20
View File
@@ -0,0 +1,20 @@
package stats
import "sync"
var streamInfoPool = sync.Pool{
New: func() interface{} {
return &streamInfo{
tags: map[string]string{},
}
},
}
func acquireStreamInfo() *streamInfo {
return streamInfoPool.Get().(*streamInfo)
}
func releaseStreamInfo(info *streamInfo) {
info.Reset()
streamInfoPool.Put(info)
}
+80 -117
View File
@@ -4,8 +4,6 @@ import (
"context" "context"
"net" "net"
"net/http" "net/http"
"strconv"
"time"
"github.com/9seconds/mtg/v2/events" "github.com/9seconds/mtg/v2/events"
"github.com/9seconds/mtg/v2/mtglib" "github.com/9seconds/mtg/v2/mtglib"
@@ -19,89 +17,61 @@ type prometheusProcessor struct {
} }
func (p prometheusProcessor) EventStart(evt mtglib.EventStart) { func (p prometheusProcessor) EventStart(evt mtglib.EventStart) {
sInfo := &streamInfo{ info := acquireStreamInfo()
createdAt: evt.CreatedAt, info.SetStartTime(evt.CreatedAt)
clientIP: evt.RemoteIP, info.SetClientIP(evt.RemoteIP)
} p.streams[evt.StreamID()] = info
p.streams[evt.StreamID()] = sInfo
p.factory.metricClientConnections.WithLabelValues(sInfo.GetClientIPType()).Inc() p.factory.metricClientConnections.
WithLabelValues(info.V(TagIPFamily)).
Inc()
} }
func (p prometheusProcessor) EventConnectedToDC(evt mtglib.EventConnectedToDC) { func (p prometheusProcessor) EventConnectedToDC(evt mtglib.EventConnectedToDC) {
sInfo, ok := p.streams[evt.StreamID()] info, ok := p.streams[evt.StreamID()]
if !ok { if !ok {
return return
} }
sInfo.remoteIP = evt.RemoteIP info.SetTelegramIP(evt.RemoteIP)
sInfo.dc = evt.DC info.SetDC(evt.DC)
p.factory.metricTelegramConnections.WithLabelValues( p.factory.metricTelegramConnections.
sInfo.GetRemoteIPType(), WithLabelValues(info.V(TagTelegramIP), info.V(TagDC)).
sInfo.remoteIP.String(), Inc()
strconv.Itoa(sInfo.dc)).Inc()
} }
func (p prometheusProcessor) EventTraffic(evt mtglib.EventTraffic) { func (p prometheusProcessor) EventTelegramTraffic(evt mtglib.EventTelegramTraffic) {
sInfo, ok := p.streams[evt.StreamID()] info, ok := p.streams[evt.StreamID()]
if !ok { if !ok {
return return
} }
labels := []string{ p.factory.metricTelegramTraffic.
sInfo.GetRemoteIPType(), WithLabelValues(info.V(TagTelegramIP), info.V(TagDC), getDirection(evt.IsRead)).
sInfo.remoteIP.String(), Add(float64(evt.Traffic))
strconv.Itoa(sInfo.dc),
}
if evt.IsRead {
sInfo.bytesRecvFromTelegram += evt.Traffic
labels = append(labels, TagDirectionClient)
} else {
sInfo.bytesSentToTelegram += evt.Traffic
labels = append(labels, TagDirectionTelegram)
}
p.factory.metricTraffic.WithLabelValues(labels...).Add(float64(evt.Traffic))
} }
func (p prometheusProcessor) EventFinish(evt mtglib.EventFinish) { func (p prometheusProcessor) EventFinish(evt mtglib.EventFinish) {
sInfo, ok := p.streams[evt.StreamID()] info, ok := p.streams[evt.StreamID()]
if !ok { if !ok {
return return
} }
defer delete(p.streams, evt.StreamID()) defer func() {
delete(p.streams, evt.StreamID())
releaseStreamInfo(info)
}()
duration := evt.CreatedAt.Sub(sInfo.createdAt) p.factory.metricClientConnections.
WithLabelValues(info.V(TagIPFamily)).
Dec()
p.factory.metricClientConnections.WithLabelValues(sInfo.GetClientIPType()).Dec() if info.V(TagTelegramIP) != "" {
p.factory.metricSessionDuration.Observe(float64(duration) / float64(time.Second)) p.factory.metricTelegramConnections.
WithLabelValues(info.V(TagTelegramIP), info.V(TagDC)).
if sInfo.remoteIP == nil { Dec()
return
} }
labels := []string{
sInfo.GetRemoteIPType(),
sInfo.remoteIP.String(),
strconv.Itoa(sInfo.dc),
}
p.factory.metricTelegramConnections.WithLabelValues(labels...).Dec()
labels = append(labels, TagDirectionClient)
p.factory.metricSessionTraffic.
WithLabelValues(labels...).
Observe(float64(sInfo.bytesRecvFromTelegram))
labels[3] = TagDirectionTelegram
p.factory.metricSessionTraffic.
WithLabelValues(labels...).
Observe(float64(sInfo.bytesSentToTelegram))
} }
func (p prometheusProcessor) EventConcurrencyLimited(_ mtglib.EventConcurrencyLimited) { func (p prometheusProcessor) EventConcurrencyLimited(_ mtglib.EventConcurrencyLimited) {
@@ -109,11 +79,7 @@ func (p prometheusProcessor) EventConcurrencyLimited(_ mtglib.EventConcurrencyLi
} }
func (p prometheusProcessor) EventIPBlocklisted(evt mtglib.EventIPBlocklisted) { func (p prometheusProcessor) EventIPBlocklisted(evt mtglib.EventIPBlocklisted) {
if evt.RemoteIP.To4() == nil { p.factory.metricIPBlocklisted.Inc()
p.factory.metricIPBlocklisted.WithLabelValues(TagIPTypeIPv6).Inc()
} else {
p.factory.metricIPBlocklisted.WithLabelValues(TagIPTypeIPv4).Inc()
}
} }
func (p prometheusProcessor) Shutdown() { func (p prometheusProcessor) Shutdown() {
@@ -123,13 +89,17 @@ func (p prometheusProcessor) Shutdown() {
type PrometheusFactory struct { type PrometheusFactory struct {
httpServer *http.Server httpServer *http.Server
metricClientConnections *prometheus.GaugeVec metricClientConnections *prometheus.GaugeVec
metricTelegramConnections *prometheus.GaugeVec metricTelegramConnections *prometheus.GaugeVec
metricTraffic *prometheus.CounterVec metricDomainDisguisingConnections *prometheus.GaugeVec
metricIPBlocklisted *prometheus.CounterVec
metricSessionTraffic *prometheus.HistogramVec metricTelegramTraffic *prometheus.CounterVec
metricConcurrencyLimited prometheus.Counter metricDomainDisguisingTraffic *prometheus.CounterVec
metricSessionDuration prometheus.Histogram
metricDomainDisguising prometheus.Counter
metricConcurrencyLimited prometheus.Counter
metricIPBlocklisted prometheus.Counter
metricReplayAttacks prometheus.Counter
} }
func (p *PrometheusFactory) Make() events.Observer { func (p *PrometheusFactory) Make() events.Observer {
@@ -164,70 +134,63 @@ func NewPrometheus(metricPrefix, httpPath string) *PrometheusFactory { // nolint
metricClientConnections: prometheus.NewGaugeVec(prometheus.GaugeOpts{ metricClientConnections: prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: metricPrefix, Namespace: metricPrefix,
Name: MetricClientConnections, Name: MetricClientConnections,
Help: "A number of connections under active processing.", Help: "A number of actively processing client connections.",
}, []string{TagIPType}), }, []string{TagIPFamily}),
metricTelegramConnections: prometheus.NewGaugeVec(prometheus.GaugeOpts{ metricTelegramConnections: prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: metricPrefix, Namespace: metricPrefix,
Name: MetricTelegramConnections, Name: MetricTelegramConnections,
Help: "A number of connections to Telegram servers.", Help: "A number of connections to Telegram servers.",
}, []string{TagIPType, TagTelegramIP, TagDC}), }, []string{TagTelegramIP, TagDC}),
metricSessionDuration: prometheus.NewHistogram(prometheus.HistogramOpts{ metricDomainDisguisingConnections: prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: metricPrefix, Namespace: metricPrefix,
Name: MetricSessionDuration, Name: MetricDomainDisguisingConnections,
Help: "Session duration.", Help: "A number of connections which talk with disguising domain.",
Buckets: []float64{ // per 30 seconds }, []string{TagIPFamily}),
30,
60, metricTelegramTraffic: prometheus.NewCounterVec(prometheus.CounterOpts{
90, Namespace: metricPrefix,
120, Name: MetricTelegramTraffic,
150, Help: "Traffic which is generated talking with Telegram servers.",
180, }, []string{TagTelegramIP, TagDC, TagDirection}),
210, metricDomainDisguisingTraffic: prometheus.NewCounterVec(prometheus.CounterOpts{
240, Namespace: metricPrefix,
270, Name: MetricDomainDisguisingTraffic,
300, Help: "Traffic which is generated talking with disguising domain.",
}, }, []string{TagDirection}),
metricDomainDisguising: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: metricPrefix,
Name: MetricDomainDisguising,
Help: "A number of routings to disguising domain.",
}), }),
metricSessionTraffic: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: metricPrefix,
Name: MetricSessionTraffic,
Help: "A traffic size which flew via proxy within a single session.",
Buckets: []float64{ // per 1mb
1 * 1024 * 1024,
2 * 1024 * 1024,
3 * 1024 * 1024,
4 * 1024 * 1024,
5 * 1024 * 1024,
6 * 1024 * 1024,
7 * 1024 * 1024,
8 * 1024 * 1024,
9 * 1024 * 1024,
},
}, []string{TagIPType, TagTelegramIP, TagDC, TagDirection}),
metricTraffic: prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: metricPrefix,
Name: MetricTraffic,
Help: "Traffic which is sent through this proxy.",
}, []string{TagIPType, TagTelegramIP, TagDC, TagDirection}),
metricConcurrencyLimited: prometheus.NewCounter(prometheus.CounterOpts{ metricConcurrencyLimited: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: metricPrefix, Namespace: metricPrefix,
Name: MetricConcurrencyLimited, Name: MetricConcurrencyLimited,
Help: "A number of sessions that were rejected by concurrency limiter.", Help: "A number of sessions that were rejected by concurrency limiter.",
}), }),
metricIPBlocklisted: prometheus.NewCounterVec(prometheus.CounterOpts{ metricIPBlocklisted: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: metricPrefix, Namespace: metricPrefix,
Name: MetricIPBlocklisted, Name: MetricIPBlocklisted,
Help: "A number of rejected sessions due to ip blocklisting", Help: "A number of rejected sessions due to ip blocklisting.",
}, []string{TagIPType}), }),
metricReplayAttacks: prometheus.NewCounter(prometheus.CounterOpts{
Namespace: metricPrefix,
Name: MetricReplayAttacks,
Help: "A number of detected replay attacks.",
}),
} }
registry.MustRegister(factory.metricClientConnections) registry.MustRegister(factory.metricClientConnections)
registry.MustRegister(factory.metricTelegramConnections) registry.MustRegister(factory.metricTelegramConnections)
registry.MustRegister(factory.metricTraffic) registry.MustRegister(factory.metricDomainDisguisingConnections)
registry.MustRegister(factory.metricSessionTraffic)
registry.MustRegister(factory.metricSessionDuration) registry.MustRegister(factory.metricTelegramTraffic)
registry.MustRegister(factory.metricDomainDisguisingTraffic)
registry.MustRegister(factory.metricDomainDisguising)
registry.MustRegister(factory.metricConcurrencyLimited) registry.MustRegister(factory.metricConcurrencyLimited)
registry.MustRegister(factory.metricIPBlocklisted) registry.MustRegister(factory.metricIPBlocklisted)
registry.MustRegister(factory.metricReplayAttacks)
return factory return factory
} }
+9 -11
View File
@@ -64,7 +64,7 @@ func (suite *PrometheusTestSuite) TestEventStartFinish() {
data, err := suite.Get() data, err := suite.Get()
suite.NoError(err) suite.NoError(err)
suite.Contains(data, `mtg_client_connections{ip_type="ipv4"} 1`) suite.Contains(data, `mtg_client_connections{ip_family="ipv4"} 1`)
suite.prometheus.EventConnectedToDC(mtglib.EventConnectedToDC{ suite.prometheus.EventConnectedToDC(mtglib.EventConnectedToDC{
CreatedAt: time.Now(), CreatedAt: time.Now(),
@@ -76,9 +76,9 @@ func (suite *PrometheusTestSuite) TestEventStartFinish() {
data, err = suite.Get() data, err = suite.Get()
suite.NoError(err) suite.NoError(err)
suite.Contains(data, `mtg_telegram_connections{dc="4",ip="10.0.0.1",ip_type="ipv4"} 1`) suite.Contains(data, `mtg_telegram_connections{dc="4",telegram_ip="10.0.0.1"} 1`)
suite.prometheus.EventTraffic(mtglib.EventTraffic{ suite.prometheus.EventTelegramTraffic(mtglib.EventTelegramTraffic{
CreatedAt: time.Now(), CreatedAt: time.Now(),
ConnID: "connID", ConnID: "connID",
Traffic: 200, Traffic: 200,
@@ -88,9 +88,9 @@ func (suite *PrometheusTestSuite) TestEventStartFinish() {
data, err = suite.Get() data, err = suite.Get()
suite.NoError(err) suite.NoError(err)
suite.Contains(data, `mtg_traffic{dc="4",direction="client",ip="10.0.0.1",ip_type="ipv4"} 200`) suite.Contains(data, `mtg_telegram_traffic{dc="4",direction="to_client",telegram_ip="10.0.0.1"} 200`)
suite.prometheus.EventTraffic(mtglib.EventTraffic{ suite.prometheus.EventTelegramTraffic(mtglib.EventTelegramTraffic{
CreatedAt: time.Now(), CreatedAt: time.Now(),
ConnID: "connID", ConnID: "connID",
Traffic: 100, Traffic: 100,
@@ -100,7 +100,7 @@ func (suite *PrometheusTestSuite) TestEventStartFinish() {
data, err = suite.Get() data, err = suite.Get()
suite.NoError(err) suite.NoError(err)
suite.Contains(data, `mtg_traffic{dc="4",direction="telegram",ip="10.0.0.1",ip_type="ipv4"} 100`) suite.Contains(data, `mtg_telegram_traffic{dc="4",direction="from_client",telegram_ip="10.0.0.1"} 100`)
suite.prometheus.EventFinish(mtglib.EventFinish{ suite.prometheus.EventFinish(mtglib.EventFinish{
CreatedAt: time.Now(), CreatedAt: time.Now(),
@@ -110,10 +110,8 @@ func (suite *PrometheusTestSuite) TestEventStartFinish() {
data, err = suite.Get() data, err = suite.Get()
suite.NoError(err) suite.NoError(err)
suite.Contains(data, `mtg_client_connections{ip_type="ipv4"} 0`) suite.Contains(data, `mtg_client_connections{ip_family="ipv4"} 0`)
suite.Contains(data, `mtg_telegram_connections{dc="4",ip="10.0.0.1",ip_type="ipv4"} 0`) suite.Contains(data, `mtg_telegram_connections{dc="4",telegram_ip="10.0.0.1"} 0`)
suite.Contains(data, `mtg_traffic{dc="4",direction="client",ip="10.0.0.1",ip_type="ipv4"} 200`)
suite.Contains(data, `mtg_traffic{dc="4",direction="telegram",ip="10.0.0.1",ip_type="ipv4"} 100`)
} }
func (suite *PrometheusTestSuite) TestEventConcurrencyLimited() { func (suite *PrometheusTestSuite) TestEventConcurrencyLimited() {
@@ -138,7 +136,7 @@ func (suite *PrometheusTestSuite) TestEventIPBlocklisted() {
data, err := suite.Get() data, err := suite.Get()
suite.NoError(err) suite.NoError(err)
suite.Contains(data, `mtg_ip_blocklisted{ip_type="ipv6"} 1`) suite.Contains(data, `mtg_ip_blocklisted 1`)
} }
func TestPrometheus(t *testing.T) { func TestPrometheus(t *testing.T) {
+27 -45
View File
@@ -17,74 +17,64 @@ type statsdProcessor struct {
} }
func (s statsdProcessor) EventStart(evt mtglib.EventStart) { func (s statsdProcessor) EventStart(evt mtglib.EventStart) {
sInfo := &streamInfo{ info := acquireStreamInfo()
createdAt: evt.CreatedAt, info.SetStartTime(evt.CreatedAt)
clientIP: evt.RemoteIP, info.SetClientIP(evt.RemoteIP)
} s.streams[evt.StreamID()] = info
s.streams[evt.StreamID()] = sInfo
s.client.GaugeDelta(MetricClientConnections, s.client.GaugeDelta(MetricClientConnections,
1, 1,
statsd.StringTag(TagIPType, sInfo.GetClientIPType())) info.TV(TagIPFamily))
} }
func (s statsdProcessor) EventConnectedToDC(evt mtglib.EventConnectedToDC) { func (s statsdProcessor) EventConnectedToDC(evt mtglib.EventConnectedToDC) {
sInfo, ok := s.streams[evt.StreamID()] info, ok := s.streams[evt.StreamID()]
if !ok { if !ok {
return return
} }
sInfo.remoteIP = evt.RemoteIP info.SetTelegramIP(evt.RemoteIP)
sInfo.dc = evt.DC info.SetDC(evt.DC)
s.client.GaugeDelta(MetricTelegramConnections, s.client.GaugeDelta(MetricTelegramConnections,
1, 1,
statsd.StringTag(TagIPType, sInfo.GetRemoteIPType()), info.TV(TagTelegramIP),
statsd.StringTag(TagTelegramIP, sInfo.remoteIP.String()), info.TV(TagDC))
statsd.IntTag(TagDC, sInfo.dc))
} }
func (s statsdProcessor) EventTraffic(evt mtglib.EventTraffic) { func (s statsdProcessor) EventTelegramTraffic(evt mtglib.EventTelegramTraffic) {
sInfo, ok := s.streams[evt.StreamID()] info, ok := s.streams[evt.StreamID()]
if !ok { if !ok {
return return
} }
tags := []statsd.Tag{ s.client.Incr(MetricTelegramTraffic,
statsd.StringTag(TagIPType, sInfo.GetRemoteIPType()), int64(evt.Traffic),
statsd.StringTag(TagTelegramIP, sInfo.remoteIP.String()), info.TV(TagTelegramIP),
statsd.IntTag(TagDC, sInfo.dc), info.TV(TagDC),
} statsd.StringTag(TagDirection, getDirection(evt.IsRead)))
if evt.IsRead {
tags = append(tags, statsd.StringTag(TagDirection, TagDirectionClient))
s.client.Incr(MetricTraffic, int64(evt.Traffic), tags...)
} else {
tags = append(tags, statsd.StringTag(TagDirection, TagDirectionTelegram))
s.client.Incr(MetricTraffic, int64(evt.Traffic), tags...)
}
} }
func (s statsdProcessor) EventFinish(evt mtglib.EventFinish) { func (s statsdProcessor) EventFinish(evt mtglib.EventFinish) {
sInfo, ok := s.streams[evt.StreamID()] info, ok := s.streams[evt.StreamID()]
if !ok { if !ok {
return return
} }
defer delete(s.streams, evt.StreamID()) defer func() {
delete(s.streams, evt.StreamID())
releaseStreamInfo(info)
}()
s.client.GaugeDelta(MetricClientConnections, s.client.GaugeDelta(MetricClientConnections,
-1, -1,
statsd.StringTag(TagIPType, sInfo.GetClientIPType())) info.TV(TagIPFamily))
s.client.PrecisionTiming(MetricSessionDuration,
evt.CreatedAt.Sub(sInfo.createdAt))
if sInfo.remoteIP != nil { if info.V(TagTelegramIP) != "" {
s.client.GaugeDelta(MetricTelegramConnections, s.client.GaugeDelta(MetricTelegramConnections,
-1, -1,
statsd.StringTag(TagIPType, sInfo.GetRemoteIPType()), info.TV(TagTelegramIP),
statsd.StringTag(TagTelegramIP, sInfo.remoteIP.String()), info.TV(TagDC))
statsd.IntTag(TagDC, sInfo.dc))
} }
} }
@@ -93,15 +83,7 @@ func (s statsdProcessor) EventConcurrencyLimited(_ mtglib.EventConcurrencyLimite
} }
func (s statsdProcessor) EventIPBlocklisted(evt mtglib.EventIPBlocklisted) { func (s statsdProcessor) EventIPBlocklisted(evt mtglib.EventIPBlocklisted) {
var tag statsd.Tag s.client.Incr(MetricIPBlocklisted, 1)
if evt.RemoteIP.To4() == nil {
tag = statsd.StringTag(TagIPType, TagIPTypeIPv6)
} else {
tag = statsd.StringTag(TagIPType, TagIPTypeIPv4)
}
s.client.Incr(MetricIPBlocklisted, 1, tag)
} }
func (s statsdProcessor) Shutdown() { func (s statsdProcessor) Shutdown() {
+9 -10
View File
@@ -111,7 +111,7 @@ func (suite *StatsdTestSuite) TestEventStartFinish() {
RemoteIP: net.ParseIP("10.0.0.10"), RemoteIP: net.ParseIP("10.0.0.10"),
}) })
time.Sleep(statsdSleepTime) time.Sleep(statsdSleepTime)
suite.Equal("mtg.client_connections:+1|g|#ip_type:ipv4", suite.statsdServer.String()) suite.Equal("mtg.client_connections:+1|g|#ip_family:ipv4", suite.statsdServer.String())
suite.statsd.EventConnectedToDC(mtglib.EventConnectedToDC{ suite.statsd.EventConnectedToDC(mtglib.EventConnectedToDC{
CreatedAt: time.Now(), CreatedAt: time.Now(),
@@ -121,9 +121,9 @@ func (suite *StatsdTestSuite) TestEventStartFinish() {
}) })
time.Sleep(statsdSleepTime) time.Sleep(statsdSleepTime)
suite.Contains(suite.statsdServer.String(), suite.Contains(suite.statsdServer.String(),
"mtg.telegram_connections:+1|g|#ip_type:ipv4,ip:10.1.0.10,dc:2") "mtg.telegram_connections:+1|g|#telegram_ip:10.1.0.10,dc:2")
suite.statsd.EventTraffic(mtglib.EventTraffic{ suite.statsd.EventTelegramTraffic(mtglib.EventTelegramTraffic{
CreatedAt: time.Now(), CreatedAt: time.Now(),
ConnID: "connID", ConnID: "connID",
Traffic: 30, Traffic: 30,
@@ -131,9 +131,9 @@ func (suite *StatsdTestSuite) TestEventStartFinish() {
}) })
time.Sleep(statsdSleepTime) time.Sleep(statsdSleepTime)
suite.Contains(suite.statsdServer.String(), suite.Contains(suite.statsdServer.String(),
"mtg.traffic:30|c|#ip_type:ipv4,ip:10.1.0.10,dc:2,direction:client") "mtg.telegram_traffic:30|c|#telegram_ip:10.1.0.10,dc:2,direction:to_client")
suite.statsd.EventTraffic(mtglib.EventTraffic{ suite.statsd.EventTelegramTraffic(mtglib.EventTelegramTraffic{
CreatedAt: time.Now(), CreatedAt: time.Now(),
ConnID: "connID", ConnID: "connID",
Traffic: 90, Traffic: 90,
@@ -141,18 +141,17 @@ func (suite *StatsdTestSuite) TestEventStartFinish() {
}) })
time.Sleep(statsdSleepTime) time.Sleep(statsdSleepTime)
suite.Contains(suite.statsdServer.String(), suite.Contains(suite.statsdServer.String(),
"mtg.traffic:90|c|#ip_type:ipv4,ip:10.1.0.10,dc:2,direction:telegram") "mtg.telegram_traffic:90|c|#telegram_ip:10.1.0.10,dc:2,direction:from_client")
suite.statsd.EventFinish(mtglib.EventFinish{ suite.statsd.EventFinish(mtglib.EventFinish{
CreatedAt: time.Now(), CreatedAt: time.Now(),
ConnID: "connID", ConnID: "connID",
}) })
time.Sleep(statsdSleepTime) time.Sleep(statsdSleepTime)
suite.Contains(suite.statsdServer.String(), "mtg.session_duration")
suite.Contains(suite.statsdServer.String(), suite.Contains(suite.statsdServer.String(),
"mtg.telegram_connections:-1|g|#ip_type:ipv4,ip:10.1.0.10,dc:2") "mtg.telegram_connections:-1|g|#telegram_ip:10.1.0.10,dc:2")
suite.Contains(suite.statsdServer.String(), suite.Contains(suite.statsdServer.String(),
"mtg.client_connections:-1|g|#ip_type:ipv4") "mtg.client_connections:-1|g|#ip_family:ipv4")
} }
func (suite *StatsdTestSuite) TestEventConcurrencyLimited() { func (suite *StatsdTestSuite) TestEventConcurrencyLimited() {
@@ -171,7 +170,7 @@ func (suite *StatsdTestSuite) TestEventIPBlocklisted() {
}) })
time.Sleep(statsdSleepTime) time.Sleep(statsdSleepTime)
suite.Equal("mtg.ip_blocklisted:1|c|#ip_type:ipv4", suite.statsdServer.String()) suite.Equal("mtg.ip_blocklisted:1|c", suite.statsdServer.String())
} }
func TestStatsd(t *testing.T) { func TestStatsd(t *testing.T) {
+41 -14
View File
@@ -2,30 +2,57 @@ package stats
import ( import (
"net" "net"
"strconv"
"time" "time"
statsd "github.com/smira/go-statsd"
) )
type streamInfo struct { type streamInfo struct {
createdAt time.Time startTime time.Time
clientIP net.IP tags map[string]string
remoteIP net.IP
dc int
bytesSentToTelegram uint
bytesRecvFromTelegram uint
} }
func (s *streamInfo) GetClientIPType() string { func (s *streamInfo) SetStartTime(tme time.Time) {
return s.getIPType(s.clientIP) s.startTime = tme
} }
func (s *streamInfo) GetRemoteIPType() string { func (s *streamInfo) SetClientIP(ip net.IP) {
return s.getIPType(s.remoteIP) if ip.To4() != nil {
s.tags[TagIPFamily] = TagIPFamilyIPv4
} else {
s.tags[TagIPFamily] = TagIPFamilyIPv6
}
} }
func (s *streamInfo) getIPType(ip net.IP) string { func (s *streamInfo) SetTelegramIP(ip net.IP) {
if ip.To4() == nil { s.tags[TagTelegramIP] = ip.String()
return TagIPTypeIPv6 }
func (s *streamInfo) SetDC(dc int) {
s.tags[TagDC] = strconv.Itoa(dc)
}
func (s *streamInfo) V(key string) string {
return s.tags[key]
}
func (s *streamInfo) TV(key string) statsd.Tag {
return statsd.StringTag(key, s.tags[key])
}
func (s *streamInfo) Reset() {
s.startTime = time.Time{}
for k := range s.tags {
delete(s.tags, k)
}
}
func getDirection(isRead bool) string {
if isRead { // for telegram
return TagDirectionToClient
} }
return TagIPTypeIPv4 return TagDirectionFromClient
} }