package internalhttp import ( "log/slog" "net/http" "time" "galaxy/mail/internal/logging" "galaxy/mail/internal/telemetry" "go.opentelemetry.io/otel/attribute" ) type edgeOutcome string const ( edgeOutcomeSuccess edgeOutcome = "success" edgeOutcomeRejected edgeOutcome = "rejected" edgeOutcomeFailed edgeOutcome = "failed" ) func instrumentRoute(route string, logger *slog.Logger, telemetryRuntime *telemetry.Runtime, next http.Handler) http.Handler { if logger == nil { logger = slog.Default() } return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { startedAt := time.Now() recorder := &observedResponseWriter{ ResponseWriter: writer, statusCode: http.StatusOK, } next.ServeHTTP(recorder, request) duration := time.Since(startedAt) outcome := outcomeFromStatusCode(recorder.statusCode) attrs := []attribute.KeyValue{ attribute.String("route", route), attribute.String("method", request.Method), attribute.String("edge_outcome", string(outcome)), } if recorder.errorCode != "" { attrs = append(attrs, attribute.String("error_code", recorder.errorCode)) } if telemetryRuntime != nil { telemetryRuntime.RecordInternalHTTPRequest(request.Context(), attrs, duration) } logArgs := []any{ "component", "internal_http", "transport", "http", "route", route, "method", request.Method, "status_code", recorder.statusCode, "duration_ms", float64(duration.Microseconds()) / 1000, "edge_outcome", string(outcome), } if recorder.errorCode != "" { logArgs = append(logArgs, "error_code", recorder.errorCode) } logArgs = append(logArgs, logging.TraceAttrsFromContext(request.Context())...) switch outcome { case edgeOutcomeSuccess: logger.Info("internal request completed", logArgs...) case edgeOutcomeFailed: logger.Error("internal request failed", logArgs...) default: logger.Warn("internal request rejected", logArgs...) } }) } type observedResponseWriter struct { http.ResponseWriter statusCode int errorCode string wroteHeader bool } func (writer *observedResponseWriter) WriteHeader(statusCode int) { if writer.wroteHeader { return } writer.statusCode = statusCode writer.wroteHeader = true writer.ResponseWriter.WriteHeader(statusCode) } func (writer *observedResponseWriter) Write(payload []byte) (int, error) { if !writer.wroteHeader { writer.WriteHeader(http.StatusOK) } return writer.ResponseWriter.Write(payload) } func (writer *observedResponseWriter) SetErrorCode(code string) { writer.errorCode = code } func outcomeFromStatusCode(statusCode int) edgeOutcome { switch { case statusCode >= 500: return edgeOutcomeFailed case statusCode >= 400: return edgeOutcomeRejected default: return edgeOutcomeSuccess } }