github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/vvm/metrics/impl.go (about) 1 /* 2 * Copyright (c) 2022-present unTill Pro, Ltd. 3 */ 4 5 package metrics 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "net" 12 "net/http" 13 14 "github.com/voedger/voedger/pkg/goutils/logger" 15 16 imetrics "github.com/voedger/voedger/pkg/metrics" 17 coreutils "github.com/voedger/voedger/pkg/utils" 18 ) 19 20 func (ms *metricsService) Prepare(interface{}) (err error) { 21 ms.listener, err = net.Listen("tcp", coreutils.ServerAddress(ms.port)) 22 return err 23 } 24 25 func (ms *metricsService) Run(_ context.Context) { 26 logger.Info("Starting Metrics Service on", ms.listener.Addr().(*net.TCPAddr).String()) 27 if err := ms.Serve(ms.listener); !errors.Is(err, http.ErrServerClosed) { 28 panic("metrics service failure: " + err.Error()) 29 } 30 } 31 32 func (ms *metricsService) Stop() { 33 // context here is used to avoid infinite awaiting for all connections close 34 // we want to all connections to close, so we provide context which is not cancelled 35 if err := ms.Shutdown(context.Background()); err != nil { 36 logger.Error("metrics service shutdown failed: ", err) 37 } 38 } 39 40 func (ms *metricsService) GetPort() int { 41 return ms.listener.Addr().(*net.TCPAddr).Port 42 } 43 44 func provideHandler(metrics imetrics.IMetrics) http.HandlerFunc { 45 return func(rw http.ResponseWriter, r *http.Request) { 46 isCheck := r.URL.Path == "/metrics/check" 47 48 if (r.URL.Path != "/metrics" && !isCheck) || r.Method != http.MethodGet { 49 http.Error(rw, "404 not found", http.StatusNotFound) 50 return 51 } 52 53 if isCheck { 54 rw.WriteHeader(http.StatusOK) 55 if _, err := rw.Write([]byte("ok")); err != nil { 56 logger.Error("metrics service: failed to reply check ok: ", err) 57 } 58 return 59 } 60 61 err := metrics.List(func(metric imetrics.IMetric, metricValue float64) (err error) { 62 if _, err = rw.Write(imetrics.ToPrometheus(metric, metricValue)); err != nil { 63 return fmt.Errorf("metrics service: failed to write metric %s for app %s on VVM %s: %w", metric.Name(), metric.App(), metric.Vvm(), err) 64 } 65 return 66 }) 67 if err != nil { 68 logger.Error(err) 69 rw.WriteHeader(http.StatusInternalServerError) 70 } 71 } 72 }