diff --git a/go.mod b/go.mod index 3438651..80ecfce 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/fasthttp/router v1.4.1 github.com/nspcc-dev/neo-go v0.101.0 github.com/prometheus/client_golang v1.13.0 + github.com/prometheus/client_model v0.2.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.1 @@ -68,7 +69,6 @@ require ( github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/metrics/desc.go b/metrics/desc.go index d904f8a..65b47d2 100644 --- a/metrics/desc.go +++ b/metrics/desc.go @@ -4,17 +4,20 @@ import ( "encoding/json" "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" ) var appMetricsDesc = map[string]map[string]Description{ poolSubsystem: { overallErrorsMetric: Description{ + Type: dto.MetricType_GAUGE, Namespace: namespace, Subsystem: poolSubsystem, Name: overallErrorsMetric, Help: "Total number of errors in pool", }, overallNodeErrorsMetric: Description{ + Type: dto.MetricType_GAUGE, Namespace: namespace, Subsystem: poolSubsystem, Name: overallNodeErrorsMetric, @@ -22,6 +25,7 @@ var appMetricsDesc = map[string]map[string]Description{ VariableLabels: []string{"node"}, }, overallNodeRequestsMetric: Description{ + Type: dto.MetricType_GAUGE, Namespace: namespace, Subsystem: poolSubsystem, Name: overallNodeRequestsMetric, @@ -29,6 +33,7 @@ var appMetricsDesc = map[string]map[string]Description{ VariableLabels: []string{"node"}, }, currentErrorMetric: Description{ + Type: dto.MetricType_GAUGE, Namespace: namespace, Subsystem: poolSubsystem, Name: currentErrorMetric, @@ -36,6 +41,7 @@ var appMetricsDesc = map[string]map[string]Description{ VariableLabels: []string{"node"}, }, avgRequestDurationMetric: Description{ + Type: dto.MetricType_GAUGE, Namespace: namespace, Subsystem: poolSubsystem, Name: avgRequestDurationMetric, @@ -45,12 +51,14 @@ var appMetricsDesc = map[string]map[string]Description{ }, stateSubsystem: { healthMetric: Description{ + Type: dto.MetricType_GAUGE, Namespace: namespace, Subsystem: stateSubsystem, Name: healthMetric, Help: "Current HTTP gateway state", }, versionInfoMetric: Description{ + Type: dto.MetricType_GAUGE, Namespace: namespace, Subsystem: stateSubsystem, Name: versionInfoMetric, @@ -61,6 +69,7 @@ var appMetricsDesc = map[string]map[string]Description{ } type Description struct { + Type dto.MetricType Namespace string Subsystem string Name string @@ -76,11 +85,13 @@ type KeyValue struct { func (d *Description) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { + Type string `json:"type"` FQName string `json:"name"` Help string `json:"help"` ConstantLabels []KeyValue `json:"constant_labels"` VariableLabels []string `json:"variable_labels"` }{ + Type: d.Type.String(), FQName: d.BuildFQName(), Help: d.Help, ConstantLabels: d.ConstantLabels, @@ -122,13 +133,19 @@ func newOpts(description Description) prometheus.Opts { } } -func newGauge(description Description) prometheus.Gauge { +func mustNewGauge(description Description) prometheus.Gauge { + if description.Type != dto.MetricType_GAUGE { + panic("invalid metric type") + } return prometheus.NewGauge( prometheus.GaugeOpts(newOpts(description)), ) } -func newGaugeVec(description Description) *prometheus.GaugeVec { +func mustNewGaugeVec(description Description) *prometheus.GaugeVec { + if description.Type != dto.MetricType_GAUGE { + panic("invalid metric type") + } return prometheus.NewGaugeVec( prometheus.GaugeOpts(newOpts(description)), description.VariableLabels, diff --git a/metrics/desc_test.go b/metrics/desc_test.go index 3c2d06b..b3e98ae 100644 --- a/metrics/desc_test.go +++ b/metrics/desc_test.go @@ -8,17 +8,28 @@ import ( "os" "testing" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool" "github.com/stretchr/testify/require" ) +type mock struct{} + +func (m mock) Statistic() pool.Statistic { + return pool.Statistic{} +} + var metricsPath = flag.String("out", "", "File to export http gateway metrics to.") func TestDescribeAll(t *testing.T) { + // to check correct metrics type mapping + _ = NewGateMetrics(mock{}) + flag.Parse() require.NotEmpty(t, metricsPath, "flag 'out' must be provided to dump metrics description") - data, err := json.Marshal(DescribeAll()) + desc := DescribeAll() + data, err := json.Marshal(desc) require.NoError(t, err) err = os.WriteFile(*metricsPath, data, 0644) diff --git a/metrics/metrics.go b/metrics/metrics.go index e2b33da..cf22099 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -56,6 +56,10 @@ const ( HealthStatusShuttingDown HealthStatus = 3 ) +type StatisticScraper interface { + Statistic() pool.Statistic +} + type GateMetrics struct { stateMetrics poolMetricsCollector @@ -67,7 +71,7 @@ type stateMetrics struct { } type poolMetricsCollector struct { - pool *pool.Pool + scraper StatisticScraper overallErrors prometheus.Gauge overallNodeErrors *prometheus.GaugeVec overallNodeRequests *prometheus.GaugeVec @@ -76,7 +80,7 @@ type poolMetricsCollector struct { } // NewGateMetrics creates new metrics for http gate. -func NewGateMetrics(p *pool.Pool) *GateMetrics { +func NewGateMetrics(p StatisticScraper) *GateMetrics { stateMetric := newStateMetrics() stateMetric.register() @@ -96,8 +100,8 @@ func (g *GateMetrics) Unregister() { func newStateMetrics() *stateMetrics { return &stateMetrics{ - healthCheck: newGauge(appMetricsDesc[stateSubsystem][healthMetric]), - versionInfo: newGaugeVec(appMetricsDesc[stateSubsystem][versionInfoMetric]), + healthCheck: mustNewGauge(appMetricsDesc[stateSubsystem][healthMetric]), + versionInfo: mustNewGaugeVec(appMetricsDesc[stateSubsystem][versionInfoMetric]), } } @@ -119,14 +123,14 @@ func (m stateMetrics) SetVersion(ver string) { m.versionInfo.WithLabelValues(ver).Set(1) } -func newPoolMetricsCollector(p *pool.Pool) *poolMetricsCollector { +func newPoolMetricsCollector(p StatisticScraper) *poolMetricsCollector { return &poolMetricsCollector{ - pool: p, - overallErrors: newGauge(appMetricsDesc[poolSubsystem][overallErrorsMetric]), - overallNodeErrors: newGaugeVec(appMetricsDesc[poolSubsystem][overallNodeErrorsMetric]), - overallNodeRequests: newGaugeVec(appMetricsDesc[poolSubsystem][overallNodeRequestsMetric]), - currentErrors: newGaugeVec(appMetricsDesc[poolSubsystem][currentErrorMetric]), - requestDuration: newGaugeVec(appMetricsDesc[poolSubsystem][avgRequestDurationMetric]), + scraper: p, + overallErrors: mustNewGauge(appMetricsDesc[poolSubsystem][overallErrorsMetric]), + overallNodeErrors: mustNewGaugeVec(appMetricsDesc[poolSubsystem][overallNodeErrorsMetric]), + overallNodeRequests: mustNewGaugeVec(appMetricsDesc[poolSubsystem][overallNodeRequestsMetric]), + currentErrors: mustNewGaugeVec(appMetricsDesc[poolSubsystem][currentErrorMetric]), + requestDuration: mustNewGaugeVec(appMetricsDesc[poolSubsystem][avgRequestDurationMetric]), } } @@ -152,7 +156,7 @@ func (m *poolMetricsCollector) register() { } func (m *poolMetricsCollector) updateStatistic() { - stat := m.pool.Statistic() + stat := m.scraper.Statistic() m.overallNodeErrors.Reset() m.overallNodeRequests.Reset()