This example will help you to capture response status code and body size then log it. You can log request related data as well if you wish but it is outside of this post's scope.


ResponseWriter


package response

import (
"bufio"
"fmt"
"net"
"net/http"
)

// Acts as an adapter for `http.ResponseWriter` type to store response status
// code and size.
type ResponseWriter struct {
http.ResponseWriter

code int
size int
}

// Returns a new `ResponseWriter` type by decorating `http.ResponseWriter` type.
func NewResponseWriter(w http.ResponseWriter) *ResponseWriter {
return &ResponseWriter{
ResponseWriter: w,
}
}

// Overrides `http.ResponseWriter` type.
func (r *ResponseWriter) WriteHeader(code int) {
if r.Code() == 0 {
r.code = code
r.ResponseWriter.WriteHeader(code)
}
}

// Overrides `http.ResponseWriter` type.
func (r *ResponseWriter) Write(body []byte) (int, error) {
if r.Code() == 0 {
r.WriteHeader(http.StatusOK)
}

var err error
r.size, err = r.ResponseWriter.Write(body)

return r.size, err
}

// Overrides `http.Flusher` type.
func (r *ResponseWriter) Flush() {
if fl, ok := r.ResponseWriter.(http.Flusher); ok {
if r.Code() == 0 {
r.WriteHeader(http.StatusOK)
}

fl.Flush()
}
}

// Overrides `http.Hijacker` type.
func (r *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
hj, ok := r.ResponseWriter.(http.Hijacker)
if !ok {
return nil, nil, fmt.Errorf("the hijacker interface is not supported")
}

return hj.Hijack()
}

// Returns response status code.
func (r *ResponseWriter) Code() int {
return r.code
}

// Returns response size.
func (r *ResponseWriter) Size() int {
return r.size
}

AccessLoger


package middleware

import (
"log"
"net/http"

"response"
)

// AccessLoger helps logging request and response related data.
func AccessLoger(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rw := response.NewResponseWriter(w)

h.ServeHTTP(rw, r)

log.Println("status:", rw.Code(), "size:", rw.Size(), "bytes")
})
}

Server


package http

import (
"net/http"
)

// NewServer returns `http.Server` pointer type.
func NewServer(address string, handler http.Handler) *http.Server {
return &http.Server{
Addr: address,
Handler: AccessLoger(handler),
}
}

Test


If you send an example request to one of your endpoints, the log should look like below.


2021/01/27 17:19:39 status: 200 size: 63 bytes