github.com/ethersphere/bee/v2@v2.2.0/pkg/log/httpaccess/http_access.go (about)

     1  // Copyright 2022 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package httpaccess
     6  
     7  import (
     8  	"bufio"
     9  	"net"
    10  	"net/http"
    11  	"time"
    12  
    13  	"github.com/ethersphere/bee/v2/pkg/log"
    14  	"github.com/ethersphere/bee/v2/pkg/tracing"
    15  )
    16  
    17  // NewHTTPAccessSuppressLogHandler creates a
    18  // handler that will suppress access log messages.
    19  func NewHTTPAccessSuppressLogHandler() func(h http.Handler) http.Handler {
    20  	return func(h http.Handler) http.Handler {
    21  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    22  			if rr, ok := w.(*responseRecorder); ok {
    23  				w = rr.ResponseWriter
    24  			}
    25  			h.ServeHTTP(w, r)
    26  		})
    27  	}
    28  }
    29  
    30  // NewHTTPAccessLogHandler creates a handler that
    31  // will log a message after a request has been served.
    32  func NewHTTPAccessLogHandler(logger log.Logger, tracer *tracing.Tracer, message string) func(h http.Handler) http.Handler {
    33  	return func(h http.Handler) http.Handler {
    34  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    35  			rr, ok := w.(*responseRecorder)
    36  			if !ok { // No need to layer on another responseRecorder.
    37  				rr = &responseRecorder{ResponseWriter: w}
    38  			}
    39  
    40  			now := time.Now()
    41  			h.ServeHTTP(rr, r)
    42  			if logger.Verbosity() < log.VerbosityInfo {
    43  				return
    44  			}
    45  			duration := time.Since(now)
    46  
    47  			ctx, _ := tracer.WithContextFromHTTPHeaders(r.Context(), r.Header)
    48  
    49  			logger := tracing.NewLoggerWithTraceID(ctx, logger)
    50  
    51  			status := rr.status
    52  			if status == 0 {
    53  				status = http.StatusOK
    54  			}
    55  
    56  			ip, _, err := net.SplitHostPort(r.RemoteAddr)
    57  			if err != nil {
    58  				ip = r.RemoteAddr
    59  			}
    60  
    61  			fields := []interface{}{
    62  				"ip", ip,
    63  				"method", r.Method,
    64  				"host", r.Host,
    65  				"uri", r.RequestURI,
    66  				"proto", r.Proto,
    67  				"status", status,
    68  				"size", rr.size,
    69  				"duration", duration,
    70  			}
    71  			if v := r.Referer(); v != "" {
    72  				fields = append(fields, "referrer", v)
    73  			}
    74  			if v := r.UserAgent(); v != "" {
    75  				fields = append(fields, "user-agent", v)
    76  			}
    77  			if v := r.Header.Get("X-Forwarded-For"); v != "" {
    78  				fields = append(fields, "x-forwarded-for", v)
    79  			}
    80  			if v := r.Header.Get("X-Real-Ip"); v != "" {
    81  				fields = append(fields, "x-real-ip", v)
    82  			}
    83  
    84  			logger.WithValues(fields...).Build().Info(message)
    85  		})
    86  	}
    87  }
    88  
    89  // responseRecorder is an implementation of
    90  // http.ResponseWriter that records various metrics.
    91  type responseRecorder struct {
    92  	http.ResponseWriter
    93  
    94  	// Metrics.
    95  	status int
    96  	size   int
    97  }
    98  
    99  // Write implements http.ResponseWriter.
   100  func (rr *responseRecorder) Write(b []byte) (int, error) {
   101  	size, err := rr.ResponseWriter.Write(b)
   102  	rr.size += size
   103  	return size, err
   104  }
   105  
   106  // WriteHeader implements http.ResponseWriter.
   107  func (rr *responseRecorder) WriteHeader(s int) {
   108  	rr.ResponseWriter.WriteHeader(s)
   109  	if rr.status == 0 {
   110  		rr.status = s
   111  	}
   112  }
   113  
   114  // CloseNotify implements http.CloseNotifier.
   115  func (rr *responseRecorder) CloseNotify() <-chan bool {
   116  	// staticcheck SA1019 CloseNotifier interface is required by gorilla compress handler.
   117  	// nolint:staticcheck
   118  	return rr.ResponseWriter.(http.CloseNotifier).CloseNotify()
   119  }
   120  
   121  // Hijack implements http.Hijacker.
   122  func (rr *responseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) {
   123  	return rr.ResponseWriter.(http.Hijacker).Hijack()
   124  }
   125  
   126  // Flush implements http.Flusher.
   127  func (rr *responseRecorder) Flush() {
   128  	rr.ResponseWriter.(http.Flusher).Flush()
   129  }
   130  
   131  // Push implements http.Pusher.
   132  func (rr *responseRecorder) Push(target string, opts *http.PushOptions) error {
   133  	return rr.ResponseWriter.(http.Pusher).Push(target, opts)
   134  }