Skip to content

Commit 0d8ed6d

Browse files
rohitsakalabrandond
authored andcommitted
Copy logging handler from gorilla mux repo
1 parent faa12a3 commit 0d8ed6d

1 file changed

Lines changed: 185 additions & 0 deletions

File tree

pkg/server/logging.go

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// Copyright 2013 The Gorilla 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 server
6+
7+
import (
8+
"io"
9+
"net"
10+
"net/http"
11+
"net/url"
12+
"strconv"
13+
"time"
14+
"unicode/utf8"
15+
)
16+
17+
// loggingHandler is the http.Handler implementation for LoggingHandler.
18+
type loggingHandler struct {
19+
writer io.Writer
20+
handler http.Handler
21+
}
22+
23+
func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
24+
t := time.Now()
25+
logger := &responseLogger{w: w, status: http.StatusOK}
26+
url := *req.URL
27+
28+
h.handler.ServeHTTP(logger, req)
29+
if req.MultipartForm != nil {
30+
req.MultipartForm.RemoveAll()
31+
}
32+
33+
writeLog(h.writer, req, url, t, logger.status, logger.size)
34+
}
35+
36+
// LoggingHandler returns a http.Handler that wraps h and logs requests to out in
37+
// Apache Common Log Format (CLF).
38+
//
39+
// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format.
40+
func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
41+
return loggingHandler{out, h}
42+
}
43+
44+
// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP
45+
// status code and body size.
46+
type responseLogger struct {
47+
w http.ResponseWriter
48+
status int
49+
size int
50+
}
51+
52+
func (l *responseLogger) Header() http.Header {
53+
return l.w.Header()
54+
}
55+
56+
func (l *responseLogger) Write(b []byte) (int, error) {
57+
size, err := l.w.Write(b)
58+
l.size += size
59+
return size, err
60+
}
61+
62+
func (l *responseLogger) WriteHeader(s int) {
63+
l.w.WriteHeader(s)
64+
l.status = s
65+
}
66+
67+
func (l *responseLogger) Flush() {
68+
if f, ok := l.w.(http.Flusher); ok {
69+
f.Flush()
70+
}
71+
}
72+
73+
const lowerhex = "0123456789abcdef"
74+
75+
func appendQuoted(buf []byte, s string) []byte {
76+
var runeTmp [utf8.UTFMax]byte
77+
for width := 0; len(s) > 0; s = s[width:] {
78+
r := rune(s[0])
79+
width = 1
80+
if r >= utf8.RuneSelf {
81+
r, width = utf8.DecodeRuneInString(s)
82+
}
83+
if width == 1 && r == utf8.RuneError {
84+
buf = append(buf, `\x`...)
85+
buf = append(buf, lowerhex[s[0]>>4])
86+
buf = append(buf, lowerhex[s[0]&0xF])
87+
continue
88+
}
89+
if r == rune('"') || r == '\\' {
90+
buf = append(buf, '\\')
91+
buf = append(buf, byte(r))
92+
continue
93+
}
94+
if strconv.IsPrint(r) {
95+
n := utf8.EncodeRune(runeTmp[:], r)
96+
buf = append(buf, runeTmp[:n]...)
97+
continue
98+
}
99+
switch r {
100+
case '\a':
101+
buf = append(buf, `\a`...)
102+
case '\b':
103+
buf = append(buf, `\b`...)
104+
case '\f':
105+
buf = append(buf, `\f`...)
106+
case '\n':
107+
buf = append(buf, `\n`...)
108+
case '\r':
109+
buf = append(buf, `\r`...)
110+
case '\t':
111+
buf = append(buf, `\t`...)
112+
case '\v':
113+
buf = append(buf, `\v`...)
114+
default:
115+
switch {
116+
case r < ' ':
117+
buf = append(buf, `\x`...)
118+
buf = append(buf, lowerhex[s[0]>>4])
119+
buf = append(buf, lowerhex[s[0]&0xF])
120+
case r > utf8.MaxRune:
121+
r = 0xFFFD
122+
fallthrough
123+
case r < 0x10000:
124+
buf = append(buf, `\u`...)
125+
for s := 12; s >= 0; s -= 4 {
126+
buf = append(buf, lowerhex[r>>uint(s)&0xF])
127+
}
128+
default:
129+
buf = append(buf, `\U`...)
130+
for s := 28; s >= 0; s -= 4 {
131+
buf = append(buf, lowerhex[r>>uint(s)&0xF])
132+
}
133+
}
134+
}
135+
}
136+
return buf
137+
}
138+
139+
// buildCommonLogLine builds a log entry for req in Apache Common Log Format.
140+
func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte {
141+
username := "-"
142+
if url.User != nil {
143+
if name := url.User.Username(); name != "" {
144+
username = name
145+
}
146+
}
147+
148+
host, _, err := net.SplitHostPort(req.RemoteAddr)
149+
if err != nil {
150+
host = req.RemoteAddr
151+
}
152+
153+
uri := req.RequestURI
154+
if req.ProtoMajor == 2 && req.Method == "CONNECT" {
155+
uri = req.Host
156+
}
157+
if uri == "" {
158+
uri = url.RequestURI()
159+
}
160+
161+
buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2)
162+
buf = append(buf, host...)
163+
buf = append(buf, " - "...)
164+
buf = append(buf, username...)
165+
buf = append(buf, " ["...)
166+
buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...)
167+
buf = append(buf, `] "`...)
168+
buf = append(buf, req.Method...)
169+
buf = append(buf, " "...)
170+
buf = appendQuoted(buf, uri)
171+
buf = append(buf, " "...)
172+
buf = append(buf, req.Proto...)
173+
buf = append(buf, `" `...)
174+
buf = append(buf, strconv.Itoa(status)...)
175+
buf = append(buf, " "...)
176+
buf = append(buf, strconv.Itoa(size)...)
177+
return buf
178+
}
179+
180+
// writeLog writes a log entry for req to w in Apache Common Log Format.
181+
func writeLog(writer io.Writer, req *http.Request, url url.URL, ts time.Time, status int, size int) {
182+
buf := buildCommonLogLine(req, url, ts, status, size)
183+
buf = append(buf, '\n')
184+
writer.Write(buf)
185+
}

0 commit comments

Comments
 (0)