package main import ( "bytes" "encoding/json" "fmt" "log" "net/http" "net/http/httptest" "net/http/httputil" "net/url" "strconv" "time" "github.com/caarlos0/env/v11" ) var cfg config type config struct { BaseURL string `env:"BASE_URL"` Port int `env:"PORT"` } func createProxy(target *url.URL) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { r.Host = target.Host r.URL.Scheme = target.Scheme r.URL.Host = target.Host lrw := &LoggingResponseWriter{ResponseWriter: w, body: new(bytes.Buffer)} proxy := httputil.NewSingleHostReverseProxy(target) startTime := time.Now() recorder := httptest.NewRecorder() proxy.ServeHTTP(recorder, r) responseBody := recorder.Body.Bytes() var jsonResponse map[string]interface{} err := json.Unmarshal(responseBody, &jsonResponse) if err != nil { log.Printf("Error unmarshalling JSON response: %v", err) lrw.Write(responseBody) return } // Add your metrics metadata here jsonResponse["metrics"] = map[string]interface{}{ "requestPath": r.URL.Path, "statusCode": recorder.Code, "responseTime": time.Since(startTime).Milliseconds(), // Add more metrics as needed } modifiedResponseBody, err := json.Marshal(jsonResponse) if err != nil { log.Printf("Error marshalling modified JSON response: %v", err) lrw.Write(responseBody) return } for name, values := range recorder.Header() { for _, value := range values { w.Header().Add(name, value) } } w.WriteHeader(recorder.Code) lrw.Write(modifiedResponseBody) log.Printf("Response with metrics: %s", lrw.Body()) } } type LoggingResponseWriter struct { http.ResponseWriter body *bytes.Buffer } func (lrw *LoggingResponseWriter) Write(b []byte) (int, error) { lrw.body.Write(b) return lrw.ResponseWriter.Write(b) } func (lrw *LoggingResponseWriter) Body() string { return lrw.body.String() } func main() { env.Parse(&cfg) targetURL, err := url.Parse(cfg.BaseURL) if err != nil { log.Fatal(err) } http.HandleFunc("/", createProxy(targetURL)) log.Printf("Starting proxy server on :%s", strconv.Itoa(cfg.Port)) err = http.ListenAndServe(fmt.Sprintf(":%s", strconv.Itoa(cfg.Port)), nil) if err != nil { log.Fatal(err) } }