github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/httptransport/handlers/log_handler.go (about)

     1  package handlers
     2  
     3  import (
     4  	"net/http"
     5  	"strconv"
     6  
     7  	"github.com/google/uuid"
     8  	"github.com/pkg/errors"
     9  
    10  	"github.com/machinefi/w3bstream/pkg/depends/kit/httptransport/httpx"
    11  	"github.com/machinefi/w3bstream/pkg/depends/kit/logr"
    12  	"github.com/machinefi/w3bstream/pkg/depends/kit/metax"
    13  	"github.com/machinefi/w3bstream/pkg/depends/x/misc/timer"
    14  )
    15  
    16  func LogHandler() func(handler http.Handler) http.Handler {
    17  	return func(handler http.Handler) http.Handler {
    18  		return &loggerHandler{
    19  			next: handler,
    20  		}
    21  	}
    22  }
    23  
    24  type loggerHandler struct {
    25  	next http.Handler
    26  }
    27  
    28  type LoggerResponseWriter struct {
    29  	rw         http.ResponseWriter
    30  	written    bool
    31  	statusCode int
    32  	err        error
    33  }
    34  
    35  func (rw *LoggerResponseWriter) Header() http.Header { return rw.rw.Header() }
    36  
    37  func (rw *LoggerResponseWriter) WriteErr(err error) { rw.err = err }
    38  
    39  func (rw *LoggerResponseWriter) WriteHeader(sc int) {
    40  	if rw.written {
    41  		return
    42  	}
    43  	rw.rw.WriteHeader(sc)
    44  	rw.statusCode = sc
    45  	rw.written = true
    46  }
    47  
    48  func (rw *LoggerResponseWriter) Write(data []byte) (int, error) {
    49  	if rw.err != nil && rw.statusCode >= http.StatusBadRequest {
    50  		rw.err = errors.New(string(data))
    51  	}
    52  	return rw.rw.Write(data)
    53  }
    54  
    55  func (h *loggerHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
    56  	cost := timer.Start()
    57  	reqID := req.Header.Get(httpx.HeaderRequestID)
    58  	if reqID == "" {
    59  		reqID = uuid.New().String()
    60  	}
    61  
    62  	var (
    63  		w = &LoggerResponseWriter{rw: rw}
    64  		l = logr.FromContext(req.Context())
    65  	)
    66  
    67  	defer func() {
    68  		header := req.Header
    69  		duration := strconv.FormatInt(cost().Microseconds(), 10) + "μs"
    70  		fields := []interface{}{
    71  			"@cst", duration,
    72  			"@rmt", httpx.ClientIP(req),
    73  			"@mtd", req.Method[0:3],
    74  			"@url", req.URL.String(),
    75  			"@agent", header.Get(httpx.HeaderUserAgent),
    76  			"@status", w.statusCode,
    77  		}
    78  		if w.err != nil {
    79  			if w.statusCode >= http.StatusInternalServerError {
    80  				l.WithValues(fields).Error(w.err)
    81  			} else {
    82  				l.WithValues(fields).Warn(w.err)
    83  			}
    84  		} else {
    85  			l.WithValues(fields).Info("")
    86  		}
    87  	}()
    88  
    89  	h.next.ServeHTTP(
    90  		w,
    91  		req.WithContext(
    92  			metax.ContextWithMeta(req.Context(), metax.ParseMeta(reqID)),
    93  		),
    94  	)
    95  }