Skip to content

Instantly share code, notes, and snippets.

@Tantas
Forked from cespare/log.go
Last active September 8, 2021 18:22
Show Gist options
  • Save Tantas/1fc00c5eb7c291e2a34b to your computer and use it in GitHub Desktop.
Save Tantas/1fc00c5eb7c291e2a34b to your computer and use it in GitHub Desktop.
Apache access logs for golang.
import (
"fmt"
"io"
"net/http"
"strings"
"time"
)
// https://httpd.apache.org/docs/2.2/logs.html#combined + execution time.
const apacheFormatPattern = "%s - - [%s] \"%s %s %s\" %d %d \"%s\" \"%s\" %.4f\n"
type ApacheLogRecord struct {
http.ResponseWriter
ip string
time time.Time
method, uri, protocol string
status int
responseBytes int64
referer string
userAgent string
elapsedTime time.Duration
}
func (r *ApacheLogRecord) Log(out io.Writer) {
timeFormatted := r.time.Format("02/Jan/2006 03:04:05")
fmt.Fprintf(out, apacheFormatPattern, r.ip, timeFormatted, r.method,
r.uri, r.protocol, r.status, r.responseBytes, r.referer, r.userAgent,
r.elapsedTime.Seconds())
}
func (r *ApacheLogRecord) Write(p []byte) (int, error) {
written, err := r.ResponseWriter.Write(p)
r.responseBytes += int64(written)
return written, err
}
func (r *ApacheLogRecord) WriteHeader(status int) {
r.status = status
r.ResponseWriter.WriteHeader(status)
}
type ApacheLoggingHandler struct {
handler http.Handler
out io.Writer
}
func NewApacheLoggingHandler(handler http.Handler, out io.Writer) http.Handler {
return &ApacheLoggingHandler{
handler: handler,
out: out,
}
}
func (h *ApacheLoggingHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
clientIP := r.RemoteAddr
if colon := strings.LastIndex(clientIP, ":"); colon != -1 {
clientIP = clientIP[:colon]
}
referer := r.Referer()
if referer == "" {
referer = "-"
}
userAgent := r.UserAgent()
if userAgent == "" {
userAgent = "-"
}
record := &ApacheLogRecord{
ResponseWriter: rw,
ip: clientIP,
time: time.Time{},
method: r.Method,
uri: r.RequestURI,
protocol: r.Proto,
status: http.StatusOK,
referer: referer,
userAgent: userAgent,
elapsedTime: time.Duration(0),
}
startTime := time.Now()
h.handler.ServeHTTP(record, r)
finishTime := time.Now()
record.time = finishTime.UTC()
record.elapsedTime = finishTime.Sub(startTime)
record.Log(h.out)
}
func main() {
// Open appending or create the access log.
accessLogFile, err := os.OpenFile("access.log", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
if err != nil {
log.Fatalf("Error using assess.log: %s", err.Error())
}
defer accessLogFile.Close()
// Create a buffered writer and ensure it is flushed when an interrupt occurs.
bufferedAccessLogWriter := bufio.NewWriter(accessLogFile)
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
<-c
log.Println("Flushing logs and exiting...")
bufferedAccessLogWriter.Flush()
os.Exit(0)
}()
mux := http.DefaultServeMux
mux.HandleFunc("/", indexHandler)
loggingHandler := NewApacheLoggingHandler(mux, bufferedAccessLogWriter)
server := &http.Server{
Addr: ":8000",
Handler: loggingHandler,
}
server.ListenAndServe()
}
@Xubairlone90
Copy link

why does it show undefined index handler?
do we need to define it explicitly ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment