diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ec69711 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module qoobing.com/gomod/log + +go 1.16 diff --git a/log.go b/log.go new file mode 100644 index 0000000..0e534a0 --- /dev/null +++ b/log.go @@ -0,0 +1,262 @@ +// 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" + "qoobing.com/gomod/str" + "runtime" + "runtime/debug" + "strings" + "sync" + "time" +) + +var ( + glogger *log.Logger = nil + once sync.Once = sync.Once{} + mylogger *Logger = nil + modellogs map[string]*Logger = map[string]*Logger{} +) + +const ErrLogPanic = "~~~~panic~~~~~~" + +//////////////global log//////////////////begin/////////////////// + +func PrintPreety(prefix string, v interface{}) { + mylogger.PrintPreety(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 Warningf(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 Logger struct { + model string + logger *log.Logger +} + +func New(model string) (logger *Logger) { + if model == "" { + model = "undefine" + } + if _, ok := modellogs[model]; ok{ + return modellogs[model] + }else{ + modellogs[model] = &Logger{model: model, logger: glogger} + return modellogs[model] + } +} + +func (log *Logger) Print(prefix string, v interface{}) { + preety, _ := json.Marshal(v) + log.logwrite("DEBUGE", 3, prefix+"%s\n", preety) +} +func (log *Logger) PrintPreety(prefix string, v interface{}) { + preety, _ := json.MarshalIndent(v, "==", " ") + log.logwrite("DEBUGE", 3, prefix+"%s\n", preety) +} + +func (log *Logger) Debugf(format string, v ...interface{}) { + log.logwrite("DEBUGE", 3, format, v...) +} + +func (log *Logger) DebugfWithDepth(calldepth int, format string, v ...interface{}) { + calldepth += 3 + log.logwrite("DEBUGE", 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) 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 (log *Logger) logwrite(typ string, calldepth int, format string, v ...interface{}) { + var ( + l = gls.GetGlsValue("logid") + id = "0" + ) + if l != nil { + id = l.(string) + } + format = strings.Trim(format, "\n") + + switch typ { + case "PANIC": + log.logger.SetPrefix("\x1b[31m" + "PANIC [" + id + "] [" + log.model + "] ") + case "FATAL": + log.logger.SetPrefix("\x1b[31m" + "FATAL [" + id + "] [" + log.model + "] ") + case "WARNING": + log.logger.SetPrefix("\x1b[32m" + "WARNING [" + id + "] [" + log.model + "] ") + case "NOTICE": + log.logger.SetPrefix("NOTICE [" + id + "] [" + log.model + "] ") + case "DEBUGE": + log.logger.SetPrefix("DEBUGE [" + id + "] [" + log.model + "] ") + default: + log.logger.SetPrefix("UNKNOWN [" + id + "] [" + log.model + "] ") + } + + if mylogger == log { + calldepth = calldepth + 1 + } + + if typ == "FATAL" || typ == "WARNING" { + 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 := "log/" + logfilename + ".log" + + 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 modellogs{ + l.logger = glogger + } + } + + timer.Reset(intva) + } + } + }() +}