Skip to content

Commit

Permalink
feat: new levels approach in metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
Iusupov Anton committed Aug 3, 2024
1 parent eeade04 commit ccbdd7c
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 53 deletions.
4 changes: 2 additions & 2 deletions metrics/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "github.com/prometheus/client_golang/prometheus"

// Registry defines the interface for managing metrics.
type Registry interface {
Inc(series Series, status string)
RecordDuration(series Series, duration float64)
Inc(string, prometheus.Labels)
RecordDuration(getMetricAndLabels func() (string, prometheus.Labels), duration float64)
PrometheusRegistry() *prometheus.Registry
}
19 changes: 10 additions & 9 deletions metrics/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ func (r *registry) sanitizeMetricName(name string) string {
return strings.ReplaceAll(name, "-", "_")
}

// Inc increments a counter for the given metric name.
func (r *registry) Inc(name string) {
// Inc increments a counter for the given Series.
func (r *registry) Inc(name string, labels prometheus.Labels) {
r.metricsMu.Lock()
defer r.metricsMu.Unlock()

Expand All @@ -49,31 +49,32 @@ func (r *registry) Inc(name string) {
Subsystem: r.Subsystem,
Namespace: r.Namespace,
Name: sanitized,
}, []string{"series_type", "name", "operation", "status"})
}, []string{"series_type", "sub_type", "operation", "status", "error_code"})
r.PromRegistry.MustRegister(counter)
r.counters[sanitized] = counter
}
counter.WithLabelValues().Inc()
counter.With(labels).Inc()
}

// RecordDuration records a duration for the given metric name and labels.
func (r *registry) RecordDuration(name string, labels []string) *prometheus.HistogramVec {
// RecordDuration records a duration for the given Series.
func (r *registry) RecordDuration(getMetricAndLabels func() (string, prometheus.Labels), duration float64) {
r.metricsMu.Lock()
defer r.metricsMu.Unlock()

sanitized := r.sanitizeMetricName(name)
metricName, labels := getMetricAndLabels()
sanitized := r.sanitizeMetricName(metricName)
histogram, exists := r.histograms[sanitized]
if !exists {
histogram = prometheus.NewHistogramVec(prometheus.HistogramOpts{
Subsystem: r.Subsystem,
Namespace: r.Namespace,
Name: sanitized,
Buckets: prometheus.DefBuckets,
}, labels)
}, []string{"series_type", "sub_type", "operation"})
r.PromRegistry.MustRegister(histogram)
r.histograms[sanitized] = histogram
}
return histogram
histogram.With(labels).Observe(duration)
}

// PrometheusRegistry returns the underlying Prometheus registry.
Expand Down
74 changes: 35 additions & 39 deletions metrics/series.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ package metrics

import (
"context"

"github.com/prometheus/client_golang/prometheus"
)

type (
SeriesType string

Series struct {
st SeriesType
name string
operation string
seriesType SeriesType
subType string
operation string
status string
}

seriesContextKey struct{}
Expand All @@ -30,14 +32,16 @@ const (
SeriesTypeDatabusConsumer SeriesType = "databus_consumer"
)

func NewSeries(st SeriesType, name string) Series {
// NewSeries creates a new Series instance with the given type and name.
func NewSeries(st SeriesType, subType string) Series {
return Series{
st: st,
name: name,
operation: "undefined",
seriesType: st,
subType: subType,
operation: "undefined",
}
}

// FromContext retrieves the Series from the context.
func FromContext(ctx context.Context) Series {
series, ok := ctx.Value(seriesContextKey{}).(Series)
if !ok {
Expand All @@ -47,73 +51,65 @@ func FromContext(ctx context.Context) Series {
return series
}

// WithOperation sets the operation name in the Series and returns an updated context.
func (s Series) WithOperation(ctx context.Context, operation string) (context.Context, Series) {
series := FromContext(ctx)

if s.st == series.st &&
s.name == series.name {
if s.seriesType == series.seriesType &&
s.subType == series.subType {
series = Series{
st: s.st,
name: s.name,
operation: series.appendOperation(operation),
seriesType: s.seriesType,
subType: s.subType,
operation: series.appendOperation(operation),
}

return series.ToContext(ctx), series
}

series = Series{
st: s.st,
name: s.name,
operation: operation,
seriesType: s.seriesType,
subType: s.subType,
operation: operation,
}

return series.ToContext(ctx), series
}

// ToContext adds the Series to the context.
func (s Series) ToContext(ctx context.Context) context.Context {
return context.WithValue(ctx, seriesContextKey{}, s)
}

func (s Series) SuccessLabels() (string, prometheus.Labels) {
return "success_count", prometheus.Labels{
"series_type": s.st.String(),
"name": s.name,
// Success returns the metric name and labels for a success event.
func (s Series) Success() (string, prometheus.Labels) {
return "operation_count", prometheus.Labels{
"series_type": s.seriesType.String(),
"sub_type": s.subType,
"operation": s.operation,
"status": "success",
}
}

func (s Series) ErrorLabels(errCode string) (string, prometheus.Labels) {
return "error_count", prometheus.Labels{
"series_type": s.st.String(),
"name": s.name,
// Error returns the metric name and labels for an error event.
func (s Series) Error(errCode string) (string, prometheus.Labels) {
return "operation_count", prometheus.Labels{
"series_type": s.seriesType.String(),
"sub_type": s.subType,
"operation": s.operation,
"status": "error",
"error_code": errCode,
}
}

func (s Series) DurationLabels() (string, prometheus.Labels) {
// Duration returns the metric name and labels for recording a duration.
func (s Series) Duration() (string, prometheus.Labels) {
return "operation_duration_seconds", prometheus.Labels{
"series_type": s.st.String(),
"name": s.name,
"series_type": s.seriesType.String(),
"sub_type": s.subType,
"operation": s.operation,
}
}

func (s Series) InfoLabels(code string) (string, prometheus.Labels) {
return "info_events", prometheus.Labels{
"series_type": s.st.String(),
"name": s.name,
"operation": s.operation,
"info_code": code,
}
}

func (s Series) Operation() string {
return s.operation
}

func (s Series) appendOperation(operation string) string {
return s.operation + "." + operation
}
10 changes: 7 additions & 3 deletions metrics/stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ import (
"github.com/prometheus/client_golang/prometheus"
)

// registryStub is a no-op implementation of the Registry interface.
type registryStub struct{}

// NewRegistryStub creates a new instance of a no-op registry.
func NewRegistryStub() Registry {
return &registryStub{}
}

func (s *registryStub) Inc(_ Series, _ string) {}
// Inc is a no-op increment method for the stub registry.
func (s *registryStub) Inc(_ string, _ prometheus.Labels) {}

func (s *registryStub) RecordDuration(_ Series, _ float64) {
}
// RecordDuration is a no-op method for recording durations in the stub registry.
func (s *registryStub) RecordDuration(_ func() (string, prometheus.Labels), _ float64) {}

// PrometheusRegistry returns nil for the stub registry.
func (s *registryStub) PrometheusRegistry() *prometheus.Registry {
return nil
}

0 comments on commit ccbdd7c

Please sign in to comment.