feat: scaffold app skeleton with config, ULID, health endpoints, compose stack (phase 1)
- internal .env loader (real env wins) + strictly validated config struct - ULID package: 48-bit ms timestamp + 80-bit crypto randomness, sortable - HTTP server with recovery/request-id/trusted-proxy/access-log middleware - /healthz, /readyz (pluggable checkers), /metricsz (counter registry) - multi-stage Dockerfile (alpine, non-root) + compose with healthchecks, app bound to 127.0.0.1:8787, mongo unpublished, graceful 15s shutdown Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
// Package metrics is a tiny in-process registry of named counters and gauges
|
||||
// exposed as JSON at /metricsz. No external metrics system is required.
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type Registry struct {
|
||||
mu sync.RWMutex
|
||||
counters map[string]*atomic.Int64
|
||||
gauges map[string]*atomic.Int64
|
||||
}
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
counters: make(map[string]*atomic.Int64),
|
||||
gauges: make(map[string]*atomic.Int64),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registry) cell(m map[string]*atomic.Int64, name string) *atomic.Int64 {
|
||||
r.mu.RLock()
|
||||
c, ok := m[name]
|
||||
r.mu.RUnlock()
|
||||
if ok {
|
||||
return c
|
||||
}
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if c, ok = m[name]; !ok {
|
||||
c = new(atomic.Int64)
|
||||
m[name] = c
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Inc adds delta to a monotonic counter.
|
||||
func (r *Registry) Inc(name string, delta int64) { r.cell(r.counters, name).Add(delta) }
|
||||
|
||||
// Set assigns the current value of a gauge (e.g. queue depth).
|
||||
func (r *Registry) Set(name string, value int64) { r.cell(r.gauges, name).Store(value) }
|
||||
|
||||
// Snapshot returns a point-in-time copy of all values for /metricsz.
|
||||
func (r *Registry) Snapshot() (counters, gauges map[string]int64) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
counters = make(map[string]int64, len(r.counters))
|
||||
for k, v := range r.counters {
|
||||
counters[k] = v.Load()
|
||||
}
|
||||
gauges = make(map[string]int64, len(r.gauges))
|
||||
for k, v := range r.gauges {
|
||||
gauges[k] = v.Load()
|
||||
}
|
||||
return counters, gauges
|
||||
}
|
||||
Reference in New Issue
Block a user