github.com/status-im/status-go@v1.1.0/centralizedmetrics/metrics.go (about) 1 package centralizedmetrics 2 3 import ( 4 "database/sql" 5 "sync" 6 "time" 7 8 "github.com/ethereum/go-ethereum/log" 9 10 "github.com/status-im/status-go/centralizedmetrics/common" 11 "github.com/status-im/status-go/centralizedmetrics/providers" 12 ) 13 14 const defaultPollInterval = 10 * time.Second 15 16 type MetricsInfo struct { 17 Enabled bool `json:"enabled"` 18 UserConfirmed bool `json:"userConfirmed"` 19 } 20 21 type MetricRepository interface { 22 Poll() ([]common.Metric, error) 23 Delete(metrics []common.Metric) error 24 Add(metric common.Metric) error 25 Info() (*MetricsInfo, error) 26 ToggleEnabled(isEnabled bool) error 27 } 28 29 type MetricService struct { 30 repository MetricRepository 31 processor common.MetricProcessor 32 ticker *time.Ticker 33 done chan bool 34 started bool 35 wg sync.WaitGroup 36 interval time.Duration 37 } 38 39 func NewDefaultMetricService(db *sql.DB) *MetricService { 40 repository := NewSQLiteMetricRepository(db) 41 processor := providers.NewMixpanelMetricProcessor(providers.MixpanelAppID, providers.MixpanelToken, providers.MixpanelBaseURL) 42 return NewMetricService(repository, processor, defaultPollInterval) 43 } 44 45 func NewMetricService(repository MetricRepository, processor common.MetricProcessor, interval time.Duration) *MetricService { 46 return &MetricService{ 47 repository: repository, 48 processor: processor, 49 interval: interval, 50 done: make(chan bool), 51 } 52 } 53 54 func (s *MetricService) Start() { 55 if s.started { 56 return 57 } 58 s.ticker = time.NewTicker(s.interval) 59 s.wg.Add(1) 60 s.started = true 61 go func() { 62 defer s.wg.Done() 63 for { 64 select { 65 case <-s.done: 66 return 67 case <-s.ticker.C: 68 s.processMetrics() 69 } 70 } 71 }() 72 } 73 74 func (s *MetricService) Stop() { 75 if !s.started { 76 return 77 } 78 s.ticker.Stop() 79 s.done <- true 80 s.wg.Wait() 81 s.started = false 82 } 83 84 func (s *MetricService) EnsureStarted() error { 85 info, err := s.Info() 86 if err != nil { 87 return err 88 } 89 if info.Enabled { 90 s.Start() 91 } 92 return nil 93 } 94 95 func (s *MetricService) Info() (*MetricsInfo, error) { 96 return s.repository.Info() 97 } 98 99 func (s *MetricService) ToggleEnabled(isEnabled bool) error { 100 err := s.repository.ToggleEnabled(isEnabled) 101 if err != nil { 102 return err 103 } 104 if isEnabled { 105 s.Start() 106 } else { 107 s.Stop() 108 } 109 return nil 110 } 111 112 func (s *MetricService) AddMetric(metric common.Metric) error { 113 return s.repository.Add(metric) 114 } 115 116 func (s *MetricService) processMetrics() { 117 log.Info("processing metrics") 118 metrics, err := s.repository.Poll() 119 if err != nil { 120 log.Warn("error polling metrics", "error", err) 121 return 122 } 123 log.Info("polled metrics") 124 125 if len(metrics) == 0 { 126 return 127 } 128 log.Info("processing metrics") 129 130 if err := s.processor.Process(metrics); err != nil { 131 log.Warn("error processing metrics", "error", err) 132 return 133 } 134 135 log.Info("deleting metrics") 136 if err := s.repository.Delete(metrics); err != nil { 137 log.Warn("error deleting metrics", "error", err) 138 } 139 log.Info("done metrics") 140 }