github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/metrics/metrics.go (about) 1 package metrics 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "net/url" 9 10 "github.com/prometheus/client_golang/prometheus" 11 12 "github.com/machinefi/w3bstream/pkg/depends/kit/logr" 13 "github.com/machinefi/w3bstream/pkg/models" 14 "github.com/machinefi/w3bstream/pkg/types" 15 ) 16 17 const ( 18 _eventMtcName = "inbound_events_metrics" 19 _publisherMtcName = "publishers_metrics" 20 _blockChainTxMtcName = "w3b_blockchain_tx_metrics" 21 ) 22 23 var ( 24 eventMtc = prometheus.NewCounterVec( 25 prometheus.CounterOpts{ 26 Name: _eventMtcName, 27 Help: "received events metrics.", 28 }, 29 []string{"account", "project", "publisher", "eventtype"}, 30 ) 31 eventClickhouseCli = NewSQLBatcher("INSERT INTO ws_metrics.inbound_events_metrics VALUES") 32 33 publisherMtc = prometheus.NewGaugeVec( 34 prometheus.GaugeOpts{ 35 Name: _publisherMtcName, 36 Help: "registered publishers for the project.", 37 }, 38 []string{"account", "project"}, 39 ) 40 publisherClickhouseCli = NewSQLBatcher("INSERT INTO ws_metrics.publishers_metrics VALUES") 41 42 BlockChainTxMtc = prometheus.NewCounterVec(prometheus.CounterOpts{ 43 Name: _blockChainTxMtcName, 44 Help: "blockchain transaction counter metrics.", 45 }, []string{"project", "chainID"}) 46 ) 47 48 func init() { 49 prometheus.MustRegister(eventMtc) 50 prometheus.MustRegister(publisherMtc) 51 prometheus.MustRegister(BlockChainTxMtc) 52 } 53 54 func RemoveMetrics(ctx context.Context, account string, project string) { 55 ctx, l := logr.Start(ctx, "metrics.RemoveMetrics") 56 defer l.End() 57 58 eventMtc.DeletePartialMatch(prometheus.Labels{"account": account, "project": project}) 59 publisherMtc.DeletePartialMatch(prometheus.Labels{"account": account, "project": project}) 60 BlockChainTxMtc.DeletePartialMatch(prometheus.Labels{"project": project}) 61 62 // erase data in metrics server 63 if err := eraseDataInServer(ctx, account, project); err != nil { 64 // the metrics server isn't essential for the core service 65 l.Warn(err) 66 } 67 if clickhouseCLI != nil { 68 if err := clickhouseCLI.Insert(fmt.Sprintf(`DELETE FROM ws_metrics.inbound_events_metrics WHERE ( 69 account = '%s') AND (project = '%s')`, account, project)); err != nil { 70 l.Warn(err) 71 } 72 if err := clickhouseCLI.Insert(fmt.Sprintf(`DELETE FROM ws_metrics.publishers_metrics WHERE ( 73 account = '%s') AND (project = '%s')`, account, project)); err != nil { 74 l.Warn(err) 75 } 76 if err := clickhouseCLI.Insert(fmt.Sprintf(`DELETE FROM ws_metrics.customized_metrics WHERE ( 77 account = '%s') AND (project = '%s')`, account, project)); err != nil { 78 l.Warn(err) 79 } 80 } 81 } 82 83 func EventMetricsInc(ctx context.Context, v *models.Event) { 84 ctx, l := logr.Start(ctx, "metrics.EventMetricsInc") 85 defer l.End() 86 87 eventMtc.WithLabelValues(v.AccountID.String(), v.ProjectName, v.PublisherKey, v.EventType).Inc() 88 if clickhouseCLI != nil { 89 if err := eventClickhouseCli.Insert(fmt.Sprintf(`now(), '%s', '%s', '%s', '%s', %d`, 90 v.AccountID, v.ProjectName, v.PublisherKey, v.EventType, 1)); err != nil { 91 l.Error(err) 92 } 93 } 94 } 95 96 func PublisherMetricsInc(ctx context.Context, account, project string) { 97 ctx, l := logr.Start(ctx, "metrics.PublisherMetricsInc") 98 defer l.End() 99 100 publisherMtc.WithLabelValues(account, project).Inc() 101 if clickhouseCLI != nil { 102 if err := publisherClickhouseCli.Insert(fmt.Sprintf(`now(), '%s', '%s', %d`, account, project, 1)); err != nil { 103 l.Error(err) 104 } 105 } 106 } 107 108 func PublisherMetricsDec(ctx context.Context, account, project string) { 109 ctx, l := logr.Start(ctx, "metrics.PublisherMetricsDec") 110 defer l.End() 111 112 publisherMtc.WithLabelValues(account, project).Dec() 113 if clickhouseCLI != nil { 114 if err := publisherClickhouseCli.Insert(fmt.Sprintf(`now(), '%s', '%s', %d`, account, project, -1)); err != nil { 115 l.Error(err) 116 } 117 } 118 } 119 120 func eraseDataInServer(ctx context.Context, account string, project string) error { 121 cfg, existed := types.MetricsCenterConfigFromContext(ctx) 122 if !existed { 123 return errors.New("fail to get the url of metrics center") 124 } 125 baseURL := cfg.Endpoint 126 127 if err := httpReq( 128 fmt.Sprintf("%s/api/v1/admin/tsdb/delete_series?match[]=%s", 129 baseURL, 130 url.QueryEscape(fmt.Sprintf(`%s{account="%s", project="%s"}`, _eventMtcName, account, project))), 131 ); err != nil { 132 return err 133 } 134 135 if err := httpReq( 136 fmt.Sprintf("%s/api/v1/admin/tsdb/delete_series?match[]=%s", 137 baseURL, 138 url.QueryEscape(fmt.Sprintf(`%s{account="%s", project="%s"}`, _publisherMtcName, account, project))), 139 ); err != nil { 140 return err 141 } 142 143 if err := httpReq( 144 fmt.Sprintf("%s/api/v1/admin/tsdb/delete_series?match[]=%s", 145 baseURL, 146 url.QueryEscape(fmt.Sprintf(`%s{project="%s"}`, _blockChainTxMtcName, project))), 147 ); err != nil { 148 return err 149 } 150 return cleanTombstones(baseURL) 151 } 152 153 func cleanTombstones(baseURL string) error { 154 return httpReq(fmt.Sprintf("%s/api/v1/admin/tsdb/clean_tombstones", baseURL)) 155 } 156 157 func httpReq(url string) error { 158 // Create the HTTP client and request 159 client := http.Client{} 160 req, err := http.NewRequest("POST", url, nil) 161 if err != nil { 162 return err 163 } 164 165 // Send the request 166 resp, err := client.Do(req) 167 if err != nil { 168 return err 169 } 170 defer resp.Body.Close() 171 172 if resp.StatusCode != 204 { 173 return errors.New("the http request to metrics center fails") 174 } 175 176 return nil 177 }