Highly-opinionated (ex-bullshit-free) MTPROTO proxy for Telegram. If you use v1.0 or upgrade broke you proxy, please read the chapter Version 2
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

prometheus.go 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. package stats
  2. import (
  3. "context"
  4. "net"
  5. "net/http"
  6. "strconv"
  7. "github.com/dolonet/mtg-multi/events"
  8. "github.com/dolonet/mtg-multi/mtglib"
  9. "github.com/prometheus/client_golang/prometheus"
  10. "github.com/prometheus/client_golang/prometheus/promhttp"
  11. )
  12. type prometheusProcessor struct {
  13. streams map[string]*streamInfo
  14. factory *PrometheusFactory
  15. }
  16. func (p prometheusProcessor) EventStart(evt mtglib.EventStart) {
  17. info := acquireStreamInfo()
  18. if evt.RemoteIP.To4() != nil {
  19. info.tags[TagIPFamily] = TagIPFamilyIPv4
  20. } else {
  21. info.tags[TagIPFamily] = TagIPFamilyIPv6
  22. }
  23. p.streams[evt.StreamID()] = info
  24. p.factory.metricClientConnections.
  25. WithLabelValues(info.tags[TagIPFamily]).
  26. Inc()
  27. }
  28. func (p prometheusProcessor) EventConnectedToDC(evt mtglib.EventConnectedToDC) {
  29. info, ok := p.streams[evt.StreamID()]
  30. if !ok {
  31. return
  32. }
  33. info.tags[TagTelegramIP] = evt.RemoteIP.String()
  34. info.tags[TagDC] = strconv.Itoa(evt.DC)
  35. p.factory.metricTelegramConnections.
  36. WithLabelValues(info.tags[TagTelegramIP], info.tags[TagDC]).
  37. Inc()
  38. }
  39. func (p prometheusProcessor) EventDomainFronting(evt mtglib.EventDomainFronting) {
  40. info, ok := p.streams[evt.StreamID()]
  41. if !ok {
  42. return
  43. }
  44. info.isDomainFronted = true
  45. p.factory.metricDomainFronting.Inc()
  46. p.factory.metricDomainFrontingConnections.
  47. WithLabelValues(info.tags[TagIPFamily]).
  48. Inc()
  49. }
  50. func (p prometheusProcessor) EventTraffic(evt mtglib.EventTraffic) {
  51. info, ok := p.streams[evt.StreamID()]
  52. if !ok {
  53. return
  54. }
  55. direction := getDirection(evt.IsRead)
  56. if info.isDomainFronted {
  57. p.factory.metricDomainFrontingTraffic.
  58. WithLabelValues(direction).
  59. Add(float64(evt.Traffic))
  60. } else {
  61. p.factory.metricTelegramTraffic.
  62. WithLabelValues(info.tags[TagTelegramIP], info.tags[TagDC], direction).
  63. Add(float64(evt.Traffic))
  64. }
  65. }
  66. func (p prometheusProcessor) EventFinish(evt mtglib.EventFinish) {
  67. info, ok := p.streams[evt.StreamID()]
  68. if !ok {
  69. return
  70. }
  71. defer func() {
  72. delete(p.streams, evt.StreamID())
  73. releaseStreamInfo(info)
  74. }()
  75. p.factory.metricClientConnections.
  76. WithLabelValues(info.tags[TagIPFamily]).
  77. Dec()
  78. if info.isDomainFronted {
  79. p.factory.metricDomainFrontingConnections.
  80. WithLabelValues(info.tags[TagIPFamily]).
  81. Dec()
  82. } else if telegramIP, ok := info.tags[TagTelegramIP]; ok {
  83. p.factory.metricTelegramConnections.
  84. WithLabelValues(telegramIP, info.tags[TagDC]).
  85. Dec()
  86. }
  87. }
  88. func (p prometheusProcessor) EventConcurrencyLimited(_ mtglib.EventConcurrencyLimited) {
  89. p.factory.metricConcurrencyLimited.Inc()
  90. }
  91. func (p prometheusProcessor) EventIPBlocklisted(evt mtglib.EventIPBlocklisted) {
  92. tag := TagIPListBlock
  93. if !evt.IsBlockList {
  94. tag = TagIPListAllow
  95. }
  96. p.factory.metricIPBlocklisted.WithLabelValues(tag).Inc()
  97. }
  98. func (p prometheusProcessor) EventReplayAttack(_ mtglib.EventReplayAttack) {
  99. p.factory.metricReplayAttacks.Inc()
  100. }
  101. func (p prometheusProcessor) EventIPListSize(evt mtglib.EventIPListSize) {
  102. tag := TagIPListBlock
  103. if !evt.IsBlockList {
  104. tag = TagIPListAllow
  105. }
  106. p.factory.metricIPListSize.WithLabelValues(tag).Set(float64(evt.Size))
  107. }
  108. func (p prometheusProcessor) Shutdown() {
  109. for k, v := range p.streams {
  110. releaseStreamInfo(v)
  111. delete(p.streams, k)
  112. }
  113. }
  114. // PrometheusFactory is a factory of [events.Observer] which collect
  115. // information in a format suitable for Prometheus.
  116. //
  117. // This factory can also serve on a given listener. In that case it starts HTTP
  118. // server with a single endpoint - a Prometheus-compatible scrape output.
  119. type PrometheusFactory struct {
  120. httpServer *http.Server
  121. metricClientConnections *prometheus.GaugeVec
  122. metricTelegramConnections *prometheus.GaugeVec
  123. metricDomainFrontingConnections *prometheus.GaugeVec
  124. metricIPListSize *prometheus.GaugeVec
  125. metricTelegramTraffic *prometheus.CounterVec
  126. metricDomainFrontingTraffic *prometheus.CounterVec
  127. metricIPBlocklisted *prometheus.CounterVec
  128. metricDomainFronting prometheus.Counter
  129. metricConcurrencyLimited prometheus.Counter
  130. metricReplayAttacks prometheus.Counter
  131. }
  132. // Make builds a new observer.
  133. func (p *PrometheusFactory) Make() events.Observer {
  134. return prometheusProcessor{
  135. streams: make(map[string]*streamInfo),
  136. factory: p,
  137. }
  138. }
  139. // Serve starts an HTTP server on a given listener.
  140. func (p *PrometheusFactory) Serve(listener net.Listener) error {
  141. return p.httpServer.Serve(listener) //nolint: wrapcheck
  142. }
  143. // Close stops a factory. Please pay attention that underlying listener
  144. // is not closed.
  145. func (p *PrometheusFactory) Close() error {
  146. return p.httpServer.Shutdown(context.Background()) //nolint: wrapcheck
  147. }
  148. // NewPrometheus builds an events.ObserverFactory which can serve HTTP
  149. // endpoint with Prometheus scrape data.
  150. func NewPrometheus(metricPrefix, httpPath string) *PrometheusFactory { //nolint: funlen
  151. registry := prometheus.NewPedanticRegistry()
  152. httpHandler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{
  153. EnableOpenMetrics: true,
  154. })
  155. mux := http.NewServeMux()
  156. mux.Handle(httpPath, httpHandler)
  157. factory := &PrometheusFactory{
  158. httpServer: &http.Server{
  159. Handler: mux,
  160. },
  161. metricClientConnections: prometheus.NewGaugeVec(prometheus.GaugeOpts{
  162. Namespace: metricPrefix,
  163. Name: MetricClientConnections,
  164. Help: "A number of actively processing client connections.",
  165. }, []string{TagIPFamily}),
  166. metricTelegramConnections: prometheus.NewGaugeVec(prometheus.GaugeOpts{
  167. Namespace: metricPrefix,
  168. Name: MetricTelegramConnections,
  169. Help: "A number of connections to Telegram servers.",
  170. }, []string{TagTelegramIP, TagDC}),
  171. metricDomainFrontingConnections: prometheus.NewGaugeVec(prometheus.GaugeOpts{
  172. Namespace: metricPrefix,
  173. Name: MetricDomainFrontingConnections,
  174. Help: "A number of connections which talk to front domain.",
  175. }, []string{TagIPFamily}),
  176. metricIPListSize: prometheus.NewGaugeVec(prometheus.GaugeOpts{
  177. Namespace: metricPrefix,
  178. Name: MetricIPListSize,
  179. Help: "A size of the ip list (blocklist or allowlist)",
  180. }, []string{TagIPList}),
  181. metricTelegramTraffic: prometheus.NewCounterVec(prometheus.CounterOpts{
  182. Namespace: metricPrefix,
  183. Name: MetricTelegramTraffic,
  184. Help: "Traffic which is generated talking with Telegram servers.",
  185. }, []string{TagTelegramIP, TagDC, TagDirection}),
  186. metricDomainFrontingTraffic: prometheus.NewCounterVec(prometheus.CounterOpts{
  187. Namespace: metricPrefix,
  188. Name: MetricDomainFrontingTraffic,
  189. Help: "Traffic which is generated talking with front domain.",
  190. }, []string{TagDirection}),
  191. metricIPBlocklisted: prometheus.NewCounterVec(prometheus.CounterOpts{
  192. Namespace: metricPrefix,
  193. Name: MetricIPBlocklisted,
  194. Help: "A number of rejected sessions due to ip blocklisting.",
  195. }, []string{TagIPList}),
  196. metricDomainFronting: prometheus.NewCounter(prometheus.CounterOpts{
  197. Namespace: metricPrefix,
  198. Name: MetricDomainFronting,
  199. Help: "A number of routings to front domain.",
  200. }),
  201. metricConcurrencyLimited: prometheus.NewCounter(prometheus.CounterOpts{
  202. Namespace: metricPrefix,
  203. Name: MetricConcurrencyLimited,
  204. Help: "A number of sessions that were rejected by concurrency limiter.",
  205. }),
  206. metricReplayAttacks: prometheus.NewCounter(prometheus.CounterOpts{
  207. Namespace: metricPrefix,
  208. Name: MetricReplayAttacks,
  209. Help: "A number of detected replay attacks.",
  210. }),
  211. }
  212. registry.MustRegister(factory.metricClientConnections)
  213. registry.MustRegister(factory.metricTelegramConnections)
  214. registry.MustRegister(factory.metricDomainFrontingConnections)
  215. registry.MustRegister(factory.metricIPListSize)
  216. registry.MustRegister(factory.metricTelegramTraffic)
  217. registry.MustRegister(factory.metricDomainFrontingTraffic)
  218. registry.MustRegister(factory.metricIPBlocklisted)
  219. registry.MustRegister(factory.metricDomainFronting)
  220. registry.MustRegister(factory.metricConcurrencyLimited)
  221. registry.MustRegister(factory.metricReplayAttacks)
  222. return factory
  223. }