package main import ( "encoding/json" "log" "net/http" "os" "path/filepath" "strings" "time" ) // some headers not worth logging var ( hdrsToNotLog = []string{ "Accept-Language", "Cache-Control", "Cf-Ray", "CF-Visitor", "CF-Connecting-IP", "Cdn-Loop", "Cookie", "Connection", "Dnt", "If-Modified-Since", "Sec-Fetch-Dest", "Sec-Ch-Ua-Mobile", // "Sec-Ch-Ua", "Sec-Ch-Ua-Platform", "Sec-Fetch-Site", "Sec-Fetch-Mode", "Sec-Fetch-User", "Upgrade-Insecure-Requests", "X-Request-Start", "X-Forwarded-For", "X-Forwarded-Proto", "X-Forwarded-Host", } hdrsToNotLogMap map[string]bool ) type LogFile struct { handle *os.File logger *log.Logger path string } type LogFileRec struct { Method string `json:"method"` IpAddr string `json:"ipAddr"` Url string `json:"url"` } func newFileLogger(path string) (*LogFile, error) { requestedFile := filepath.Clean(filepath.Join("/", path)) parentDir := filepath.Dir(requestedFile) err := os.MkdirAll(parentDir, 0777) if err != nil { return nil, err } f, err := os.OpenFile(requestedFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) if err != nil { return nil, err } return &LogFile{ handle: f, logger: log.New(f, "", 0), path: path, }, nil } func (f *LogFile) Close() error { if f == nil { return nil } err := f.handle.Close() f.handle = nil return err } func extractFirstFragment(header *http.Header, headerName string) string { s := header.Get(headerName) if len(strings.TrimSpace(s)) == 0 { return s } fragments := strings.Split(s, ",") return strings.TrimSpace(fragments[0]) } // Get Ip Address of the client func extractIpAddress(r *http.Request) string { var ipAddr string if r == nil { return "" } possibleIpHeaders := []string{"CF-Connecting-IP", "X-Real-Ip", "X-Forwarded-For"} for _, header := range possibleIpHeaders { ipAddr = extractFirstFragment(&r.Header, header) if len(strings.TrimSpace(ipAddr)) != 0 { return ipAddr } } // pull ip from Request.RemoteAddr if len(strings.TrimSpace(r.RemoteAddr)) != 0 { index := strings.LastIndex(r.RemoteAddr, ";") if index == -1 { return r.RemoteAddr } ipAddr = r.RemoteAddr[:index] } return ipAddr } func canSkipExtraHeaders(r *http.Request) bool { ref := r.Header.Get("Referer") if len(strings.TrimSpace(ref)) == 0 { return false } return strings.Contains(ref, r.Host) } func shouldLogHeader(s string) bool { if hdrsToNotLogMap == nil { hdrsToNotLogMap = map[string]bool{} for _, h := range hdrsToNotLog { h = strings.ToLower(h) hdrsToNotLogMap[h] = true } } s = strings.ToLower(s) return !hdrsToNotLogMap[s] } func getCurrentDate() string { dt := time.Now() return dt.Format(time.RFC3339Nano) } func (f *LogFile) WriteLog(r *http.Request) error { if f == nil { return nil } var rec = make(map[string]string) rec["method"] = r.Method rec["requestUri"] = r.RequestURI rec["Host"] = r.Host rec["ipAddr"] = extractIpAddress(r) rec["requestDate"] = getCurrentDate() if !canSkipExtraHeaders(r) { for key, val := range r.Header { if shouldLogHeader(key) && len(val) > 0 { rec[key] = val[0] } } } b, err := json.Marshal(rec) if err != nil { return err } f.logger.Println(string(b)) return nil }