github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/website/logging.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "log" 6 "net/http" 7 "os" 8 "strings" 9 "time" 10 ) 11 12 type logRecord struct { 13 http.ResponseWriter 14 15 time time.Time 16 ip, method, rawpath string 17 responseBytes int64 18 responseStatus int 19 userAgent, referer string 20 proto string // "HTTP/1.1" 21 } 22 23 type logHandler struct { 24 ch chan *logRecord 25 handler http.Handler 26 27 dir string // or "" to not log 28 stdout bool 29 } 30 31 func NewLoggingHandler(handler http.Handler, dir string, writeStdout bool) http.Handler { 32 h := &logHandler{ 33 ch: make(chan *logRecord, 1000), 34 dir: dir, 35 handler: handler, 36 stdout: writeStdout, 37 } 38 go h.logFromChannel() 39 return h 40 } 41 42 func (h *logHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 43 // Strip port number from address 44 addr := r.RemoteAddr 45 if colon := strings.LastIndex(addr, ":"); colon != -1 { 46 addr = addr[:colon] 47 } 48 49 lr := &logRecord{ 50 time: time.Now().UTC(), 51 ip: addr, 52 method: r.Method, 53 rawpath: r.URL.RequestURI(), 54 userAgent: r.UserAgent(), 55 referer: r.Referer(), 56 responseStatus: http.StatusOK, 57 proto: r.Proto, 58 ResponseWriter: rw, 59 } 60 h.handler.ServeHTTP(lr, r) 61 h.ch <- lr 62 } 63 64 var monthAbbr = [12]string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", 65 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} 66 67 func (h *logHandler) logFromChannel() { 68 lastFileName := "" 69 var logFile *os.File 70 for { 71 lr := <-h.ch 72 73 // [10/Oct/2000:13:55:36 -0700] 74 dateString := fmt.Sprintf("%02d/%s/%04d:%02d:%02d:%02d -0000", 75 lr.time.Day(), 76 monthAbbr[lr.time.Month()-1], 77 lr.time.Year(), 78 lr.time.Hour(), lr.time.Minute(), lr.time.Second()) 79 80 if h.dir != "" { 81 fileName := fmt.Sprintf("%s/%04d-%02d-%02d%s%02d.log", h.dir, 82 lr.time.Year(), lr.time.Month(), lr.time.Day(), "h", lr.time.Hour()) 83 if fileName > lastFileName { 84 if logFile != nil { 85 logFile.Close() 86 } 87 var err error 88 logFile, err = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) 89 if err != nil { 90 log.Printf("Error opening %q: %v", fileName, err) 91 continue 92 } 93 lastFileName = fileName 94 } 95 } 96 97 // Combined Log Format 98 // http://httpd.apache.org/docs/1.3/logs.html#combined 99 logLine := fmt.Sprintf("%s - - [%s] %q %d %d %q %q\n", 100 lr.ip, 101 dateString, 102 lr.method+" "+lr.rawpath+" "+lr.proto, 103 lr.responseStatus, 104 lr.responseBytes, 105 lr.referer, 106 lr.userAgent, 107 ) 108 if h.stdout { 109 os.Stdout.WriteString(logLine) 110 } 111 if logFile != nil { 112 logFile.WriteString(logLine) 113 } 114 } 115 } 116 117 func (lr *logRecord) Write(p []byte) (int, error) { 118 written, err := lr.ResponseWriter.Write(p) 119 lr.responseBytes += int64(written) 120 return written, err 121 } 122 123 func (lr *logRecord) WriteHeader(status int) { 124 lr.responseStatus = status 125 lr.ResponseWriter.WriteHeader(status) 126 }