package restapi import ( "time" "galaxy/gateway/internal/logging" "galaxy/gateway/internal/telemetry" "github.com/gin-gonic/gin" "go.opentelemetry.io/otel/attribute" "go.uber.org/zap" ) func withPublicObservability(logger *zap.Logger, metrics *telemetry.Runtime) gin.HandlerFunc { if logger == nil { logger = zap.NewNop() } return func(c *gin.Context) { start := time.Now() c.Next() statusCode := c.Writer.Status() route := c.FullPath() if route == "" { route = c.Request.URL.Path } class, ok := PublicRouteClassFromContext(c.Request.Context()) if !ok { class = PublicRouteClassPublicMisc } errorCode, _ := c.Get(publicErrorCodeContextKey) errorCodeValue, _ := errorCode.(string) outcome := telemetry.OutcomeFromPublicErrorCode(statusCode, errorCodeValue) rejectReason := telemetry.RejectReason(outcome) duration := time.Since(start) attrs := []attribute.KeyValue{ attribute.String("route_class", string(class)), attribute.String("route", route), attribute.String("method", c.Request.Method), attribute.String("edge_outcome", string(outcome)), } if rejectReason != "" { attrs = append(attrs, attribute.String("reject_reason", rejectReason)) } metrics.RecordPublicRequest(c.Request.Context(), attrs, duration) fields := []zap.Field{ zap.String("component", "public_http"), zap.String("transport", "http"), zap.String("route", route), zap.String("route_class", string(class)), zap.String("method", c.Request.Method), zap.Int("status_code", statusCode), zap.Float64("duration_ms", float64(duration.Microseconds())/1000), zap.String("edge_outcome", string(outcome)), } if rejectReason != "" { fields = append(fields, zap.String("reject_reason", rejectReason)) } fields = append(fields, logging.TraceFieldsFromContext(c.Request.Context())...) switch outcome { case telemetry.EdgeOutcomeSuccess: logger.Info("public request completed", fields...) case telemetry.EdgeOutcomeBackendUnavailable, telemetry.EdgeOutcomeInternalError: logger.Error("public request failed", fields...) default: logger.Warn("public request rejected", fields...) } } }