Files
LLM-Proxy-Metrics/llmproxymetrics.go

163 lines
4.1 KiB
Go
Raw Normal View History

2025-04-20 13:47:17 +03:00
package main
import (
2025-04-22 19:31:34 +03:00
"bufio"
2025-04-20 13:47:17 +03:00
"encoding/json"
"fmt"
"log"
"net/http"
"net/http/httptest"
"net/http/httputil"
"net/url"
"strconv"
2025-04-22 19:31:34 +03:00
"strings"
2025-04-20 13:47:17 +03:00
"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
2025-04-22 19:31:34 +03:00
// startTime := time.Now()
2025-04-20 13:47:17 +03:00
proxy := httputil.NewSingleHostReverseProxy(target)
2025-04-22 19:31:34 +03:00
proxy.Director = func(req *http.Request) {
req.Header.Set("X-Forwarded-For", r.RemoteAddr)
req.Host = target.Host
}
2025-04-20 13:47:17 +03:00
recorder := httptest.NewRecorder()
2025-04-22 19:31:34 +03:00
2025-04-20 13:47:17 +03:00
proxy.ServeHTTP(recorder, r)
responseBody := recorder.Body.Bytes()
2025-04-22 19:31:34 +03:00
contentType := recorder.Result().Header.Get("Content-Type")
log.Printf("Response Content-Type: %s", contentType)
log.Printf("Response Body: %s", string(responseBody))
if strings.Contains(contentType, "application/json") {
var jsonResponse map[string]interface{}
err := json.Unmarshal(responseBody, &jsonResponse)
if err != nil {
log.Printf("Error unmarshalling JSON response: %v", err)
w.WriteHeader(recorder.Code)
w.Write(responseBody)
return
}
2025-04-20 13:47:17 +03:00
2025-04-22 19:31:34 +03:00
// jsonResponse["metrics"] = map[string]interface{}{
// "requestPath": r.URL.Path,
// "statusCode": recorder.Code,
// "responseTime": time.Since(startTime).Milliseconds(),
// }
modifiedResponseBody, err := json.Marshal(jsonResponse)
if err != nil {
log.Printf("Error marshalling modified JSON response: %v", err)
w.WriteHeader(recorder.Code)
w.Write(responseBody)
return
}
2025-04-20 13:47:17 +03:00
2025-04-22 19:31:34 +03:00
for name, values := range recorder.Header() {
for _, value := range values {
w.Header().Add(name, value)
}
}
2025-04-20 13:47:17 +03:00
2025-04-22 19:31:34 +03:00
w.WriteHeader(recorder.Code)
w.Write(modifiedResponseBody)
// log.Printf("Modified Response Body: %s", string(modifiedResponseBody))
} else if strings.Contains(contentType, "application/x-ndjson") {
var modifiedResponseBody []string
scanner := bufio.NewScanner(strings.NewReader(string(responseBody)))
for scanner.Scan() {
line := scanner.Text()
if line != "" {
var jsonResponse map[string]interface{}
err := json.Unmarshal([]byte(line), &jsonResponse)
if err != nil {
log.Printf("Error unmarshalling NDJSON line: %v", err)
modifiedResponseBody = append(modifiedResponseBody, line)
continue
}
// jsonResponse["metrics"] = map[string]interface{}{
// "requestPath": r.URL.Path,
// "statusCode": recorder.Code,
// "responseTime": time.Since(startTime).Milliseconds(),
// }
modifiedLine, err := json.Marshal(jsonResponse)
if err != nil {
log.Printf("Error marshalling modified NDJSON line: %v", err)
modifiedResponseBody = append(modifiedResponseBody, string([]byte(line)))
continue
}
modifiedResponseBody = append(modifiedResponseBody, string(modifiedLine))
}
}
2025-04-20 13:47:17 +03:00
2025-04-22 19:31:34 +03:00
if err := scanner.Err(); err != nil {
log.Printf("Error scanning NDJSON: %v", err)
w.WriteHeader(recorder.Code)
w.Write(responseBody)
return
2025-04-20 13:47:17 +03:00
}
2025-04-22 19:31:34 +03:00
for name, values := range recorder.Header() {
for _, value := range values {
w.Header().Add(name, value)
}
}
2025-04-20 13:47:17 +03:00
2025-04-22 19:31:34 +03:00
w.WriteHeader(recorder.Code)
w.Write([]byte(strings.Join(modifiedResponseBody, "\n")))
2025-04-20 13:47:17 +03:00
2025-04-22 19:31:34 +03:00
// log.Printf("Modified Response Body: %s", strings.Join(modifiedResponseBody, "\n"))
} else {
for name, values := range recorder.Header() {
for _, value := range values {
w.Header().Add(name, value)
}
}
2025-04-20 13:47:17 +03:00
2025-04-22 19:31:34 +03:00
w.WriteHeader(recorder.Code)
w.Write(responseBody)
2025-04-20 13:47:17 +03:00
2025-04-22 19:31:34 +03:00
// log.Printf("Response without metrics: %s", string(responseBody))
}
}
2025-04-20 13:47:17 +03:00
}
func main() {
2025-04-22 19:31:34 +03:00
err := env.Parse(&cfg)
if err != nil {
log.Fatalf("Error parsing environment variables: %v", err)
}
2025-04-20 13:47:17 +03:00
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))
2025-04-22 19:31:34 +03:00
err = http.ListenAndServe(fmt.Sprintf(":%d", cfg.Port), nil)
2025-04-20 13:47:17 +03:00
if err != nil {
log.Fatal(err)
}
}