2011-01-29 19:53:22 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2011-01-29 20:31:35 +00:00
|
|
|
"log"
|
2012-02-21 10:11:28 +00:00
|
|
|
"net/http"
|
2011-01-29 19:53:22 +00:00
|
|
|
"os"
|
2011-01-29 20:17:59 +00:00
|
|
|
"strings"
|
2011-01-29 19:53:22 +00:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type logRecord struct {
|
2011-02-08 21:29:53 +00:00
|
|
|
http.ResponseWriter
|
|
|
|
|
2012-02-21 10:11:28 +00:00
|
|
|
time time.Time
|
2011-01-29 19:53:22 +00:00
|
|
|
ip, method, rawpath string
|
|
|
|
responseBytes int64
|
|
|
|
responseStatus int
|
|
|
|
userAgent, referer string
|
2011-01-29 20:17:59 +00:00
|
|
|
proto string // "HTTP/1.1"
|
2011-01-29 19:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type logHandler struct {
|
|
|
|
ch chan *logRecord
|
|
|
|
handler http.Handler
|
2011-01-29 20:31:35 +00:00
|
|
|
|
|
|
|
dir string // or "" to not log
|
|
|
|
stdout bool
|
2011-01-29 19:53:22 +00:00
|
|
|
}
|
|
|
|
|
2011-01-29 20:31:35 +00:00
|
|
|
func NewLoggingHandler(handler http.Handler, dir string, writeStdout bool) http.Handler {
|
2011-01-29 19:53:22 +00:00
|
|
|
h := &logHandler{
|
2011-01-29 20:17:59 +00:00
|
|
|
ch: make(chan *logRecord, 1000),
|
2011-01-29 19:53:22 +00:00
|
|
|
dir: dir,
|
|
|
|
handler: handler,
|
2011-01-29 20:31:35 +00:00
|
|
|
stdout: writeStdout,
|
2011-01-29 19:53:22 +00:00
|
|
|
}
|
|
|
|
go h.logFromChannel()
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *logHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
2011-01-29 20:17:59 +00:00
|
|
|
// Strip port number from address
|
2011-03-12 20:32:40 +00:00
|
|
|
addr := r.RemoteAddr
|
2011-01-29 20:17:59 +00:00
|
|
|
if colon := strings.LastIndex(addr, ":"); colon != -1 {
|
|
|
|
addr = addr[:colon]
|
|
|
|
}
|
|
|
|
|
2011-01-29 19:53:22 +00:00
|
|
|
lr := &logRecord{
|
2012-02-21 10:11:28 +00:00
|
|
|
time: time.Now().UTC(),
|
2011-01-29 20:17:59 +00:00
|
|
|
ip: addr,
|
2011-01-29 19:53:22 +00:00
|
|
|
method: r.Method,
|
2012-02-21 10:11:28 +00:00
|
|
|
rawpath: r.URL.RequestURI(),
|
2011-06-17 01:38:21 +00:00
|
|
|
userAgent: r.UserAgent(),
|
|
|
|
referer: r.Referer(),
|
2011-01-29 19:53:22 +00:00
|
|
|
responseStatus: http.StatusOK,
|
2011-01-29 20:17:59 +00:00
|
|
|
proto: r.Proto,
|
2011-02-08 21:29:53 +00:00
|
|
|
ResponseWriter: rw,
|
2011-01-29 19:53:22 +00:00
|
|
|
}
|
|
|
|
h.handler.ServeHTTP(lr, r)
|
|
|
|
h.ch <- lr
|
|
|
|
}
|
|
|
|
|
2011-01-29 20:17:59 +00:00
|
|
|
var monthAbbr = [12]string{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
|
|
|
|
|
2011-01-29 19:53:22 +00:00
|
|
|
func (h *logHandler) logFromChannel() {
|
2011-01-29 20:31:35 +00:00
|
|
|
lastFileName := ""
|
|
|
|
var logFile *os.File
|
2011-01-29 19:53:22 +00:00
|
|
|
for {
|
|
|
|
lr := <-h.ch
|
2011-01-29 20:17:59 +00:00
|
|
|
|
|
|
|
// [10/Oct/2000:13:55:36 -0700]
|
|
|
|
dateString := fmt.Sprintf("%02d/%s/%04d:%02d:%02d:%02d -0000",
|
2012-02-21 10:11:28 +00:00
|
|
|
lr.time.Day(),
|
|
|
|
monthAbbr[lr.time.Month()-1],
|
|
|
|
lr.time.Year(),
|
|
|
|
lr.time.Hour(), lr.time.Minute(), lr.time.Second())
|
2011-01-29 20:17:59 +00:00
|
|
|
|
2011-01-29 20:31:35 +00:00
|
|
|
if h.dir != "" {
|
|
|
|
fileName := fmt.Sprintf("%s/%04d-%02d-%02d%s%02d.log", h.dir,
|
2012-02-21 10:11:28 +00:00
|
|
|
lr.time.Year(), lr.time.Month(), lr.time.Day(), "h", lr.time.Hour())
|
2011-01-29 20:31:35 +00:00
|
|
|
if fileName > lastFileName {
|
|
|
|
if logFile != nil {
|
|
|
|
logFile.Close()
|
|
|
|
}
|
2012-02-21 10:11:28 +00:00
|
|
|
var err error
|
2011-04-07 17:58:29 +00:00
|
|
|
logFile, err = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
|
2011-01-29 20:31:35 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("Error opening %q: %v", fileName, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
lastFileName = fileName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-29 20:17:59 +00:00
|
|
|
// Combined Log Format
|
|
|
|
// http://httpd.apache.org/docs/1.3/logs.html#combined
|
|
|
|
logLine := fmt.Sprintf("%s - - [%s] %q %d %d %q %q\n",
|
|
|
|
lr.ip,
|
|
|
|
dateString,
|
|
|
|
lr.method+" "+lr.rawpath+" "+lr.proto,
|
|
|
|
lr.responseStatus,
|
|
|
|
lr.responseBytes,
|
|
|
|
lr.referer,
|
|
|
|
lr.userAgent,
|
|
|
|
)
|
2011-01-29 20:31:35 +00:00
|
|
|
if h.stdout {
|
2011-01-29 19:53:22 +00:00
|
|
|
os.Stdout.WriteString(logLine)
|
|
|
|
}
|
2011-01-29 20:31:35 +00:00
|
|
|
if logFile != nil {
|
|
|
|
logFile.WriteString(logLine)
|
|
|
|
}
|
2011-01-29 19:53:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-21 10:11:28 +00:00
|
|
|
func (lr *logRecord) Write(p []byte) (int, error) {
|
2011-02-08 21:29:53 +00:00
|
|
|
written, err := lr.ResponseWriter.Write(p)
|
2011-01-29 19:53:22 +00:00
|
|
|
lr.responseBytes += int64(written)
|
|
|
|
return written, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (lr *logRecord) WriteHeader(status int) {
|
|
|
|
lr.responseStatus = status
|
2011-02-08 21:29:53 +00:00
|
|
|
lr.ResponseWriter.WriteHeader(status)
|
2011-01-29 19:53:22 +00:00
|
|
|
}
|