package gitea import ( "net/http" "time" "gitea.com/gitea/gitea-mcp/pkg/debug" "gitea.com/gitea/gitea-mcp/pkg/flag" "gitea.com/gitea/gitea-mcp/pkg/log" "go.uber.org/zap" ) // loggingRoundTripper wraps an http.RoundTripper to emit one structured log // entry per upstream Gitea API call when debug is enabled. The information // here is much more useful for diagnosing slow / failing tool calls than the // gitea SDK's built-in debug mode (which only writes a separate, free-form // stream that doesn't merge with the zap log file). // // We log: // - method, URL (with sensitive query params stripped) // - request_id from the request context (when the caller propagated one) // - HTTP status code (or transport-level error) // - duration in milliseconds // - response Content-Length when the server provided one // // We deliberately do NOT log request or response bodies — those routinely // contain user secrets (tokens being created, webhook payloads, file // contents) and the log file is meant to be safe to share. type loggingRoundTripper struct { base http.RoundTripper } func (rt *loggingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if !flag.DebugScopeEnabled("http") { return rt.base.RoundTrip(req) } start := time.Now() resp, err := rt.base.RoundTrip(req) duration := time.Since(start) fields := []zap.Field{ zap.String("method", req.Method), zap.String("url", debug.StripQueryTokens(req.URL.String())), zap.Int64("duration_ms", duration.Milliseconds()), } if reqID := debug.RequestIDFromContext(req.Context()); reqID != "" { fields = append(fields, zap.String("request_id", reqID)) } if toolName := debug.ToolNameFromContext(req.Context()); toolName != "" { fields = append(fields, zap.String("tool", toolName)) } if err != nil { fields = append(fields, zap.String("error", err.Error())) log.Debug("gitea http: transport error", fields...) return resp, err } fields = append(fields, zap.Int("status", resp.StatusCode), zap.Int64("bytes_in", resp.ContentLength), ) log.Debug("gitea http: response", fields...) return resp, nil }