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  }