Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/platform/obs/obs.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | internal/platform/obs/obs.go |
| Original SHA-256 | 2b193698a2a3a0dddd71ee72a30d3620bba32e8b15d38f074fa7903195d34a96 |
| Git blob SHA | 471d2d9ddee11c4d145ce10ab372ff9aa4efff79 |
| Reading-copy SHA-256 | 2b193698a2a3a0dddd71ee72a30d3620bba32e8b15d38f074fa7903195d34a96 |
Every recorded occurrence
These 35 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| Janady13/missionmatch / internal/platform/obs/obs.go | feature/opportunity-to-outcome | 720d47fae476b0d38477ae4401697d372b1ea2bc | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | per-tenant-routing | 9bde0f9a74b89d01980fa658e5253acda7528d0c | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | collision-deconfliction | 4ee5a09972608ed89f8377c6665542d5f450f0a6 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | ws6-retrieval | 6c56e29231ec05735927fa265c97d517499dfeaf | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | workstream-a-shared-core | e0de03ac3a1ec826bd15d0e048e3b02baa23eeff | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | m5-price-to-win | 41000ad30fff4129fdaf5ec84e15ba14c27d4890 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | workstream-c-slatematch | 5cd60c918301b46ecd6ed3e03e20c395da0062e2 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | ws1-ontology-sync | b596f449a32a1016fbd403c36505cc00a1ac9ee6 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | master | 1e9249840a4302ec82e123b9c9cb6f75badea3e4 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | ws8-teaching-loop | c823f0fb6bb2aba0f39001eb6de96f4000d9321f | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | w3-ai-staffing-recommendations | 43d56387270ef4da4cabd794d97ca6a49909bc7d | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | mag-substrate | b82c7100a492dec67dfd1eda6b3f53def859be35 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | ws3-clearance | 4b8e7ce594c068621867f9c9deed0ec64ae6c553 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | workstream-d-command-center | 255e7d1d420abf5e277d0cd8c9b14f6f72b6acb9 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | phase2-eval-fairness | d361af254701975fe239927656ababb56374655e | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | workstream-e-dataflow-deploy | 433815a042f9741411e60fa553fe72143e9b10db | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | ruthless-debug-sweep | b7d742f8f032984be3817efffa3eb8b13b651c1f | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | time-to-fill-survival | b66e1e505863d4f17741a4b9ede73509e45bd6c6 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | ai-intelligence-layer | 5d5ddff70393a197c5f54a45280bb5dd42b24755 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | graphrag-foundation | b0ddeb9f633fd138d09309d1a17b7d13bc3a67f7 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | workstream-a-opportunity-intelligence | 1675031db74316e100782d063e9eda1a597fe3a1 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | deploy-and-ops | bdacd9d0b4d5998d25fc74bf4007f4c3ec538c7a | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | ws9-bench-ingestion | f585a9ef32fa554663ce1fba9a414a54b6e49cf0 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | ws2-alias-relationship | e4dada4601c6eb101830f9d223a9655dfa120616 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | presentation-readiness | eb574acaa29d71ce4d0f852572e87c275c79fdb7 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | vendor-intelligence | fe9796a19c261951d3f2565206c0c1b5ac294465 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | workstream-b-screenmatch | 09015496d565a172bcaa15cfe9a8e4b072b13df0 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | ws7-federal-signal | 6a9f6ce9cfb13345ffb47770d4960e7c2d9c17c2 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | workstream-e-commercial-stubs | 4437da660539284a400ee380b9b427a300c369ee | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | ai-brain-live | aac32fc49c878cc5bca099459307611562d0bac1 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | workstream-d-missionmatch | 3be46ba89edb737182075a2b320509b42dc916f8 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | phase2-connectors | e038d4c687aec16f8bd9bc4d907eea14d91f8704 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | workstream-b-sam-contract-awards | d5101cceb98e70000cebb31d3e7ac73d747d3a6d | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | feature/aioe-upgrade | 2178e7298d0e5d9af83afa12402fd587f0ba6ed8 | Open original |
| Janady13/missionmatch / internal/platform/obs/obs.go | capture-readiness | bae6af7cb2997e819174a9e9cfff5e16067a1975 | Open original |
Recovered source transcript
Download complete source text ↓194 lines · 6.6 KB
// Package obs is the dependency free observability layer: structured JSON logging, an access log plus
// metrics middleware, and a minimal Prometheus text /metrics endpoint. It deliberately pulls no metrics
// library so the FIPS binary stays lean; a richer stack can be added at deploy time behind the same
// endpoint. It records the chi request id on every line so a log entry and a metric ties back to one
// request, and it NEVER logs a query string, a header, or a body, so no secret or PII reaches the logs.
package obs
import (
"fmt"
"log/slog"
"net/http"
"os"
"sort"
"strconv"
"sync"
"sync/atomic"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
// Setup installs a JSON slog logger as the process default at the given level (info unless debug is
// set). Returns the logger so the caller can also use it directly.
func Setup(debug bool) *slog.Logger {
level := slog.LevelInfo
if debug {
level = slog.LevelDebug
}
h := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: level})
l := slog.New(h)
slog.SetDefault(l)
return l
}
// histogram buckets in seconds, the conventional Prometheus default ladder.
var durBuckets = []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}
// Registry holds the in process HTTP metrics. All counters are atomic so the hot path is lock free.
type Registry struct {
total [6]atomic.Int64 // index 0 = other, 1..5 = 1xx..5xx
bucketHits []atomic.Int64 // cumulative le buckets, plus one overflow slot
durSum atomic.Uint64 // sum of durations in microseconds (integer, avoids float races)
durCount atomic.Int64
inFlight atomic.Int64
// poolStats, when set, contributes gauge lines (for example pgxpool acquired/idle/total conns).
poolStats func() map[string]float64
poolMu sync.RWMutex
}
// NewRegistry builds a metrics registry.
func NewRegistry() *Registry {
return &Registry{bucketHits: make([]atomic.Int64, len(durBuckets)+1)}
}
// SetPoolStats registers a callback that returns named gauge values (for example database pool stats).
func (r *Registry) SetPoolStats(f func() map[string]float64) {
r.poolMu.Lock()
r.poolStats = f
r.poolMu.Unlock()
}
func (r *Registry) observe(status int, d time.Duration) {
class := status / 100
if class < 1 || class > 5 {
class = 0
}
r.total[class].Add(1)
secs := d.Seconds()
for i, b := range durBuckets {
if secs <= b {
r.bucketHits[i].Add(1)
}
}
r.bucketHits[len(durBuckets)].Add(1) // +Inf
r.durSum.Add(uint64(d.Microseconds()))
r.durCount.Add(1)
}
// statusRecorder captures the response status and byte count without buffering the body.
type statusRecorder struct {
http.ResponseWriter
status int
bytes int
}
func (s *statusRecorder) WriteHeader(code int) {
s.status = code
s.ResponseWriter.WriteHeader(code)
}
func (s *statusRecorder) Write(b []byte) (int, error) {
if s.status == 0 {
s.status = http.StatusOK
}
n, err := s.ResponseWriter.Write(b)
s.bytes += n
return n, err
}
// Middleware times each request, records the metrics, and emits one structured access log line. It logs
// the method, the route PATTERN (never the raw path or query string, so ids and secrets stay out of the
// logs), the status, the latency, the bytes, and the chi request id. A 5xx logs at error level.
func (r *Registry) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
r.inFlight.Add(1)
defer r.inFlight.Add(-1)
start := time.Now()
rec := &statusRecorder{ResponseWriter: w}
next.ServeHTTP(rec, req)
dur := time.Since(start)
if rec.status == 0 {
rec.status = http.StatusOK
}
r.observe(rec.status, dur)
pattern := routePattern(req)
attrs := []any{
slog.String("request_id", middleware.GetReqID(req.Context())),
slog.String("method", req.Method),
slog.String("route", pattern),
slog.Int("status", rec.status),
slog.Int64("latency_ms", dur.Milliseconds()),
slog.Int("bytes", rec.bytes),
}
switch {
case rec.status >= 500:
slog.Error("request", attrs...)
case rec.status >= 400:
slog.Warn("request", attrs...)
default:
slog.Info("request", attrs...)
}
})
}
// routePattern returns the matched chi route pattern (for example /api/candidates/{candidateID}), so the
// access log and the metrics never carry the concrete id segment. chi populates the RouteContext during
// matching, so by the time the wrapped handler returns the pattern is set. Falls back to a constant when
// the route did not match, to avoid an unbounded label set on 404s.
func routePattern(req *http.Request) string {
if rc := chi.RouteContext(req.Context()); rc != nil {
if p := rc.RoutePattern(); p != "" {
return p
}
}
return "unmatched"
}
// Handler writes the metrics in Prometheus text exposition format.
func (r *Registry) Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/plain; version=0.0.4")
bw := w
fmt.Fprintln(bw, "# HELP mm_http_requests_total Total HTTP requests by status class.")
fmt.Fprintln(bw, "# TYPE mm_http_requests_total counter")
classes := map[int]string{1: "1xx", 2: "2xx", 3: "3xx", 4: "4xx", 5: "5xx", 0: "other"}
for class, label := range classes {
fmt.Fprintf(bw, "mm_http_requests_total{class=%q} %d\n", label, r.total[class].Load())
}
fmt.Fprintln(bw, "# HELP mm_http_request_duration_seconds HTTP request latency.")
fmt.Fprintln(bw, "# TYPE mm_http_request_duration_seconds histogram")
for i, b := range durBuckets {
fmt.Fprintf(bw, "mm_http_request_duration_seconds_bucket{le=%q} %d\n", strconv.FormatFloat(b, 'g', -1, 64), r.bucketHits[i].Load())
}
fmt.Fprintf(bw, "mm_http_request_duration_seconds_bucket{le=\"+Inf\"} %d\n", r.bucketHits[len(durBuckets)].Load())
fmt.Fprintf(bw, "mm_http_request_duration_seconds_sum %s\n", strconv.FormatFloat(float64(r.durSum.Load())/1e6, 'g', -1, 64))
fmt.Fprintf(bw, "mm_http_request_duration_seconds_count %d\n", r.durCount.Load())
fmt.Fprintln(bw, "# HELP mm_http_in_flight_requests In flight HTTP requests.")
fmt.Fprintln(bw, "# TYPE mm_http_in_flight_requests gauge")
fmt.Fprintf(bw, "mm_http_in_flight_requests %d\n", r.inFlight.Load())
r.poolMu.RLock()
f := r.poolStats
r.poolMu.RUnlock()
if f != nil {
stats := f()
names := make([]string, 0, len(stats))
for k := range stats {
names = append(names, k)
}
sort.Strings(names)
fmt.Fprintln(bw, "# HELP mm_db_pool Database pool gauges.")
fmt.Fprintln(bw, "# TYPE mm_db_pool gauge")
for _, name := range names {
fmt.Fprintf(bw, "mm_db_pool{stat=%q} %s\n", name, strconv.FormatFloat(stats[name], 'g', -1, 64))
}
}
})
}