mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-01-02 01:44:25 +00:00
refactor: ♻️ ach package accepts its configuration as a parameter rather than reading from global config
Some checks failed
Build , Vet Test, and Lint / Run Vet Tests (1.24.x) (push) Successful in -25m44s
Build , Vet Test, and Lint / Run Vet Tests (1.23.x) (push) Successful in -25m19s
Build , Vet Test, and Lint / Lint Code (push) Successful in -25m18s
Build , Vet Test, and Lint / Build (push) Successful in -25m30s
Tests / Unit Tests (push) Failing after -25m51s
Tests / Integration Tests (push) Failing after -25m53s
Some checks failed
Build , Vet Test, and Lint / Run Vet Tests (1.24.x) (push) Successful in -25m44s
Build , Vet Test, and Lint / Run Vet Tests (1.23.x) (push) Successful in -25m19s
Build , Vet Test, and Lint / Lint Code (push) Successful in -25m18s
Build , Vet Test, and Lint / Build (push) Successful in -25m30s
Tests / Unit Tests (push) Failing after -25m51s
Tests / Integration Tests (push) Failing after -25m53s
This commit is contained in:
@@ -7,8 +7,8 @@ A pluggable metrics collection system with Prometheus implementation.
|
||||
```go
|
||||
import "github.com/bitechdev/ResolveSpec/pkg/metrics"
|
||||
|
||||
// Initialize Prometheus provider
|
||||
provider := metrics.NewPrometheusProvider()
|
||||
// Initialize Prometheus provider with default config
|
||||
provider := metrics.NewPrometheusProvider(nil)
|
||||
metrics.SetProvider(provider)
|
||||
|
||||
// Apply middleware to your router
|
||||
@@ -18,6 +18,41 @@ router.Use(provider.Middleware)
|
||||
http.Handle("/metrics", provider.Handler())
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
You can customize the metrics provider using a configuration struct:
|
||||
|
||||
```go
|
||||
import "github.com/bitechdev/ResolveSpec/pkg/metrics"
|
||||
|
||||
// Create custom configuration
|
||||
config := &metrics.Config{
|
||||
Enabled: true,
|
||||
Provider: "prometheus",
|
||||
Namespace: "myapp", // Prefix all metrics with "myapp_"
|
||||
HTTPRequestBuckets: []float64{0.01, 0.05, 0.1, 0.5, 1, 2, 5},
|
||||
DBQueryBuckets: []float64{0.001, 0.01, 0.05, 0.1, 0.5, 1},
|
||||
}
|
||||
|
||||
// Initialize with custom config
|
||||
provider := metrics.NewPrometheusProvider(config)
|
||||
metrics.SetProvider(provider)
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `Enabled` | `bool` | `true` | Enable/disable metrics collection |
|
||||
| `Provider` | `string` | `"prometheus"` | Metrics provider type |
|
||||
| `Namespace` | `string` | `""` | Prefix for all metric names |
|
||||
| `HTTPRequestBuckets` | `[]float64` | See below | Histogram buckets for HTTP duration (seconds) |
|
||||
| `DBQueryBuckets` | `[]float64` | See below | Histogram buckets for DB query duration (seconds) |
|
||||
|
||||
**Default HTTP Request Buckets:** `[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]`
|
||||
|
||||
**Default DB Query Buckets:** `[0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5]`
|
||||
|
||||
## Provider Interface
|
||||
|
||||
The package uses a provider interface, allowing you to plug in different metric systems:
|
||||
@@ -87,6 +122,13 @@ When using `PrometheusProvider`, the following metrics are available:
|
||||
| `cache_hits_total` | Counter | provider | Total cache hits |
|
||||
| `cache_misses_total` | Counter | provider | Total cache misses |
|
||||
| `cache_size_items` | Gauge | provider | Current cache size |
|
||||
| `events_published_total` | Counter | source, event_type | Total events published |
|
||||
| `events_processed_total` | Counter | source, event_type, status | Total events processed |
|
||||
| `event_processing_duration_seconds` | Histogram | source, event_type | Event processing duration |
|
||||
| `event_queue_size` | Gauge | - | Current event queue size |
|
||||
| `panics_total` | Counter | method | Total panics recovered |
|
||||
|
||||
**Note:** If a custom `Namespace` is configured, all metric names will be prefixed with `{namespace}_`.
|
||||
|
||||
## Prometheus Queries
|
||||
|
||||
@@ -148,6 +190,8 @@ metrics.SetProvider(&CustomProvider{})
|
||||
|
||||
## Complete Example
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
@@ -162,8 +206,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Initialize metrics
|
||||
provider := metrics.NewPrometheusProvider()
|
||||
// Initialize metrics with default config
|
||||
provider := metrics.NewPrometheusProvider(nil)
|
||||
metrics.SetProvider(provider)
|
||||
|
||||
// Create router
|
||||
@@ -198,6 +242,42 @@ func getUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
```
|
||||
|
||||
### With Custom Configuration
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/bitechdev/ResolveSpec/pkg/metrics"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Custom metrics configuration
|
||||
metricsConfig := &metrics.Config{
|
||||
Enabled: true,
|
||||
Provider: "prometheus",
|
||||
Namespace: "myapp",
|
||||
// Custom buckets optimized for your application
|
||||
HTTPRequestBuckets: []float64{0.01, 0.05, 0.1, 0.5, 1, 2, 5, 10},
|
||||
DBQueryBuckets: []float64{0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1},
|
||||
}
|
||||
|
||||
// Initialize with custom config
|
||||
provider := metrics.NewPrometheusProvider(metricsConfig)
|
||||
metrics.SetProvider(provider)
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.Use(provider.Middleware)
|
||||
router.Handle("/metrics", provider.Handler())
|
||||
|
||||
log.Fatal(http.ListenAndServe(":8080", router))
|
||||
}
|
||||
```
|
||||
|
||||
## Docker Compose Example
|
||||
|
||||
```yaml
|
||||
|
||||
46
pkg/metrics/config.go
Normal file
46
pkg/metrics/config.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package metrics
|
||||
|
||||
// Config holds configuration for the metrics provider
|
||||
type Config struct {
|
||||
// Enabled determines whether metrics collection is enabled
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
|
||||
// Provider specifies which metrics provider to use (prometheus, noop)
|
||||
Provider string `mapstructure:"provider"`
|
||||
|
||||
// Namespace is an optional prefix for all metric names
|
||||
Namespace string `mapstructure:"namespace"`
|
||||
|
||||
// HTTPRequestBuckets defines histogram buckets for HTTP request duration (in seconds)
|
||||
// Default: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
|
||||
HTTPRequestBuckets []float64 `mapstructure:"http_request_buckets"`
|
||||
|
||||
// DBQueryBuckets defines histogram buckets for database query duration (in seconds)
|
||||
// Default: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5]
|
||||
DBQueryBuckets []float64 `mapstructure:"db_query_buckets"`
|
||||
}
|
||||
|
||||
// DefaultConfig returns a Config with sensible defaults
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
Enabled: true,
|
||||
Provider: "prometheus",
|
||||
// HTTP requests typically take longer than DB queries
|
||||
HTTPRequestBuckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10},
|
||||
// DB queries are usually faster
|
||||
DBQueryBuckets: []float64{0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5},
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyDefaults fills in any missing values with defaults
|
||||
func (c *Config) ApplyDefaults() {
|
||||
if c.Provider == "" {
|
||||
c.Provider = "prometheus"
|
||||
}
|
||||
if len(c.HTTPRequestBuckets) == 0 {
|
||||
c.HTTPRequestBuckets = []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}
|
||||
}
|
||||
if len(c.DBQueryBuckets) == 0 {
|
||||
c.DBQueryBuckets = []float64{0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5}
|
||||
}
|
||||
}
|
||||
64
pkg/metrics/example_test.go
Normal file
64
pkg/metrics/example_test.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package metrics_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bitechdev/ResolveSpec/pkg/metrics"
|
||||
)
|
||||
|
||||
// ExampleNewPrometheusProvider_default demonstrates using default configuration
|
||||
func ExampleNewPrometheusProvider_default() {
|
||||
// Initialize with default configuration
|
||||
provider := metrics.NewPrometheusProvider(nil)
|
||||
metrics.SetProvider(provider)
|
||||
|
||||
fmt.Println("Provider initialized with defaults")
|
||||
// Output: Provider initialized with defaults
|
||||
}
|
||||
|
||||
// ExampleNewPrometheusProvider_custom demonstrates using custom configuration
|
||||
func ExampleNewPrometheusProvider_custom() {
|
||||
// Create custom configuration
|
||||
config := &metrics.Config{
|
||||
Enabled: true,
|
||||
Provider: "prometheus",
|
||||
Namespace: "myapp",
|
||||
HTTPRequestBuckets: []float64{0.01, 0.05, 0.1, 0.5, 1, 2, 5},
|
||||
DBQueryBuckets: []float64{0.001, 0.01, 0.05, 0.1, 0.5, 1},
|
||||
}
|
||||
|
||||
// Initialize with custom configuration
|
||||
provider := metrics.NewPrometheusProvider(config)
|
||||
metrics.SetProvider(provider)
|
||||
|
||||
fmt.Println("Provider initialized with custom config")
|
||||
// Output: Provider initialized with custom config
|
||||
}
|
||||
|
||||
// ExampleDefaultConfig demonstrates getting default configuration
|
||||
func ExampleDefaultConfig() {
|
||||
config := metrics.DefaultConfig()
|
||||
fmt.Printf("Default provider: %s\n", config.Provider)
|
||||
fmt.Printf("Default enabled: %v\n", config.Enabled)
|
||||
// Output:
|
||||
// Default provider: prometheus
|
||||
// Default enabled: true
|
||||
}
|
||||
|
||||
// ExampleConfig_ApplyDefaults demonstrates applying defaults to partial config
|
||||
func ExampleConfig_ApplyDefaults() {
|
||||
// Create partial configuration
|
||||
config := &metrics.Config{
|
||||
Namespace: "myapp",
|
||||
// Other fields will be filled with defaults
|
||||
}
|
||||
|
||||
// Apply defaults
|
||||
config.ApplyDefaults()
|
||||
|
||||
fmt.Printf("Provider: %s\n", config.Provider)
|
||||
fmt.Printf("Namespace: %s\n", config.Namespace)
|
||||
// Output:
|
||||
// Provider: prometheus
|
||||
// Namespace: myapp
|
||||
}
|
||||
@@ -20,23 +20,44 @@ type PrometheusProvider struct {
|
||||
cacheHits *prometheus.CounterVec
|
||||
cacheMisses *prometheus.CounterVec
|
||||
cacheSize *prometheus.GaugeVec
|
||||
eventPublished *prometheus.CounterVec
|
||||
eventProcessed *prometheus.CounterVec
|
||||
eventDuration *prometheus.HistogramVec
|
||||
eventQueueSize prometheus.Gauge
|
||||
panicsTotal *prometheus.CounterVec
|
||||
}
|
||||
|
||||
// NewPrometheusProvider creates a new Prometheus metrics provider
|
||||
func NewPrometheusProvider() *PrometheusProvider {
|
||||
// If cfg is nil, default configuration will be used
|
||||
func NewPrometheusProvider(cfg *Config) *PrometheusProvider {
|
||||
// Use default config if none provided
|
||||
if cfg == nil {
|
||||
cfg = DefaultConfig()
|
||||
} else {
|
||||
// Apply defaults for any missing values
|
||||
cfg.ApplyDefaults()
|
||||
}
|
||||
|
||||
// Helper to add namespace prefix if configured
|
||||
metricName := func(name string) string {
|
||||
if cfg.Namespace != "" {
|
||||
return cfg.Namespace + "_" + name
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
return &PrometheusProvider{
|
||||
requestDuration: promauto.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "http_request_duration_seconds",
|
||||
Name: metricName("http_request_duration_seconds"),
|
||||
Help: "HTTP request duration in seconds",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
Buckets: cfg.HTTPRequestBuckets,
|
||||
},
|
||||
[]string{"method", "path", "status"},
|
||||
),
|
||||
requestTotal: promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "http_requests_total",
|
||||
Name: metricName("http_requests_total"),
|
||||
Help: "Total number of HTTP requests",
|
||||
},
|
||||
[]string{"method", "path", "status"},
|
||||
@@ -44,49 +65,77 @@ func NewPrometheusProvider() *PrometheusProvider {
|
||||
|
||||
requestsInFlight: promauto.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "http_requests_in_flight",
|
||||
Name: metricName("http_requests_in_flight"),
|
||||
Help: "Current number of HTTP requests being processed",
|
||||
},
|
||||
),
|
||||
dbQueryDuration: promauto.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "db_query_duration_seconds",
|
||||
Name: metricName("db_query_duration_seconds"),
|
||||
Help: "Database query duration in seconds",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
Buckets: cfg.DBQueryBuckets,
|
||||
},
|
||||
[]string{"operation", "table"},
|
||||
),
|
||||
dbQueryTotal: promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "db_queries_total",
|
||||
Name: metricName("db_queries_total"),
|
||||
Help: "Total number of database queries",
|
||||
},
|
||||
[]string{"operation", "table", "status"},
|
||||
),
|
||||
cacheHits: promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "cache_hits_total",
|
||||
Name: metricName("cache_hits_total"),
|
||||
Help: "Total number of cache hits",
|
||||
},
|
||||
[]string{"provider"},
|
||||
),
|
||||
cacheMisses: promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "cache_misses_total",
|
||||
Name: metricName("cache_misses_total"),
|
||||
Help: "Total number of cache misses",
|
||||
},
|
||||
[]string{"provider"},
|
||||
),
|
||||
cacheSize: promauto.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "cache_size_items",
|
||||
Name: metricName("cache_size_items"),
|
||||
Help: "Number of items in cache",
|
||||
},
|
||||
[]string{"provider"},
|
||||
),
|
||||
eventPublished: promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: metricName("events_published_total"),
|
||||
Help: "Total number of events published",
|
||||
},
|
||||
[]string{"source", "event_type"},
|
||||
),
|
||||
eventProcessed: promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: metricName("events_processed_total"),
|
||||
Help: "Total number of events processed",
|
||||
},
|
||||
[]string{"source", "event_type", "status"},
|
||||
),
|
||||
eventDuration: promauto.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: metricName("event_processing_duration_seconds"),
|
||||
Help: "Event processing duration in seconds",
|
||||
Buckets: cfg.DBQueryBuckets, // Events are typically fast like DB queries
|
||||
},
|
||||
[]string{"source", "event_type"},
|
||||
),
|
||||
eventQueueSize: promauto.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Name: metricName("event_queue_size"),
|
||||
Help: "Current number of events in queue",
|
||||
},
|
||||
),
|
||||
panicsTotal: promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "panics_total",
|
||||
Name: metricName("panics_total"),
|
||||
Help: "Total number of panics",
|
||||
},
|
||||
[]string{"method"},
|
||||
@@ -153,6 +202,22 @@ func (p *PrometheusProvider) UpdateCacheSize(provider string, size int64) {
|
||||
p.cacheSize.WithLabelValues(provider).Set(float64(size))
|
||||
}
|
||||
|
||||
// RecordEventPublished implements Provider interface
|
||||
func (p *PrometheusProvider) RecordEventPublished(source, eventType string) {
|
||||
p.eventPublished.WithLabelValues(source, eventType).Inc()
|
||||
}
|
||||
|
||||
// RecordEventProcessed implements Provider interface
|
||||
func (p *PrometheusProvider) RecordEventProcessed(source, eventType, status string, duration time.Duration) {
|
||||
p.eventProcessed.WithLabelValues(source, eventType, status).Inc()
|
||||
p.eventDuration.WithLabelValues(source, eventType).Observe(duration.Seconds())
|
||||
}
|
||||
|
||||
// UpdateEventQueueSize implements Provider interface
|
||||
func (p *PrometheusProvider) UpdateEventQueueSize(size int64) {
|
||||
p.eventQueueSize.Set(float64(size))
|
||||
}
|
||||
|
||||
// RecordPanic implements the Provider interface
|
||||
func (p *PrometheusProvider) RecordPanic(methodName string) {
|
||||
p.panicsTotal.WithLabelValues(methodName).Inc()
|
||||
|
||||
Reference in New Issue
Block a user