mirror of
https://github.com/fosrl/gerbil.git
synced 2026-05-07 04:09:58 -05:00
154 lines
5.7 KiB
Go
154 lines
5.7 KiB
Go
package observability
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
obsotel "github.com/fosrl/gerbil/internal/observability/otel"
|
|
obsprom "github.com/fosrl/gerbil/internal/observability/prometheus"
|
|
)
|
|
|
|
// Labels is a set of key-value pairs attached to a metric observation.
|
|
// Use only stable, bounded-cardinality label values.
|
|
type Labels = map[string]string
|
|
|
|
// Counter is a monotonically increasing instrument.
|
|
type Counter interface {
|
|
Add(ctx context.Context, value int64, labels Labels)
|
|
}
|
|
|
|
// UpDownCounter is a bidirectional integer instrument (can go up or down).
|
|
type UpDownCounter interface {
|
|
Add(ctx context.Context, value int64, labels Labels)
|
|
}
|
|
|
|
// Int64Gauge records a snapshot integer value.
|
|
type Int64Gauge interface {
|
|
Record(ctx context.Context, value int64, labels Labels)
|
|
}
|
|
|
|
// Float64Gauge records a snapshot float value.
|
|
type Float64Gauge interface {
|
|
Record(ctx context.Context, value float64, labels Labels)
|
|
}
|
|
|
|
// Histogram records a distribution of values.
|
|
type Histogram interface {
|
|
Record(ctx context.Context, value float64, labels Labels)
|
|
}
|
|
|
|
// Backend is the single interface that each metrics implementation must satisfy.
|
|
// Application code must not import backend-specific packages (prometheus, otel).
|
|
type Backend interface {
|
|
// NewCounter creates a counter metric.
|
|
// labelNames declares the set of label keys that will be passed at observation time.
|
|
NewCounter(name, desc string, labelNames ...string) (Counter, error)
|
|
|
|
// NewUpDownCounter creates an up-down counter metric.
|
|
NewUpDownCounter(name, desc string, labelNames ...string) (UpDownCounter, error)
|
|
|
|
// NewInt64Gauge creates an integer gauge metric.
|
|
NewInt64Gauge(name, desc string, labelNames ...string) (Int64Gauge, error)
|
|
|
|
// NewFloat64Gauge creates a float gauge metric.
|
|
NewFloat64Gauge(name, desc string, labelNames ...string) (Float64Gauge, error)
|
|
|
|
// NewHistogram creates a histogram metric.
|
|
// buckets are the explicit upper-bound bucket boundaries.
|
|
NewHistogram(name, desc string, buckets []float64, labelNames ...string) (Histogram, error)
|
|
|
|
// HTTPHandler returns the /metrics HTTP handler.
|
|
// Implementations that do not expose an HTTP endpoint return nil.
|
|
HTTPHandler() http.Handler
|
|
|
|
// Shutdown performs a graceful flush / shutdown of the backend.
|
|
Shutdown(ctx context.Context) error
|
|
}
|
|
|
|
// New creates the backend selected by cfg and returns it.
|
|
// Exactly one backend is created; the selection is mutually exclusive.
|
|
func New(cfg MetricsConfig) (Backend, error) {
|
|
if err := cfg.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch cfg.effectiveBackend() {
|
|
case "prometheus":
|
|
b, err := obsprom.New(obsprom.Config{
|
|
Path: cfg.Prometheus.Path,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &promAdapter{b: b}, nil
|
|
case "otel":
|
|
b, err := obsotel.New(obsotel.Config{
|
|
Protocol: cfg.OTel.Protocol,
|
|
Endpoint: cfg.OTel.Endpoint,
|
|
Insecure: cfg.OTel.Insecure,
|
|
ExportInterval: cfg.OTel.ExportInterval,
|
|
Timeout: cfg.OTel.Timeout,
|
|
ServiceName: cfg.ServiceName,
|
|
ServiceVersion: cfg.ServiceVersion,
|
|
DeploymentEnvironment: cfg.DeploymentEnvironment,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &otelAdapter{b: b}, nil
|
|
case "none":
|
|
return &NoopBackend{}, nil
|
|
default:
|
|
return nil, fmt.Errorf("observability: unknown backend %q", cfg.effectiveBackend())
|
|
}
|
|
}
|
|
|
|
// promAdapter wraps obsprom.Backend to implement the observability.Backend interface.
|
|
// The concrete instrument types from the prometheus sub-package satisfy the instrument
|
|
// interfaces via Go's structural (duck) typing without importing this package.
|
|
type promAdapter struct {
|
|
b *obsprom.Backend
|
|
}
|
|
|
|
func (a *promAdapter) NewCounter(name, desc string, labelNames ...string) (Counter, error) {
|
|
return a.b.NewCounter(name, desc, labelNames...)
|
|
}
|
|
func (a *promAdapter) NewUpDownCounter(name, desc string, labelNames ...string) (UpDownCounter, error) {
|
|
return a.b.NewUpDownCounter(name, desc, labelNames...)
|
|
}
|
|
func (a *promAdapter) NewInt64Gauge(name, desc string, labelNames ...string) (Int64Gauge, error) {
|
|
return a.b.NewInt64Gauge(name, desc, labelNames...)
|
|
}
|
|
func (a *promAdapter) NewFloat64Gauge(name, desc string, labelNames ...string) (Float64Gauge, error) {
|
|
return a.b.NewFloat64Gauge(name, desc, labelNames...)
|
|
}
|
|
func (a *promAdapter) NewHistogram(name, desc string, buckets []float64, labelNames ...string) (Histogram, error) {
|
|
return a.b.NewHistogram(name, desc, buckets, labelNames...)
|
|
}
|
|
func (a *promAdapter) HTTPHandler() http.Handler { return a.b.HTTPHandler() }
|
|
func (a *promAdapter) Shutdown(ctx context.Context) error { return a.b.Shutdown(ctx) }
|
|
|
|
// otelAdapter wraps obsotel.Backend to implement the observability.Backend interface.
|
|
type otelAdapter struct {
|
|
b *obsotel.Backend
|
|
}
|
|
|
|
func (a *otelAdapter) NewCounter(name, desc string, labelNames ...string) (Counter, error) {
|
|
return a.b.NewCounter(name, desc, labelNames...)
|
|
}
|
|
func (a *otelAdapter) NewUpDownCounter(name, desc string, labelNames ...string) (UpDownCounter, error) {
|
|
return a.b.NewUpDownCounter(name, desc, labelNames...)
|
|
}
|
|
func (a *otelAdapter) NewInt64Gauge(name, desc string, labelNames ...string) (Int64Gauge, error) {
|
|
return a.b.NewInt64Gauge(name, desc, labelNames...)
|
|
}
|
|
func (a *otelAdapter) NewFloat64Gauge(name, desc string, labelNames ...string) (Float64Gauge, error) {
|
|
return a.b.NewFloat64Gauge(name, desc, labelNames...)
|
|
}
|
|
func (a *otelAdapter) NewHistogram(name, desc string, buckets []float64, labelNames ...string) (Histogram, error) {
|
|
return a.b.NewHistogram(name, desc, buckets, labelNames...)
|
|
}
|
|
func (a *otelAdapter) HTTPHandler() http.Handler { return a.b.HTTPHandler() }
|
|
func (a *otelAdapter) Shutdown(ctx context.Context) error { return a.b.Shutdown(ctx) }
|