From 1eafebedbc3af5b709fdb4371268ae3627932863 Mon Sep 17 00:00:00 2001 From: Xe Iaso Date: Fri, 18 Jul 2025 20:13:03 +0000 Subject: [PATCH] feat(osiris): serve metrics and healthz Signed-off-by: Xe Iaso --- cmd/osiris/internal/entrypoint/entrypoint.go | 79 +++++++++++++++++++- cmd/osiris/internal/entrypoint/router.go | 21 +++++- 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/cmd/osiris/internal/entrypoint/entrypoint.go b/cmd/osiris/internal/entrypoint/entrypoint.go index fdc643e1..611f8097 100644 --- a/cmd/osiris/internal/entrypoint/entrypoint.go +++ b/cmd/osiris/internal/entrypoint/entrypoint.go @@ -1,13 +1,20 @@ package entrypoint import ( + "context" "fmt" "log/slog" + "net" "net/http" + "os" + "os/signal" + "syscall" "github.com/TecharoHQ/anubis/cmd/osiris/internal/config" "github.com/TecharoHQ/anubis/internal" "github.com/hashicorp/hcl/v2/hclsimple" + "github.com/prometheus/client_golang/prometheus/promhttp" + "golang.org/x/sync/errgroup" healthv1 "google.golang.org/grpc/health/grpc_health_v1" ) @@ -18,6 +25,9 @@ type Options struct { func Main(opts Options) error { internal.SetHealth("osiris", healthv1.HealthCheckResponse_NOT_SERVING) + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer cancel() + var cfg config.Toplevel if err := hclsimple.DecodeFile(opts.ConfigFname, nil, &cfg); err != nil { return fmt.Errorf("can't read configuration file %s:\n\n%w", opts.ConfigFname, err) @@ -32,6 +42,71 @@ func Main(opts Options) error { return err } - slog.Info("listening on", "http", cfg.Bind.HTTP) - return http.ListenAndServe(cfg.Bind.HTTP, rtr) + g, gCtx := errgroup.WithContext(ctx) + + // HTTP + g.Go(func() error { + ln, err := net.Listen("tcp", cfg.Bind.HTTP) + if err != nil { + return fmt.Errorf("(HTTP) can't bind to tcp %s: %w", cfg.Bind.HTTP, err) + } + defer ln.Close() + + go func(ctx context.Context) { + <-ctx.Done() + ln.Close() + }(gCtx) + + slog.Info("listening for HTTP", "bind", cfg.Bind.HTTP) + + srv := http.Server{Handler: rtr, ErrorLog: internal.GetFilteredHTTPLogger()} + + return srv.Serve(ln) + }) + + // HTTPS + + // Metrics + g.Go(func() error { + ln, err := net.Listen("tcp", cfg.Bind.Metrics) + if err != nil { + return fmt.Errorf("(metrics) can't bind to tcp %s: %w", cfg.Bind.Metrics, err) + } + defer ln.Close() + + go func(ctx context.Context) { + <-ctx.Done() + ln.Close() + }(gCtx) + + mux := http.NewServeMux() + + mux.Handle("/metrics", promhttp.Handler()) + mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { + st, ok := internal.GetHealth("osiris") + if !ok { + slog.Error("health service osiris does not exist, file a bug") + } + + switch st { + case healthv1.HealthCheckResponse_NOT_SERVING: + http.Error(w, "NOT OK", http.StatusInternalServerError) + return + case healthv1.HealthCheckResponse_SERVING: + fmt.Fprintln(w, "OK") + return + default: + http.Error(w, "UNKNOWN", http.StatusFailedDependency) + return + } + }) + + slog.Info("listening for Metrics", "bind", cfg.Bind.Metrics) + + srv := http.Server{Handler: mux, ErrorLog: internal.GetFilteredHTTPLogger()} + + return srv.Serve(ln) + }) + + return g.Wait() } diff --git a/cmd/osiris/internal/entrypoint/router.go b/cmd/osiris/internal/entrypoint/router.go index 4f16f34a..d120fbab 100644 --- a/cmd/osiris/internal/entrypoint/router.go +++ b/cmd/osiris/internal/entrypoint/router.go @@ -14,11 +14,25 @@ import ( "github.com/TecharoHQ/anubis/cmd/osiris/internal/config" "github.com/lum8rjack/go-ja4h" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" ) var ( ErrTargetInvalid = errors.New("[unexpected] target invalid") ErrNoHandler = errors.New("[unexpected] no handler for domain") + + requestsPerDomain = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: "techaro", + Subsystem: "osiris", + Name: "request_count", + }, []string{"domain"}) + + unresolvedRequests = promauto.NewGauge(prometheus.GaugeOpts{ + Namespace: "techaro", + Subsystem: "osiris", + Name: "unresolved_requests", + }) ) type Router struct { @@ -86,12 +100,12 @@ func NewRouter(c config.Toplevel) (*Router, error) { return nil, err } - fmt.Printf("%#v\n", result.routes) - return result, nil } func (rtr *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) { + requestsPerDomain.WithLabelValues(r.Host).Inc() + var h http.Handler var ok bool @@ -104,11 +118,12 @@ func (rtr *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) { rtr.lock.RUnlock() if !ok { + unresolvedRequests.Inc() http.NotFound(w, r) // TODO(Xe): brand this return } - r.Header.Set("X-HTTP-JA4H-Fingerprint", ja4hFP) + r.Header.Set("X-Http-Ja4h-Fingerprint", ja4hFP) h.ServeHTTP(w, r) }