mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
100 lines
2.7 KiB
Go
100 lines
2.7 KiB
Go
package allocator
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"sync"
|
|
)
|
|
|
|
// Metrics is a bounded allocator-role collector. Region is the only label so
|
|
// a bad request cannot create unbounded Prometheus cardinality.
|
|
type Metrics struct {
|
|
mu sync.Mutex
|
|
regions map[string]*allocationMetric
|
|
}
|
|
|
|
type allocationMetric struct {
|
|
attempts uint64
|
|
success uint64
|
|
failure uint64
|
|
denied uint64
|
|
}
|
|
|
|
func NewMetrics() *Metrics {
|
|
return &Metrics{regions: map[string]*allocationMetric{"EU": {}, "NA": {}}}
|
|
}
|
|
|
|
func (m *Metrics) ObserveAttempt(region string) {
|
|
if metric := m.metric(region); metric != nil {
|
|
m.mu.Lock()
|
|
metric.attempts++
|
|
m.mu.Unlock()
|
|
}
|
|
}
|
|
|
|
func (m *Metrics) ObserveSuccess(region string) {
|
|
if metric := m.metric(region); metric != nil {
|
|
m.mu.Lock()
|
|
metric.success++
|
|
m.mu.Unlock()
|
|
}
|
|
}
|
|
|
|
func (m *Metrics) ObserveFailure(region string) {
|
|
if metric := m.metric(region); metric != nil {
|
|
m.mu.Lock()
|
|
metric.failure++
|
|
m.mu.Unlock()
|
|
}
|
|
}
|
|
|
|
func (m *Metrics) ObserveDenied(region string) {
|
|
if metric := m.metric(region); metric != nil {
|
|
m.mu.Lock()
|
|
metric.denied++
|
|
m.mu.Unlock()
|
|
}
|
|
}
|
|
|
|
func (m *Metrics) metric(region string) *allocationMetric {
|
|
if m == nil || (region != "EU" && region != "NA") {
|
|
return nil
|
|
}
|
|
return m.regions[region]
|
|
}
|
|
|
|
func (m *Metrics) WritePrometheus(w io.Writer) error {
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if _, err := io.WriteString(w, "# TYPE cosmic_clash_allocator_allocation_attempts_total counter\n# TYPE cosmic_clash_allocator_allocations_total counter\n# TYPE cosmic_clash_allocator_allocation_failures_total counter\n# TYPE cosmic_clash_allocator_quota_denials_total counter\n"); err != nil {
|
|
return err
|
|
}
|
|
for _, region := range []string{"EU", "NA"} {
|
|
metric := m.regions[region]
|
|
labels := fmt.Sprintf(`region="%s"`, region)
|
|
if _, err := fmt.Fprintf(w, "cosmic_clash_allocator_allocation_attempts_total{%s} %d\ncosmic_clash_allocator_allocations_total{%s} %d\ncosmic_clash_allocator_allocation_failures_total{%s} %d\ncosmic_clash_allocator_quota_denials_total{%s} %d\n", labels, metric.attempts, labels, metric.success, labels, metric.failure, labels, metric.denied); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// MetricsHandler exposes only the read-only Prometheus endpoint. The caller
|
|
// owns the listener and can bind it to a private metrics network.
|
|
func MetricsHandler(metrics *Metrics) http.Handler {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "text/plain; version=0.0.4")
|
|
_ = metrics.WritePrometheus(w)
|
|
})
|
|
return mux
|
|
}
|