feat: add upstream request/response logging for passthrough routes#186
feat: add upstream request/response logging for passthrough routes#186
Conversation
5e86692 to
63d2dcb
Compare
| if !bytes.HasSuffix(result, []byte("\n")) { | ||
| result = append(result, []byte("\n")...) | ||
| } | ||
| // Trim trailing newline added by pretty.Pretty. |
There was a problem hiding this comment.
Was there a reson for removing trailing new line?
Viewing dumps in terminal without it is annoying.
|
I'm thinking we should perhaps hold off on this change until #166 lands. If we have a session ID then we can correlate all intercepted and passthrough routes. If you'd rather land this in the interim and then add session ID correlation later, that's fine too - in which case I'll give this a review. |
Will session ID be available in each request? Or only in requests from clients that set it? |
Only if clients set it |
If session ID is not always set I'm not sure what do you mean by session ID correlation. Traffic dumping (both intercepted or passed though) will need to work without session ID. I agree that dump file names are unfortunate for pass though routes (file names contain random UUIDs, not related to anything) but I wanted to keep the same format. Maybe different approach would be better. |
ssncferreira
left a comment
There was a problem hiding this comment.
Nice addition 💪 this will be super nice for debugging. Just a few comments, but nothing blockin
intercept/apidump/apidump.go
Outdated
| // NewRoundTripperMiddleware returns http.RoundTripper that dumps requests and responses to files. | ||
| // If baseDir is empty, returns the original transport unchanged. | ||
| // Used for logging passed through requests. | ||
| func NewRoundTripperMiddleware(transport http.RoundTripper, baseDir string, provider string, logger slog.Logger, clk quartz.Clock) http.RoundTripper { |
There was a problem hiding this comment.
nit: Since this is just for Passthrough routes, I would suggest renaming it:
| func NewRoundTripperMiddleware(transport http.RoundTripper, baseDir string, provider string, logger slog.Logger, clk quartz.Clock) http.RoundTripper { | |
| func NewPassthroughMiddleware(transport http.RoundTripper, baseDir string, provider string, logger slog.Logger, clk quartz.Clock) http.RoundTripper { |
Similarly, rename NewMiddleware to NewBridgeMiddleware
intercept/apidump/apidump.go
Outdated
| // A random UUID is generated for the filename. "passthrough" is used as the directory name | ||
| // in place of the model. | ||
| func passthroughDumpPath(baseDir string, provider string, clk quartz.Clock) string { | ||
| return filepath.Join(baseDir, provider, "passthrough", fmt.Sprintf("%d-%s", clk.Now().UTC().UnixMilli(), uuid.New())) |
There was a problem hiding this comment.
Using a randomly generated UUID here doesn't seem very useful 🤔 What if we use the sanitized route instead (e.g., /v1/models → v1-models) to make it easier to identify requests in the dump files. In order to avoid collisions, we can also adda short UUID suffix.
| result := body | ||
| if json.Valid(body) { | ||
| result = pretty.Pretty(body) | ||
| } |
There was a problem hiding this comment.
Why did this change? Since result is body, if json.Valid returns false we'll be appending a newline to it, couldn't this impact the caller's slice?
intercept/apidump/apidump.go
Outdated
| fmt.Fprintf(&buf, "\r\n") | ||
| _, err = fmt.Fprintf(&buf, "\r\n") | ||
| if err != nil { | ||
| return fmt.Errorf("write request body: %w", err) |
There was a problem hiding this comment.
nit: not really the body, right?
| return fmt.Errorf("write request body: %w", err) | |
| return fmt.Errorf("write header terminator: %w", err) |
intercept/apidump/apidump.go
Outdated
| } | ||
| _, err = fmt.Fprintf(&headerBuf, "\r\n") | ||
| if err != nil { | ||
| return fmt.Errorf("write response body: %w", err) |
There was a problem hiding this comment.
nit: same as above
| return fmt.Errorf("write response body: %w", err) | |
| return fmt.Errorf("write header terminator: %w", err) |
| fmt.Fprintf(w, "%s: %s\r\n", key, override) | ||
| _, err := fmt.Fprintf(w, "%s: %s\r\n", key, override) | ||
| if err != nil { | ||
| return fmt.Errorf("write response header override: %w", err) |
There was a problem hiding this comment.
This function is used for both request/responses, right? Consider removing response from here, to avoid confusion. Same below 👀
dannykopping
left a comment
There was a problem hiding this comment.
LGTM, thanks!
As discussed, we'll leave session indexing for later.
36705e3 to
153f439
Compare

Added request/response dump functionality for passthrough routes.
RoundTripperMiddlewarein theapidumppackage.APIDumpDirmethodDrive by change