github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/httpsnoop/README.md (about)

     1  # httpsnoop
     2  
     3  Package httpsnoop provides an easy way to capture http related metrics (i.e.
     4  response time, bytes written, and http status code) from your application's
     5  http.Handlers.
     6  
     7  Doing this requires non-trivial wrapping of the http.ResponseWriter interface,
     8  which is also exposed for users interested in a more low-level API.
     9  
    10  [![GoDoc](https://godoc.org/github.com/felixge/httpsnoop?status.svg)](https://godoc.org/github.com/felixge/httpsnoop)
    11  [![Build Status](https://travis-ci.org/felixge/httpsnoop.svg?branch=master)](https://travis-ci.org/felixge/httpsnoop)
    12  
    13  ## Usage Example
    14  
    15  ```go
    16  // myH is your app's http handler, perhaps a http.ServeMux or similar.
    17  var myH http.Handler
    18  // wrappedH wraps myH in order to log every request.
    19  wrappedH := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    20  	m := httpsnoop.CaptureMetrics(myH, w, r)
    21  	log.Printf(
    22  		"%s %s (code=%d dt=%s written=%d)",
    23  		r.Method,
    24  		r.URL,
    25  		m.Code,
    26  		m.Duration,
    27  		m.Written,
    28  	)
    29  })
    30  http.ListenAndServe(":8080", wrappedH)
    31  ```
    32  
    33  ## Why this package exists
    34  
    35  Instrumenting an application's http.Handler is surprisingly difficult.
    36  
    37  However if you google for e.g. "capture ResponseWriter status code" you'll find
    38  lots of advise and code examples that suggest it to be a fairly trivial
    39  undertaking. Unfortunately everything I've seen so far has a high chance of
    40  breaking your application.
    41  
    42  The main problem is that a `http.ResponseWriter` often implements additional
    43  interfaces such as `http.Flusher`, `http.CloseNotifier`, `http.Hijacker`, `http.Pusher`, and
    44  `io.ReaderFrom`. So the naive approach of just wrapping `http.ResponseWriter`
    45  in your own struct that also implements the `http.ResponseWriter` interface
    46  will hide the additional interfaces mentioned above. This has a high change of
    47  introducing subtle bugs into any non-trivial application.
    48  
    49  Another approach I've seen people take is to return a struct that implements
    50  all of the interfaces above. However, that's also problematic, because it's
    51  difficult to fake some of these interfaces behaviors when the underlying
    52  `http.ResponseWriter` doesn't have an implementation. It's also dangerous,
    53  because an application may choose to operate differently, merely because it
    54  detects the presence of these additional interfaces.
    55  
    56  This package solves this problem by checking which additional interfaces a
    57  `http.ResponseWriter` implements, returning a wrapped version implementing the
    58  exact same set of interfaces.
    59  
    60  Additionally this package properly handles edge cases such as `WriteHeader` not
    61  being called, or called more than once, as well as concurrent calls to
    62  `http.ResponseWriter` methods, and even calls happening after the wrapped
    63  `ServeHTTP` has already returned.
    64  
    65  Unfortunately this package is not perfect either. It's possible that it is
    66  still missing some interfaces provided by the go core (let me know if you find
    67  one), and it won't work for applications adding their own interfaces into the
    68  mix. You can however use `httpsnoop.Unwrap(w)` to access the underlying
    69  `http.ResponseWriter` and type-assert the result to its other interfaces.
    70  
    71  However, hopefully the explanation above has sufficiently scared you of rolling
    72  your own solution to this problem. httpsnoop may still break your application,
    73  but at least it tries to avoid it as much as possible.
    74  
    75  Anyway, the real problem here is that smuggling additional interfaces inside
    76  `http.ResponseWriter` is a problematic design choice, but it probably goes as
    77  deep as the Go language specification itself. But that's okay, I still prefer
    78  Go over the alternatives ;).
    79  
    80  ## Performance
    81  
    82  ```
    83  BenchmarkBaseline-8      	   20000	     94912 ns/op
    84  BenchmarkCaptureMetrics-8	   20000	     95461 ns/op
    85  ```
    86  
    87  As you can see, using `CaptureMetrics` on a vanilla http.Handler introduces an
    88  overhead of ~500 ns per http request on my machine. However, the margin of
    89  error appears to be larger than that, therefor it should be reasonable to
    90  assume that the overhead introduced by `CaptureMetrics` is absolutely
    91  negligible.
    92  
    93  ## License
    94  
    95  MIT