|
1 | 1 | package aibridge |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "context" |
| 5 | + "fmt" |
4 | 6 | "log" |
5 | 7 | "net/http" |
6 | 8 | "net/http/httputil" |
7 | 9 | "os" |
| 10 | + "path/filepath" |
| 11 | + "strings" |
| 12 | + |
| 13 | + "cdr.dev/slog" |
8 | 14 | ) |
9 | 15 |
|
| 16 | +// sanitizeModelName makes a model name safe for use as a directory name. |
| 17 | +// Replaces filesystem-unsafe characters with underscores. |
| 18 | +func sanitizeModelName(model string) string { |
| 19 | + replacer := strings.NewReplacer( |
| 20 | + "/", "_", |
| 21 | + "\\", "_", |
| 22 | + ":", "_", |
| 23 | + "*", "_", |
| 24 | + "?", "_", |
| 25 | + "\"", "_", |
| 26 | + "<", "_", |
| 27 | + ">", "_", |
| 28 | + "|", "_", |
| 29 | + ) |
| 30 | + return replacer.Replace(model) |
| 31 | +} |
| 32 | + |
10 | 33 | // logUpstreamRequest logs an HTTP request with the given ID and model name. |
11 | 34 | // The prefix format is: [req] [id] [model] |
12 | 35 | func logUpstreamRequest(logger *log.Logger, id, model string, req *http.Request) { |
@@ -42,16 +65,32 @@ func logUpstreamError(logger *log.Logger, id, model string, err error) { |
42 | 65 | } |
43 | 66 |
|
44 | 67 | // createLoggingMiddleware creates a middleware function that logs requests and responses. |
45 | | -// Returns nil if logging setup fails. |
46 | | -func createLoggingMiddleware(provider, id, model string) func(*http.Request, func(*http.Request) (*http.Response, error)) (*http.Response, error) { |
47 | | - reqLogFile, err := os.OpenFile("/tmp/"+provider+"-req.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) |
| 68 | +// Logs are written to $TMPDIR/$provider/$model/$id.req.log and $TMPDIR/$provider/$model/$id.res.log |
| 69 | +// Returns nil if logging setup fails, logging errors via the provided logger. |
| 70 | +func createLoggingMiddleware(logger slog.Logger, provider, id, model string) func(*http.Request, func(*http.Request) (*http.Response, error)) (*http.Response, error) { |
| 71 | + ctx := context.Background() |
| 72 | + safeModel := sanitizeModelName(model) |
| 73 | + logDir := filepath.Join(os.TempDir(), provider, safeModel) |
| 74 | + |
| 75 | + // Create the directory structure if it doesn't exist |
| 76 | + if err := os.MkdirAll(logDir, 0755); err != nil { |
| 77 | + logger.Warn(ctx, "failed to create log directory", slog.Error(err), slog.F("dir", logDir)) |
| 78 | + return nil |
| 79 | + } |
| 80 | + |
| 81 | + reqLogPath := filepath.Join(logDir, fmt.Sprintf("%s.req.log", id)) |
| 82 | + resLogPath := filepath.Join(logDir, fmt.Sprintf("%s.res.log", id)) |
| 83 | + |
| 84 | + reqLogFile, err := os.OpenFile(reqLogPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) |
48 | 85 | if err != nil { |
| 86 | + logger.Warn(ctx, "failed to open request log file", slog.Error(err), slog.F("path", reqLogPath)) |
49 | 87 | return nil |
50 | 88 | } |
51 | 89 |
|
52 | | - resLogFile, err := os.OpenFile("/tmp/"+provider+"-res.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) |
| 90 | + resLogFile, err := os.OpenFile(resLogPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) |
53 | 91 | if err != nil { |
54 | 92 | reqLogFile.Close() |
| 93 | + logger.Warn(ctx, "failed to open response log file", slog.Error(err), slog.F("path", resLogPath)) |
55 | 94 | return nil |
56 | 95 | } |
57 | 96 |
|
|
0 commit comments