2015-01-21 20:58:06 +00:00
|
|
|
package httpd
|
|
|
|
|
|
|
|
import (
|
2015-01-21 23:00:44 +00:00
|
|
|
"encoding/base64"
|
2015-01-21 20:58:06 +00:00
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
2015-01-21 23:14:51 +00:00
|
|
|
"strconv"
|
2015-01-21 23:00:44 +00:00
|
|
|
"strings"
|
2015-01-21 20:58:06 +00:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type loggingResponseWriter interface {
|
|
|
|
http.ResponseWriter
|
|
|
|
Status() int
|
|
|
|
Size() int
|
|
|
|
}
|
|
|
|
|
|
|
|
// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status
|
|
|
|
// code and body size
|
|
|
|
type responseLogger struct {
|
|
|
|
w http.ResponseWriter
|
|
|
|
status int
|
|
|
|
size int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *responseLogger) Header() http.Header {
|
|
|
|
return l.w.Header()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *responseLogger) Write(b []byte) (int, error) {
|
|
|
|
if l.status == 0 {
|
2015-01-21 21:07:30 +00:00
|
|
|
// Set status if WriteHeader has not been called
|
2015-01-21 20:58:06 +00:00
|
|
|
l.status = http.StatusOK
|
|
|
|
}
|
|
|
|
size, err := l.w.Write(b)
|
|
|
|
l.size += size
|
|
|
|
return size, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *responseLogger) WriteHeader(s int) {
|
|
|
|
l.w.WriteHeader(s)
|
|
|
|
l.status = s
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *responseLogger) Status() int {
|
|
|
|
return l.status
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *responseLogger) Size() int {
|
|
|
|
return l.size
|
|
|
|
}
|
|
|
|
|
|
|
|
// Common Log Format: http://en.wikipedia.org/wiki/Common_Log_Format
|
|
|
|
|
2015-01-21 22:36:50 +00:00
|
|
|
// buildLogLine creates a common log format
|
|
|
|
// in addittion to the common fields, we also append referrer, user agent and request ID
|
2015-01-21 20:58:06 +00:00
|
|
|
func buildLogLine(l *responseLogger, r *http.Request, start time.Time) string {
|
2015-01-21 23:14:51 +00:00
|
|
|
username := parseUsername(r)
|
|
|
|
|
|
|
|
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
host = r.RemoteAddr
|
|
|
|
}
|
|
|
|
|
|
|
|
uri := r.URL.RequestURI()
|
|
|
|
|
|
|
|
fields := []string{
|
|
|
|
host,
|
|
|
|
"-",
|
|
|
|
username,
|
|
|
|
fmt.Sprintf("[%s]", start.Format("02/Jan/2006:15:04:05 -0700")),
|
|
|
|
r.Method,
|
|
|
|
uri,
|
|
|
|
r.Proto,
|
|
|
|
strconv.Itoa(l.Status()),
|
|
|
|
strconv.Itoa(l.Size()),
|
|
|
|
r.Referer(),
|
|
|
|
r.UserAgent(),
|
|
|
|
r.Header.Get("Request-Id"),
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Join(fields, " ")
|
|
|
|
}
|
|
|
|
|
|
|
|
// parses the uesrname either from the url or auth header
|
|
|
|
func parseUsername(r *http.Request) string {
|
2015-01-21 20:58:06 +00:00
|
|
|
username := "-"
|
2015-01-21 23:14:51 +00:00
|
|
|
|
2015-01-21 20:58:06 +00:00
|
|
|
url := r.URL
|
2015-01-21 23:00:44 +00:00
|
|
|
|
|
|
|
// get username from the url if passed there
|
2015-01-21 20:58:06 +00:00
|
|
|
if url.User != nil {
|
|
|
|
if name := url.User.Username(); name != "" {
|
|
|
|
username = name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-21 23:00:44 +00:00
|
|
|
// Try to get it from the authorization header if set there
|
|
|
|
if username == "-" {
|
|
|
|
auth := r.Header.Get("Authorization")
|
|
|
|
fields := strings.Split(auth, " ")
|
|
|
|
if len(fields) == 2 {
|
|
|
|
bs, err := base64.StdEncoding.DecodeString(fields[1])
|
|
|
|
if err == nil {
|
|
|
|
fields = strings.Split(string(bs), ":")
|
|
|
|
if len(fields) >= 1 {
|
|
|
|
username = fields[0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-01-21 23:14:51 +00:00
|
|
|
return username
|
2015-01-21 20:58:06 +00:00
|
|
|
}
|