package logger import ( "fmt" "net/http" "os" "time" "golang-fave/engine/consts" "golang-fave/engine/domains" "golang-fave/engine/utils" ) type logMsg struct { host string message string isError bool } type Logger struct { wwwDir string cdata chan logMsg cclose chan bool } func (this *Logger) console(msg *logMsg) { if consts.ParamDebug { if !msg.isError { if consts.IS_WIN { fmt.Fprintln(os.Stdout, "[ACCESS] "+msg.message) } else { fmt.Fprintln(os.Stdout, "\033[0;32m[ACCESS] "+msg.message+"\033[0m") } } else { if consts.IS_WIN { fmt.Fprintln(os.Stdout, "[ERROR] "+msg.message) } else { fmt.Fprintln(os.Stdout, "\033[0;31m[ERROR] "+msg.message+"\033[0m") } } return } if !msg.isError { fmt.Fprintln(os.Stdout, msg.message) } else { fmt.Fprintln(os.Stderr, msg.message) } } func (this *Logger) write(msg *logMsg) { // Ignore file if debug if consts.ParamDebug { this.console(msg) return } // Ignore file if host not set if msg.host == "" { this.console(msg) return } // Ignore file if www dir is not exists if !utils.IsDirExists(this.wwwDir) { this.console(msg) return } // Extract host host, _ := utils.ExtractHostPort(msg.host, false) // --- TODO: optimize this later curr_host := host doms := domains.New(this.wwwDir) if mhost := doms.GetHost(host); mhost != "" { curr_host = mhost } // --- logs_dir := this.wwwDir + string(os.PathSeparator) + curr_host + string(os.PathSeparator) + "logs" // Try use localhost folder for logs if !utils.IsDirExists(logs_dir) { logs_dir = this.wwwDir + string(os.PathSeparator) + "localhost" + string(os.PathSeparator) + "logs" } // Ignore file if logs dir is not exists if !utils.IsDirExists(logs_dir) { this.console(msg) return } // Detect which log file log_file := logs_dir + string(os.PathSeparator) + "access.log" if msg.isError { log_file = logs_dir + string(os.PathSeparator) + "error.log" } // Try write to file f, err := os.OpenFile(log_file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err == nil { defer f.Close() fmt.Fprintln(f, msg.message) return } // By default this.console(msg) } func New() *Logger { // Logs channel cdata := make(chan logMsg) // Close channel cclose := make(chan bool) // Init logger pointer lg := Logger{cdata: cdata, cclose: cclose} // Write log string in background go func() { for { select { case msg := <-cdata: lg.write(&msg) case <-cclose: cclose <- true return } } }() return &lg } func (this *Logger) Log(msg string, r *http.Request, isError bool, vars ...interface{}) { var host string = "" if r != nil { host = r.Host } if len(vars) > 0 { msg = fmt.Sprintf(msg, vars...) } select { case <-r.Context().Done(): return case this.cdata <- logMsg{host, msg, isError}: return case <-time.After(1 * time.Second): fmt.Printf("Logger, can't send msg (overflow): %s, %s, %v\n", host, msg, isError) return } } func (this *Logger) SetWwwDir(dir string) { this.wwwDir = dir } func (this *Logger) Handler(h http.Handler) http.Handler { return handler{ h: h, w: os.Stdout, c: this.cdata, } } func (this *Logger) Close() { this.cclose <- true <-this.cclose }