-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Vyom Yadav <jackhammervyom@gmail.com>
- Loading branch information
1 parent
1bc9af5
commit eab51a3
Showing
5 changed files
with
260 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package metrics | ||
Check failure on line 1 in internal/reminder/metrics/metrics.go GitHub Actions / lint / Run golangci-lint
|
||
|
||
import ( | ||
"context" | ||
"go.opentelemetry.io/otel/metric" | ||
) | ||
|
||
// Default bucket boundaries in seconds for the send delay histogram | ||
var sendDelayBuckets = []float64{ | ||
0, // immediate | ||
10, // 10 seconds | ||
20, // 20 seconds | ||
40, // 40 seconds | ||
80, // 1m 20s | ||
160, // 2m 40s | ||
320, // 5m 20s | ||
640, // 10m 40s | ||
1280, // 21m 20s | ||
} | ||
|
||
type Metrics struct { | ||
Check failure on line 21 in internal/reminder/metrics/metrics.go GitHub Actions / lint / Run golangci-lint
|
||
// Time between when a reminder became eligible and when it was sent | ||
SendDelay metric.Float64Histogram | ||
|
||
// Current number of reminders in the batch | ||
BatchSize metric.Int64Gauge | ||
|
||
// Average batch size (updated on each batch) | ||
AvgBatchSize metric.Float64Gauge | ||
|
||
// For tracking average calculation | ||
// TODO: consider persisting this to avoid reset on restart (maybe) | ||
totalBatches int64 | ||
totalReminders int64 | ||
} | ||
|
||
func NewMetrics(meter metric.Meter) (*Metrics, error) { | ||
Check failure on line 37 in internal/reminder/metrics/metrics.go GitHub Actions / lint / Run golangci-lint
|
||
sendDelay, err := meter.Float64Histogram( | ||
"reminder_send_delay", | ||
metric.WithDescription("Time between reminder becoming eligible and actual send (seconds)"), | ||
metric.WithUnit("s"), | ||
metric.WithExplicitBucketBoundaries(sendDelayBuckets...), | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
batchSize, err := meter.Int64Gauge( | ||
"reminder_batch_size", | ||
metric.WithDescription("Current number of reminders in the batch"), | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
avgBatchSize, err := meter.Float64Gauge( | ||
"reminder_avg_batch_size", | ||
metric.WithDescription("Average number of reminders per batch"), | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &Metrics{ | ||
SendDelay: sendDelay, | ||
BatchSize: batchSize, | ||
AvgBatchSize: avgBatchSize, | ||
}, nil | ||
} | ||
|
||
func (m *Metrics) RecordBatch(ctx context.Context, size int64) { | ||
Check failure on line 71 in internal/reminder/metrics/metrics.go GitHub Actions / lint / Run golangci-lint
|
||
// Update current batch size | ||
m.BatchSize.Record(ctx, size) | ||
|
||
// Update running average | ||
m.totalBatches++ | ||
m.totalReminders += size | ||
avgSize := float64(m.totalReminders) / float64(m.totalBatches) | ||
m.AvgBatchSize.Record(ctx, avgSize) | ||
} | ||
|
||
func (m *Metrics) RecordSendDelay(ctx context.Context, delaySeconds float64) { | ||
Check failure on line 82 in internal/reminder/metrics/metrics.go GitHub Actions / lint / Run golangci-lint
|
||
m.SendDelay.Record(ctx, delaySeconds) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package metrics | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"github.com/mindersec/minder/pkg/config/reminder" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/prometheus/client_golang/prometheus/promhttp" | ||
"github.com/rs/zerolog/log" | ||
"go.opentelemetry.io/otel" | ||
"go.opentelemetry.io/otel/exporters/prometheus" | ||
sdkmetric "go.opentelemetry.io/otel/sdk/metric" | ||
"go.opentelemetry.io/otel/sdk/resource" | ||
semconv "go.opentelemetry.io/otel/semconv/v1.17.0" | ||
) | ||
|
||
const ( | ||
metricsPath = "/metrics" | ||
readHeaderTimeout = 2 * time.Second | ||
) | ||
|
||
// Provider manages the metrics server and OpenTelemetry setup | ||
type Provider struct { | ||
server *http.Server | ||
mp *sdkmetric.MeterProvider | ||
metrics *Metrics | ||
} | ||
|
||
// NewProvider creates a new metrics provider | ||
func NewProvider(cfg *reminder.MetricsConfig) (*Provider, error) { | ||
if cfg == nil { | ||
return nil, errors.New("metrics config is nil") | ||
} | ||
|
||
if !cfg.Enabled { | ||
return &Provider{}, nil | ||
} | ||
|
||
// Create Prometheus exporter | ||
prometheusExporter, err := prometheus.New( | ||
prometheus.WithNamespace("reminder_service"), | ||
) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create Prometheus exporter: %w", err) | ||
} | ||
|
||
// Create resource with service information | ||
res := resource.NewWithAttributes( | ||
semconv.SchemaURL, | ||
semconv.ServiceName("reminder-service"), | ||
semconv.ServiceVersion("v0.1.0"), | ||
) | ||
|
||
// Create meter provider | ||
mp := sdkmetric.NewMeterProvider( | ||
sdkmetric.WithReader(prometheusExporter), | ||
sdkmetric.WithResource(res), | ||
) | ||
|
||
// Set global meter provider | ||
otel.SetMeterProvider(mp) | ||
|
||
// Create metrics | ||
meter := mp.Meter("reminder-service") | ||
metrics, err := NewMetrics(meter) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create metrics: %w", err) | ||
} | ||
|
||
// Create HTTP server | ||
mux := http.NewServeMux() | ||
mux.Handle(metricsPath, promhttp.Handler()) | ||
|
||
server := &http.Server{ | ||
Addr: fmt.Sprintf("%s:%d", cfg.Host, cfg.Port), | ||
Handler: mux, | ||
ReadHeaderTimeout: readHeaderTimeout, | ||
} | ||
|
||
return &Provider{ | ||
server: server, | ||
mp: mp, | ||
metrics: metrics, | ||
}, nil | ||
} | ||
|
||
// Start starts the metrics server if enabled | ||
func (p *Provider) Start(ctx context.Context) error { | ||
if p.server == nil { | ||
return nil // Metrics disabled | ||
} | ||
|
||
errCh := make(chan error) | ||
go func() { | ||
log.Info().Str("address", p.server.Addr).Msg("Starting metrics server") | ||
if err := p.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { | ||
errCh <- fmt.Errorf("metrics server error: %w", err) | ||
} | ||
}() | ||
|
||
select { | ||
case err := <-errCh: | ||
return err | ||
case <-ctx.Done(): | ||
return p.Shutdown(ctx) | ||
} | ||
} | ||
|
||
// Shutdown gracefully shuts down the metrics server | ||
func (p *Provider) Shutdown(ctx context.Context) error { | ||
if p.server == nil { | ||
return nil | ||
} | ||
|
||
shutdownCtx, cancel := context.WithTimeout(ctx, 5*time.Second) | ||
defer cancel() | ||
|
||
log.Info().Msg("Shutting down metrics server") | ||
if err := p.mp.Shutdown(shutdownCtx); err != nil { | ||
log.Error().Err(err).Msg("Error shutting down meter provider") | ||
} | ||
|
||
return p.server.Shutdown(shutdownCtx) | ||
} | ||
|
||
// Metrics returns the metrics instance | ||
func (p *Provider) Metrics() *Metrics { | ||
return p.metrics | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package reminder | ||
|
||
type MetricsConfig struct { | ||
Check failure on line 3 in pkg/config/reminder/metrics.go GitHub Actions / lint / Run golangci-lint
|
||
Enabled bool `mapstructure:"enabled" default:"true"` | ||
Host string `mapstructure:"host" default:"127.0.0.1"` | ||
Port int `mapstructure:"port" default:"8080"` | ||
} |