// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. package log import ( "bytes" "encoding/json" "fmt" "log" "os" "path/filepath" "runtime" "runtime/debug" "strings" "sync" "time" "qoobing.com/gomod/str" ) var ( glogger *log.Logger = nil once sync.Once = sync.Once{} mylogger *Logger = nil modulelogs map[string]*Logger = map[string]*Logger{} logidSetter func(string) = nil logidGetter func() string = nil ) const ErrLogPanic = "~~~~panic~~~~~~" //////////////global log//////////////////begin/////////////////// func SetLogLevel(newlv logLevel) (oldlv logLevel) { return mylogger.SetLogLevel(newlv) } func PrintPretty(prefix string, v interface{}) { mylogger.PrintPretty(prefix, v) } func DebugfWithDepth(calldepth int, format string, v ...interface{}) { mylogger.DebugfWithDepth(calldepth, format, v...) } func Debugf(format string, v ...interface{}) { mylogger.Debugf(format, v...) } func Noticef(format string, v ...interface{}) { mylogger.Noticef(format, v...) } func NoticefWithDepth(calldepth int, format string, v ...interface{}) { mylogger.NoticefWithDepth(calldepth, format, v...) } func Infof(format string, v ...interface{}) { mylogger.Infof(format, v...) } func Warningf(format string, v ...interface{}) { mylogger.Warningf(format, v...) } func Errorf(format string, v ...interface{}) { mylogger.Warningf(format, v...) } func Panicf(format string, v ...interface{}) { mylogger.Panicf(format, v...) } func Fatalf(format string, v ...interface{}) { mylogger.Fatalf(format, v...) } ////////////////////////////////////////////////////////////////// type logLevel int const ( PANIC logLevel = 0 FATAL logLevel = 1 ERROR logLevel = 2 NOTICE logLevel = 3 WARNING logLevel = 4 INFO logLevel = 5 DEBUG logLevel = 9 ) type Logger struct { module string loglevel logLevel logger *log.Logger } func New(module string) (logger *Logger) { if module == "" { module = "undefine" } if _, ok := modulelogs[module]; ok { return modulelogs[module] } else { modulelogs[module] = &Logger{ module: module, logger: glogger, loglevel: DEBUG, } return modulelogs[module] } } func (log *Logger) SetLogLevel(newlv logLevel) (oldlv logLevel) { oldlv = log.loglevel log.loglevel = newlv return oldlv } func (log *Logger) Print(prefix string, v interface{}) { preety, _ := json.Marshal(v) log.logwrite(DEBUG, 3, prefix+"%s\n", preety) } func (log *Logger) PrintPretty(prefix string, v interface{}) { preety, _ := json.MarshalIndent(v, "==", " ") log.logwrite(DEBUG, 3, prefix+"%s\n", preety) } func (log *Logger) Debugf(format string, v ...interface{}) { log.logwrite(DEBUG, 3, format, v...) } func (log *Logger) DebugfWithDepth(calldepth int, format string, v ...interface{}) { calldepth += 3 log.logwrite(DEBUG, calldepth, format, v...) } func (log *Logger) Noticef(format string, v ...interface{}) { log.logwrite(NOTICE, 3, format, v...) } func (log *Logger) NoticefWithDepth(calldepth int, format string, v ...interface{}) { calldepth += 3 log.logwrite(NOTICE, calldepth, format, v...) } func (log *Logger) Warningf(format string, v ...interface{}) { log.logwrite(WARNING, 3, format, v...) } func (log *Logger) Infof(format string, v ...interface{}) { log.logwrite(INFO, 3, format, v...) } func (log *Logger) Errorf(format string, v ...interface{}) { log.logwrite(ERROR, 3, format, v...) } func (log *Logger) Fatalf(format string, v ...interface{}) { log.logwrite(FATAL, 3, format, v...) } func (log *Logger) Panicf(format string, v ...interface{}) { log.logwrite(PANIC, 3, format, v...) } func GetLogid() string { if logidGetter != nil { return logidGetter() } return "" } func SetLogid(logid string) { if logidSetter != nil { logidSetter(logid) } } func (log *Logger) logwrite(typ logLevel, calldepth int, format string, v ...interface{}) { if typ > log.loglevel { return } var idstr = "" if id := GetLogid(); id != "" { idstr = "[" + id + "] " } format = strings.Trim(format, "\n") switch typ { case PANIC: log.logger.SetPrefix("\x1b[31m" + "PANIC " + idstr + "[" + log.module + "] ") case FATAL: log.logger.SetPrefix("\x1b[31m" + "FATAL " + idstr + "[" + log.module + "] ") case WARNING: log.logger.SetPrefix("\x1b[32m" + "WARNING " + idstr + "[" + log.module + "] ") case ERROR: log.logger.SetPrefix("\x1b[32m" + "ERROR " + idstr + "[" + log.module + "] ") case INFO: log.logger.SetPrefix("INFO " + idstr + "[" + log.module + "] ") case NOTICE: log.logger.SetPrefix("NOTICE " + idstr + "[" + log.module + "] ") case DEBUG: log.logger.SetPrefix("DEBUG " + idstr + "[" + log.module + "] ") default: log.logger.SetPrefix("UNKNOWN " + idstr + "[" + log.module + "] ") } if mylogger == log { calldepth = calldepth + 1 } if typ == FATAL || typ == WARNING || typ == ERROR { log.logger.Output(calldepth, fmt.Sprintf(format+"\x1b[0m\n", v...)) } else if typ == NOTICE { calldepth = calldepth + 2 log.logger.Output(calldepth, fmt.Sprintf(format+"\n", v...)) } else if typ == PANIC { stack := strings.Replace(string(debug.Stack()), "\n", "\n== ", -1) stack = str.SkipLine(stack, calldepth*2+1) v = append(v, stack) panicstr := fmt.Sprintf(format+"\x1b[0m. Panic stack:\n%s\n", v...) log.logger.Output(calldepth, panicstr) panic(ErrLogPanic) } else { log.logger.Output(calldepth, fmt.Sprintf(format+"\n", v...)) } } //////////////global log trace//////////////////begin/////////////////// func TRACE_INTO(format string, v ...interface{}) string { _, fn, line, _ := runtime.Caller(1) strfn := fmt.Sprintf("%s:%d", fn, line) Debugf("TRACE into ["+strfn+"]"+format+"...\r\n", v...) return strfn } func TRACE_EXIT(strfn string, format string, v ...interface{}) { Debugf("TRACE exit ["+strfn+"]"+format+"...\r\n", v...) } type mWriter struct { logw *os.File stdw *os.File } func (w mWriter) Write(p []byte) (n int, err error) { if flagcachelog { logCache.Write(p) } w.stdw.Write(p) return w.logw.Write(p) } var cacheLogLock = new(sync.Mutex) var flagcachelog = false var logCache = bytes.Buffer{} func StartCacheLog() { cacheLogLock.Lock() flagcachelog = true logCache.Reset() } func StopCacheLog() { cacheLogLock.Unlock() flagcachelog = false } func GetCacheLog() string { if flagcachelog != true { panic("GetCacheLog MUST call after StartCacheLog called") } return logCache.String() } //////////////////////////////////////////////////////////////////////////////////// /////////////////////////Initialize///////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// func init() { once.Do(initlog) } func initlog() { var mwriter mWriter var openlog = func(logname string) *log.Logger { fp, err := os.OpenFile(logname, os.O_APPEND|os.O_CREATE|os.O_WRONLY, os.ModePerm) if err != nil { panic("open log file failed:" + err.Error()) } mwriter.logw.Close() mwriter.logw = fp mwriter.stdw = os.Stdout glogger = log.New(mwriter, "", log.Ldate|log.Lmicroseconds|log.Lshortfile) return glogger } _, logfilename := filepath.Split(os.Args[0]) LOGSPLITTIME := "20060102" //20060102150405 LOGFILENAME := logfilename + ".log" if _, err := os.Stat("log/"); err == nil { LOGFILENAME = "log/" + LOGFILENAME } else if _, err := os.Stat("logs/"); err == nil { LOGFILENAME = "logs/" + LOGFILENAME } else { LOGFILENAME = "logs/" + LOGFILENAME } glogger = openlog(LOGFILENAME) mylogger = New("system") curhourtime := time.Now().Local().Format(LOGSPLITTIME) prehourtime := curhourtime go func() { intva := 2 * time.Minute timer := time.NewTimer(1 * time.Second) for { select { case <-timer.C: curhourtime := time.Now().Local().Format(LOGSPLITTIME) //Debugf("check logfile: prehourtime:%s,curhourtime:%s", prehourtime, curhourtime) if prehourtime != curhourtime { // close old logger & move log to backup file. PREFILENAME := LOGFILENAME + "." + prehourtime os.Rename(LOGFILENAME, PREFILENAME) prehourtime = curhourtime glogger = openlog(LOGFILENAME) mylogger = New("system") for _, l := range modulelogs { l.logger = glogger } } timer.Reset(intva) } } }() }