github.com/cs3org/reva/v2@v2.27.7/internal/http/interceptors/log/log.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package log 20 21 import ( 22 "bufio" 23 "fmt" 24 "net" 25 "net/http" 26 "net/url" 27 "time" 28 29 "github.com/cs3org/reva/v2/pkg/appctx" 30 "github.com/rs/zerolog" 31 ) 32 33 // New returns a new HTTP middleware that logs HTTP requests and responses. 34 // TODO(labkode): maybe log to another file? 35 func New() func(http.Handler) http.Handler { 36 return handler 37 } 38 39 // handler is a logging middleware 40 func handler(h http.Handler) http.Handler { 41 return newLoggingHandler(h) 42 } 43 44 func newLoggingHandler(h http.Handler) http.Handler { 45 return loggingHandler{handler: h} 46 } 47 48 type loggingHandler struct { 49 handler http.Handler 50 } 51 52 func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { 53 log := appctx.GetLogger(req.Context()) 54 t := time.Now() 55 logger := makeLogger(w) 56 url := *req.URL 57 h.handler.ServeHTTP(logger, req) 58 writeLog(log, req, url, t, logger.Status(), logger.Size()) 59 } 60 61 func makeLogger(w http.ResponseWriter) loggingResponseWriter { 62 var logger loggingResponseWriter = &responseLogger{w: w, status: http.StatusOK} 63 if _, ok := w.(http.Hijacker); ok { 64 logger = &hijackLogger{responseLogger{w: w, status: http.StatusOK}} 65 } 66 h, ok1 := logger.(http.Hijacker) 67 c, ok2 := w.(http.CloseNotifier) 68 if ok1 && ok2 { 69 return hijackCloseNotifier{logger, h, c} 70 } 71 if ok2 { 72 return &closeNotifyWriter{logger, c} 73 } 74 return logger 75 } 76 77 func writeLog(log *zerolog.Logger, req *http.Request, url url.URL, ts time.Time, status, size int) { 78 end := time.Now() 79 host, _, err := net.SplitHostPort(req.RemoteAddr) 80 81 if err != nil { 82 host = req.RemoteAddr 83 } 84 85 uri := req.RequestURI 86 u := req.URL.String() 87 88 if req.ProtoMajor == 2 && req.Method == "CONNECT" { 89 uri = req.Host 90 } 91 if uri == "" { 92 uri = url.RequestURI() 93 } 94 95 diff := end.Sub(ts).Nanoseconds() 96 97 var event *zerolog.Event 98 switch { 99 case status < 400: 100 event = log.Debug() 101 case status < 500: 102 event = log.Warn() 103 default: 104 event = log.Error() 105 } 106 107 event.Str("host", host).Str("method", req.Method). 108 Str("uri", uri).Str("url", u).Str("proto", req.Proto).Int("status", status). 109 Int("size", size). 110 Str("start", ts.Format("02/Jan/2006:15:04:05 -0700")). 111 Str("end", end.Format("02/Jan/2006:15:04:05 -0700")).Int("time_ns", int(diff)). 112 Msg("http") 113 } 114 115 type loggingResponseWriter interface { 116 commonLoggingResponseWriter 117 http.Pusher 118 } 119 120 func (l *responseLogger) Push(target string, opts *http.PushOptions) error { 121 p, ok := l.w.(http.Pusher) 122 if !ok { 123 return fmt.Errorf("responseLogger does not implement http.Pusher") 124 } 125 return p.Push(target, opts) 126 } 127 128 type commonLoggingResponseWriter interface { 129 http.ResponseWriter 130 http.Flusher 131 Status() int 132 Size() int 133 } 134 135 // responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP 136 // status code and body size 137 type responseLogger struct { 138 w http.ResponseWriter 139 status int 140 size int 141 } 142 143 func (l *responseLogger) Header() http.Header { 144 return l.w.Header() 145 } 146 147 func (l *responseLogger) Write(b []byte) (int, error) { 148 size, err := l.w.Write(b) 149 l.size += size 150 return size, err 151 } 152 153 func (l *responseLogger) WriteHeader(s int) { 154 l.w.WriteHeader(s) 155 l.status = s 156 } 157 158 func (l *responseLogger) Status() int { 159 return l.status 160 } 161 162 func (l *responseLogger) Size() int { 163 return l.size 164 } 165 166 func (l *responseLogger) Flush() { 167 f, ok := l.w.(http.Flusher) 168 if ok { 169 f.Flush() 170 } 171 } 172 173 type hijackLogger struct { 174 responseLogger 175 } 176 177 func (l *hijackLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) { 178 h := l.responseLogger.w.(http.Hijacker) 179 conn, rw, err := h.Hijack() 180 if err == nil && l.responseLogger.status == 0 { 181 l.responseLogger.status = http.StatusSwitchingProtocols 182 } 183 return conn, rw, err 184 } 185 186 type closeNotifyWriter struct { 187 loggingResponseWriter 188 http.CloseNotifier 189 } 190 191 type hijackCloseNotifier struct { 192 loggingResponseWriter 193 http.Hijacker 194 http.CloseNotifier 195 }