feat(packages): Prometheus publish events

This commit is contained in:
Hein
2026-01-02 13:48:31 +02:00
parent c2fcc5aaff
commit 443a672fcb
5 changed files with 288 additions and 19 deletions

View File

@@ -53,6 +53,24 @@ metrics.SetProvider(provider)
**Default DB Query Buckets:** `[0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5]`
### Pushgateway Configuration (Optional)
For batch jobs, cron tasks, or short-lived processes, you can push metrics to Prometheus Pushgateway:
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `PushgatewayURL` | `string` | `""` | URL of Pushgateway (e.g., "http://pushgateway:9091") |
| `PushgatewayJobName` | `string` | `"resolvespec"` | Job name for pushed metrics |
| `PushgatewayInterval` | `int` | `0` | Auto-push interval in seconds (0 = disabled) |
```go
config := &metrics.Config{
PushgatewayURL: "http://pushgateway:9091",
PushgatewayJobName: "batch-job",
PushgatewayInterval: 30, // Push every 30 seconds
}
```
## Provider Interface
The package uses a provider interface, allowing you to plug in different metric systems:
@@ -188,6 +206,122 @@ func (c *CustomProvider) Handler() http.Handler {
metrics.SetProvider(&CustomProvider{})
```
## Pushgateway Usage
### Automatic Push (Batch Jobs)
For jobs that run periodically, use automatic pushing:
```go
package main
import (
"time"
"github.com/bitechdev/ResolveSpec/pkg/metrics"
)
func main() {
// Configure with automatic pushing every 30 seconds
config := &metrics.Config{
Enabled: true,
Provider: "prometheus",
Namespace: "batch_job",
PushgatewayURL: "http://pushgateway:9091",
PushgatewayJobName: "data-processor",
PushgatewayInterval: 30, // Push every 30 seconds
}
provider := metrics.NewPrometheusProvider(config)
metrics.SetProvider(provider)
// Ensure cleanup on exit
defer provider.StopAutoPush()
// Your batch job logic here
processBatchData()
}
```
### Manual Push (Short-lived Processes)
For one-time jobs or when you want manual control:
```go
package main
import (
"log"
"github.com/bitechdev/ResolveSpec/pkg/metrics"
)
func main() {
// Configure without automatic pushing
config := &metrics.Config{
Enabled: true,
Provider: "prometheus",
PushgatewayURL: "http://pushgateway:9091",
PushgatewayJobName: "migration-job",
// PushgatewayInterval: 0 (default - no auto-push)
}
provider := metrics.NewPrometheusProvider(config)
metrics.SetProvider(provider)
// Run your job
err := runMigration()
// Push metrics at the end
if pushErr := provider.Push(); pushErr != nil {
log.Printf("Failed to push metrics: %v", pushErr)
}
if err != nil {
log.Fatal(err)
}
}
```
### Docker Compose with Pushgateway
```yaml
version: '3'
services:
batch-job:
build: .
environment:
PUSHGATEWAY_URL: "http://pushgateway:9091"
pushgateway:
image: prom/pushgateway
ports:
- "9091:9091"
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
```
**prometheus.yml for Pushgateway:**
```yaml
global:
scrape_interval: 15s
scrape_configs:
# Scrape the pushgateway
- job_name: 'pushgateway'
honor_labels: true # Important: preserve job labels from pushed metrics
static_configs:
- targets: ['pushgateway:9091']
```
## Complete Example
### Basic Usage
@@ -337,3 +471,8 @@ scrape_configs:
4. **Performance**: Metrics collection is lock-free and highly performant
- Safe for high-throughput applications
- Minimal overhead (<1% in most cases)
5. **Pull vs Push**:
- **Use Pull (default)**: Long-running services, web servers, microservices
- **Use Push (Pushgateway)**: Batch jobs, cron tasks, short-lived processes, serverless functions
- Pull is preferred for most applications as it allows Prometheus to detect if your service is down

View File

@@ -18,6 +18,20 @@ type Config struct {
// 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"`
// PushgatewayURL is the URL of the Prometheus Pushgateway (optional)
// If set, metrics will be pushed to this gateway instead of only being scraped
// Example: "http://pushgateway:9091"
PushgatewayURL string `mapstructure:"pushgateway_url"`
// PushgatewayJobName is the job name to use when pushing metrics to Pushgateway
// Default: "resolvespec"
PushgatewayJobName string `mapstructure:"pushgateway_job_name"`
// PushgatewayInterval is the interval at which to push metrics to Pushgateway
// Only used if PushgatewayURL is set. If 0, automatic pushing is disabled.
// Default: 0 (no automatic pushing)
PushgatewayInterval int `mapstructure:"pushgateway_interval"`
}
// DefaultConfig returns a Config with sensible defaults
@@ -43,4 +57,8 @@ func (c *Config) ApplyDefaults() {
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}
}
// Set default job name if pushgateway is configured but job name is empty
if c.PushgatewayURL != "" && c.PushgatewayJobName == "" {
c.PushgatewayJobName = "resolvespec"
}
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/client_golang/prometheus/push"
)
// PrometheusProvider implements the Provider interface using Prometheus
@@ -25,6 +26,13 @@ type PrometheusProvider struct {
eventDuration *prometheus.HistogramVec
eventQueueSize prometheus.Gauge
panicsTotal *prometheus.CounterVec
// Pushgateway fields (optional)
pushgatewayURL string
pushgatewayJobName string
pusher *push.Pusher
pushTicker *time.Ticker
pushStop chan bool
}
// NewPrometheusProvider creates a new Prometheus metrics provider
@@ -46,7 +54,7 @@ func NewPrometheusProvider(cfg *Config) *PrometheusProvider {
return name
}
return &PrometheusProvider{
p := &PrometheusProvider{
requestDuration: promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: metricName("http_request_duration_seconds"),
@@ -140,7 +148,25 @@ func NewPrometheusProvider(cfg *Config) *PrometheusProvider {
},
[]string{"method"},
),
pushgatewayURL: cfg.PushgatewayURL,
pushgatewayJobName: cfg.PushgatewayJobName,
}
// Initialize pushgateway if configured
if cfg.PushgatewayURL != "" {
p.pusher = push.New(cfg.PushgatewayURL, cfg.PushgatewayJobName).
Gatherer(prometheus.DefaultGatherer)
// Start automatic pushing if interval is configured
if cfg.PushgatewayInterval > 0 {
p.pushStop = make(chan bool)
p.pushTicker = time.NewTicker(time.Duration(cfg.PushgatewayInterval) * time.Second)
go p.startAutoPush()
}
}
return p
}
// ResponseWriter wraps http.ResponseWriter to capture status code
@@ -250,3 +276,37 @@ func (p *PrometheusProvider) Middleware(next http.Handler) http.Handler {
p.RecordHTTPRequest(r.Method, r.URL.Path, status, duration)
})
}
// Push manually pushes metrics to the configured Pushgateway
// Returns an error if pushing fails or if Pushgateway is not configured
func (p *PrometheusProvider) Push() error {
if p.pusher == nil {
return nil // Pushgateway not configured, silently skip
}
return p.pusher.Push()
}
// startAutoPush runs in a goroutine and periodically pushes metrics to Pushgateway
func (p *PrometheusProvider) startAutoPush() {
for {
select {
case <-p.pushTicker.C:
if err := p.Push(); err != nil {
// Log error but continue pushing
// Note: In production, you might want to use a proper logger
_ = err
}
case <-p.pushStop:
p.pushTicker.Stop()
return
}
}
}
// StopAutoPush stops the automatic push goroutine
// This should be called when shutting down the application
func (p *PrometheusProvider) StopAutoPush() {
if p.pushStop != nil {
close(p.pushStop)
}
}