Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New levels approach #35

Merged
merged 3 commits into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions metrics/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package metrics

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(string, prometheus.Labels, 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 @@ -18,6 +18,7 @@ type registry struct {
histograms map[string]*prometheus.HistogramVec
}

// NewRegistry creates a new metrics registry with the specified subsystem and namespace.
func NewRegistry(subsystem, namespace string) Registry {
r := &registry{
Subsystem: subsystem,
Expand All @@ -36,46 +37,46 @@ func (r *registry) sanitizeMetricName(name string) string {
return strings.ReplaceAll(name, "-", "_")
}

func (r *registry) Inc(series Series, status 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()

metricName, labels := series.SuccessLabels()
labels["status"] = status
sanitized := r.sanitizeMetricName(metricName)
sanitized := r.sanitizeMetricName(name)
counter, exists := r.counters[sanitized]
if !exists {
counter = prometheus.NewCounterVec(prometheus.CounterOpts{
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.With(labels).Inc()
}

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

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

// PrometheusRegistry returns the underlying Prometheus registry.
func (r *registry) PrometheusRegistry() *prometheus.Registry {
return r.PromRegistry
}
Expand Down
76 changes: 36 additions & 40 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
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
}
Loading