metrics.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. /*
  2. We export metrics in the following format:
  3. "snowflake-stats-end" YYYY-MM-DD HH:MM:SS (NSEC s) NL
  4. [At most once.]
  5. YYYY-MM-DD HH:MM:SS defines the end of the included measurement
  6. interval of length NSEC seconds (86400 seconds by default).
  7. "snowflake-ips" CC=NUM,CC=NUM,... NL
  8. [At most once.]
  9. List of mappings from two-letter country codes to the number of
  10. unique IP addresses of snowflake proxies that have polled.
  11. "snowflake-ips-total" NUM NL
  12. [At most once.]
  13. A count of the total number of unique IP addresses of snowflake
  14. proxies that have polled.
  15. "snowflake-idle-count" NUM NL
  16. [At most once.]
  17. A count of the number of times a proxy has polled but received
  18. no client offer, rounded up to the nearest multiple of 8.
  19. "client-denied-count" NUM NL
  20. [At most once.]
  21. A count of the number of times a client has requested a proxy
  22. from the broker but no proxies were available, rounded up to
  23. the nearest multiple of 8.
  24. "client-snowflake-match-count" NUM NL
  25. [At most once.]
  26. A count of the number of times a client successfully received a
  27. proxy from the broker, rounded up to the nearest multiple of 8.
  28. */
  29. package main
  30. import (
  31. // "golang.org/x/net/internal/timeseries"
  32. "fmt"
  33. "log"
  34. "math"
  35. "net"
  36. "sync"
  37. "time"
  38. )
  39. var (
  40. once sync.Once
  41. )
  42. const metricsResolution = 60 * 60 * 24 * time.Second //86400 seconds
  43. type CountryStats struct {
  44. addrs map[string]bool
  45. counts map[string]int
  46. }
  47. // Implements Observable
  48. type Metrics struct {
  49. logger *log.Logger
  50. tablev4 *GeoIPv4Table
  51. tablev6 *GeoIPv6Table
  52. countryStats CountryStats
  53. clientRoundtripEstimate time.Duration
  54. proxyIdleCount uint
  55. clientDeniedCount uint
  56. clientProxyMatchCount uint
  57. }
  58. func (s CountryStats) Display() string {
  59. output := ""
  60. for cc, count := range s.counts {
  61. output += fmt.Sprintf("%s=%d,", cc, count)
  62. }
  63. // cut off trailing ","
  64. if len(output) > 0 {
  65. return output[:len(output)-1]
  66. }
  67. return output
  68. }
  69. func (m *Metrics) UpdateCountryStats(addr string) {
  70. var country string
  71. var ok bool
  72. if m.countryStats.addrs[addr] {
  73. return
  74. }
  75. ip := net.ParseIP(addr)
  76. if ip.To4() != nil {
  77. //This is an IPv4 address
  78. if m.tablev4 == nil {
  79. return
  80. }
  81. country, ok = GetCountryByAddr(m.tablev4, ip)
  82. } else {
  83. if m.tablev6 == nil {
  84. return
  85. }
  86. country, ok = GetCountryByAddr(m.tablev6, ip)
  87. }
  88. if !ok {
  89. country = "??"
  90. log.Println("Unknown geoip")
  91. }
  92. //update map of unique ips and counts
  93. m.countryStats.counts[country]++
  94. m.countryStats.addrs[addr] = true
  95. return
  96. }
  97. func (m *Metrics) LoadGeoipDatabases(geoipDB string, geoip6DB string) error {
  98. // Load geoip databases
  99. log.Println("Loading geoip databases")
  100. tablev4 := new(GeoIPv4Table)
  101. err := GeoIPLoadFile(tablev4, geoipDB)
  102. if err != nil {
  103. m.tablev4 = nil
  104. return err
  105. } else {
  106. m.tablev4 = tablev4
  107. }
  108. tablev6 := new(GeoIPv6Table)
  109. err = GeoIPLoadFile(tablev6, geoip6DB)
  110. if err != nil {
  111. m.tablev6 = nil
  112. return err
  113. } else {
  114. m.tablev6 = tablev6
  115. }
  116. return nil
  117. }
  118. func NewMetrics(metricsLogger *log.Logger) (*Metrics, error) {
  119. m := new(Metrics)
  120. m.countryStats = CountryStats{
  121. counts: make(map[string]int),
  122. addrs: make(map[string]bool),
  123. }
  124. m.logger = metricsLogger
  125. // Write to log file every hour with updated metrics
  126. go once.Do(m.logMetrics)
  127. return m, nil
  128. }
  129. // Logs metrics in intervals specified by metricsResolution
  130. func (m *Metrics) logMetrics() {
  131. heartbeat := time.Tick(metricsResolution)
  132. for range heartbeat {
  133. m.printMetrics()
  134. m.zeroMetrics()
  135. }
  136. }
  137. func (m *Metrics) printMetrics() {
  138. m.logger.Println("snowflake-stats-end", time.Now().UTC().Format("2006-01-02 15:04:05"), fmt.Sprintf("(%d s)", int(metricsResolution.Seconds())))
  139. m.logger.Println("snowflake-ips", m.countryStats.Display())
  140. m.logger.Println("snowflake-ips-total", len(m.countryStats.addrs))
  141. m.logger.Println("snowflake-idle-count", binCount(m.proxyIdleCount))
  142. m.logger.Println("client-denied-count", binCount(m.clientDeniedCount))
  143. m.logger.Println("client-snowflake-match-count", binCount(m.clientProxyMatchCount))
  144. }
  145. // Restores all metrics to original values
  146. func (m *Metrics) zeroMetrics() {
  147. m.proxyIdleCount = 0
  148. m.clientDeniedCount = 0
  149. m.clientProxyMatchCount = 0
  150. m.countryStats.counts = make(map[string]int)
  151. m.countryStats.addrs = make(map[string]bool)
  152. }
  153. // Rounds up a count to the nearest multiple of 8.
  154. func binCount(count uint) uint {
  155. return uint((math.Ceil(float64(count) / 8)) * 8)
  156. }