Skip to content

Commit

Permalink
chore(examples): add one example to log request body when the status …
Browse files Browse the repository at this point in the history
…code is non 200 (#4108)

* chore(examples): add one example to log request body when the status code is non 200

* chore(examples): fix typo

* chore(deps): add logging the request body for a request doc

* chore(deps): update logging docs with some minor edits
  • Loading branch information
richzw authored Mar 18, 2024
1 parent 68f8d10 commit 4319001
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 2 deletions.
61 changes: 61 additions & 0 deletions docs/docs/operations/logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
layout: default
title: Logging the request body pattern for a request
nav_order: 5
parent: Operations
---

# Logging the request body pattern for a request

If you want to log the request body of incoming requests, you will need to buffer the body before it reaches the gateway. To log the request body, you can use a middleware `http.Handler` to buffer the request body before it's consumed.

1. Create a `http.Handler` middleware. The `logRequestBody` example middleware logs the request body when the response status code is not 200.

```go
type logResponseWriter struct {
http.ResponseWriter
statusCode int
}

func (rsp *logResponseWriter) WriteHeader(code int) {
rsp.statusCode = code
rsp.ResponseWriter.WriteHeader(code)
}

func newLogResponseWriter(w http.ResponseWriter) *logResponseWriter {
return &logResponseWriter{w, http.StatusOK}
}

// logRequestBody logs the request body when the response status code is not 200.
func logRequestBody(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
lw := newLogResponseWriter(w)

// Note that buffering the entire request body could consume a lot of memory.
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, fmt.Sprintf("failed to read body: %v", err), http.StatusBadRequest)
return
}
clonedR := r.Clone(r.Context())
clonedR.Body = io.NopCloser(bytes.NewReader(body))

h.ServeHTTP(lw, clonedR)

if lw.statusCode != http.StatusOK {
grpclog.Errorf("http error %+v request body %+v", lw.statusCode, string(body))
}
})
}
```

2. Wrap the gateway serve mux with the `logRequestBody` middleware:

```go
mux := runtime.NewServeMux()
// Register generated gateway handlers

s := &http.Server{
Handler: logRequestBody(mux),
}
```
39 changes: 38 additions & 1 deletion examples/internal/gateway/handlers.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package gateway

import (
"bytes"
"fmt"
"io"
"net/http"
"path"
"strings"
Expand All @@ -27,7 +29,7 @@ func openAPIServer(dir string) http.HandlerFunc {
}
}

// allowCORS allows Cross Origin Resoruce Sharing from any origin.
// allowCORS allows Cross Origin Resource Sharing from any origin.
// Don't do this without consideration in production systems.
func allowCORS(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -64,3 +66,38 @@ func healthzServer(conn *grpc.ClientConn) http.HandlerFunc {
fmt.Fprintln(w, "ok")
}
}

type logResponseWriter struct {
http.ResponseWriter
statusCode int
}

func (rsp *logResponseWriter) WriteHeader(code int) {
rsp.statusCode = code
rsp.ResponseWriter.WriteHeader(code)
}

func newLogResponseWriter(w http.ResponseWriter) *logResponseWriter {
return &logResponseWriter{w, http.StatusOK}
}

// logRequestBody logs the request body when the response status code is not 200.
// This addresses the issue of being unable to retrieve the request body in the customErrorHandler middleware.
func logRequestBody(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
lw := newLogResponseWriter(w)
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, fmt.Sprintf("grpc server read request body err %+v", err), http.StatusBadRequest)
return
}
clonedR := r.Clone(r.Context())
clonedR.Body = io.NopCloser(bytes.NewReader(body))

h.ServeHTTP(lw, clonedR)

if lw.statusCode != http.StatusOK {
grpclog.Errorf("http error %+v request body %+v", lw.statusCode, string(body))
}
})
}
2 changes: 1 addition & 1 deletion examples/internal/gateway/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func Run(ctx context.Context, opts Options) error {

s := &http.Server{
Addr: opts.Addr,
Handler: allowCORS(mux),
Handler: logRequestBody(allowCORS(mux)),
}
go func() {
<-ctx.Done()
Expand Down

0 comments on commit 4319001

Please sign in to comment.