там имена типов, но не верю, что даже современный Go заставляет это делать!
Есть 2 типа CounterVec и GaugeVec, оба типа умеют WithLabelValues:
func (v *CounterVec) WithLabelValues (lvs ...string) Counter
func (v *GaugeVec) WithLabelValues (lvs ...string) Gauge
Counter и Gauge - интерфейсы, оба включают в себя интерфейс Metric.
Но при этом переменные этих типов не соответствуют такому интерфейсу:
type hasWithLabelValues interface {
WithLabelValues (lvs ...string) Metric
}
Вопрос: почему?
Я не понимаю такой логики от слова совсем :(
Я б попробовал определить Metric через дженерики как gauge|counter
сигнатура в интерфейсе и реализации должны полностью совпадать
Вы мыслите терминами наследования Включает != наследует Привыкайте к го)
потому что Counter и Metric это разные интерфейсы
Так они вложены один в другой...
Потому что го не приводит внутренние типы сам, зато может приводить тип дженерика, в ограниченом виде можно реализовать так, но не для всех случаев подойдет, в слайс несколько таких интерфейсов не положишь: type hasWithLabelValues[M Metric] interface { WithLabelValues (lvs ...string) M }
Хм... хорошо бы, но так оно не работает: «cannot use prometheus.NewCounterVec(prometheus.CounterOpts{…}, []string{…}) (value of type *prometheus.CounterVec) as hasWithLabelValues[M] value in assignment: *prometheus.CounterVec does not implement hasWithLabelValues[M] (wrong type for method WithLabelValues)» import ( "fmt" "github.com/prometheus/client_golang/prometheus" ) type hasWithLabelValues[M prometheus.Metric] interface { WithLabelValues(lvs ...string) M } func monitoringHasSpoken[M prometheus.Metric]() { var cv hasWithLabelValues[M] cv = prometheus.NewCounterVec(prometheus.CounterOpts{Name: "total_reqs"}, []string{"name"}) // ???? .(hasWithLabelValues[prometheus.Counter]) fmt.Println(cv.WithLabelValues("Adyn")) }
hasWithLabelValues[prometheus.Metric] и hasWithLabelValues[prometheus.Counter] - разные типы, которые друг к другу не приводятся, можно конечно что то подобное изобрести но непонятно зачем, это уже натуральный оверинжениринг func withLabels[M prometheus.Metric, ML hasWithLabelValues[M]](metric ML, lvs []string) M { return metric.WithLabelValues(lvs...) }
Я попытался сделать алиасы для типов в client_golang prometheus'а: type CounterVec prometheus.CounterVec type GaugeVec prometheus.GaugeVec func (v *CounterVec) WithLabelValues(lvs ...string) prometheus.Metric { return v.WithLabelValues(lvs...).(prometheus.Metric) } func (v *GaugeVec) WithLabelValues(lvs ...string) prometheus.Metric { return v.WithLabelValues(lvs...).(prometheus.Metric) } type canWithLabelValues interface { WithLabelValues(lvs ...string) prometheus.Metric } но желаемый результат так не достигается: var cv canWithLabelValues cv = prometheus.NewCounterVec( ) В итоге функции теперь отличаются примерно ничем, но сделать их одной функцией не удаётся: func (ms *MetricSet) GetCounter(metricLabels RawLabels) (metric *MetricRecord) { metricVec := ms.mvec.(*prometheus.CounterVec) .... return } func (ms *MetricSet) GetGauge(metricLabels RawLabels) (metric *MetricRecord) { metricVec := ms.mvec.(*prometheus.GaugeVec) .... return }
Таки сделал это! Но для этого пришлось рубить странноватую обвязку вокруг чужих структур: type CounterSet struct { v *prometheus.CounterVec } type GaugeSet struct { v *prometheus.GaugeVec } func (cs *CounterSet) WithLabelValues(lvs ...string) prometheus.Metric { return cs.v.WithLabelValues(lvs...).(prometheus.Metric) } func (gs *GaugeSet) WithLabelValues(lvs ...string) prometheus.Metric { return gs.v.WithLabelValues(lvs...).(prometheus.Metric) } type canWithLabelValues interface { WithLabelValues(lvs ...string) prometheus.Metric } type MetricSet struct { DefaultLabels MetricLabels mvec canWithLabelValues MetricsType MetricType Name string Metrics map[string]*MetricRecord }
Так Counter и Gauge - это разные сущности, у которых случайно наполовину совпала сигнатура одного метода
Но именно этот метод и нужен
То, что их реализовали копипастой в client_golang - как раз довольно странно: по сути это метрики, числа, и логика их для просто изменения представления наружу очень близка. Это уже в promql разница будет
А такой вариант не устроит? type Metrics[T any] interface { WithLabelValues(lvs ...string) T }
Обсуждают сегодня