- Add internal/logging package with zap-based structured JSON logging - Support development (colored) and production (JSON) modes via ENVIRONMENT env var - Add logging helpers: Info(), Error(), Warn(), Debug(), Fatal() - Add field helpers: String(), Int(), Int64(), Err() - Add internal/metrics package with 16 comprehensive Prometheus metrics - Track workflows: starts, completions, duration by type/status - Track activities: starts, completions, duration, retries by type - Track LLM calls: total calls and latency by model - Track git operations: total and duration by operation type - Track judge decisions: decisions by type - Track Temporal errors: connection errors by type - Track cache efficiency: hits and misses by cache type - Track tasks in progress: gauge metric by task type - Metrics exported on /metrics endpoint (Prometheus text format) - Integrate structured logging in cmd/worker and cmd/starter - Replace all log.Printf/log.Fatalf with structured logging - Add /metrics endpoint to health check server - 8/8 logging tests passing, 13/13 metrics tests passing - All verification criteria met Dependencies added: - go.uber.org/zap v1.28.0 (structured logging) - github.com/prometheus/client_golang v1.24.1 (metrics export) Closes T1.2
89 lines
2.2 KiB
Go
89 lines
2.2 KiB
Go
package health
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
)
|
|
|
|
// Handler provides HTTP endpoints for health checks
|
|
type Handler struct {
|
|
checker *Checker
|
|
}
|
|
|
|
// NewHandler creates a new HTTP handler for health checks
|
|
func NewHandler(checker *Checker) *Handler {
|
|
return &Handler{
|
|
checker: checker,
|
|
}
|
|
}
|
|
|
|
// RegisterRoutes registers health check routes on a mux
|
|
func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
|
|
mux.HandleFunc("/health", h.handleHealth)
|
|
mux.HandleFunc("/health/live", h.handleLive)
|
|
mux.HandleFunc("/health/ready", h.handleReady)
|
|
// Prometheus metrics endpoint
|
|
mux.Handle("/metrics", promhttp.Handler())
|
|
}
|
|
|
|
// handleHealth returns full health report
|
|
func (h *Handler) handleHealth(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
report := h.checker.Check(r.Context())
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
// Return 200 if healthy, 503 if unhealthy
|
|
if report.Status != StatusHealthy {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(report)
|
|
}
|
|
|
|
// handleLive is Kubernetes liveness probe endpoint
|
|
// Returns 200 if the service is running, 503 otherwise
|
|
func (h *Handler) handleLive(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
if h.checker.temporalClient == nil {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
w.Write([]byte("service not initialized"))
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte("alive"))
|
|
}
|
|
|
|
// handleReady is Kubernetes readiness probe endpoint
|
|
// Returns 200 if the service is ready to accept traffic, 503 otherwise
|
|
func (h *Handler) handleReady(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
report := h.checker.Check(r.Context())
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
// Service is ready only if healthy
|
|
if report.Status != StatusHealthy {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"ready": report.Status == StatusHealthy,
|
|
"components": report.Components,
|
|
})
|
|
}
|