// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. package log import ( "encoding/json" "fmt" "log" "os" "runtime/debug" "strings" "sync" "time" "qoobing.com/gomod/str" ) const ( PANIC logLevel = 0 FATAL logLevel = 1 ERROR logLevel = 2 NOTICE logLevel = 3 WARNING logLevel = 4 INFO logLevel = 5 DEBUG logLevel = 9 ErrLogPanic = "~~~~panic~~~~~~" ) var ( golog *log.Logger = nil mylog *Logger = nil logidCreator LogidCreator = nil modulelogs map[string]*Logger = map[string]*Logger{} ) type logLevel int type Logger struct { module string golog *log.Logger loglevel logLevel } type LogidCreator interface { Cleanup() GetLogid() string SetLogid(logid string) } func New(module string) *Logger { if module == "" { module = "undefine" } if _, ok := modulelogs[module]; ok { return modulelogs[module] } else { modulelogs[module] = &Logger{ module: module, golog: golog, 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 (log *Logger) GetLogidStr(format string) string { if logidCreator != nil { return fmt.Sprintf(format, logidCreator.GetLogid()) } return "" } func (log *Logger) logwrite(typ logLevel, calldepth int, format string, v ...interface{}) { if typ > log.loglevel { return } var ( idstr = log.GetLogidStr("[%s] ") prestr = idstr // + "[" + log.module + "] " ) format = strings.Trim(format, "\n") switch typ { case PANIC: log.golog.SetPrefix("\x1b[31m" + "PANI ") case FATAL: log.golog.SetPrefix("\x1b[31m" + "FATA ") case WARNING: log.golog.SetPrefix("\x1b[32m" + "WARN ") case ERROR: log.golog.SetPrefix("\x1b[32m" + "ERRO ") case INFO: log.golog.SetPrefix("INFO ") case NOTICE: log.golog.SetPrefix("NOTI ") case DEBUG: log.golog.SetPrefix("DBUG ") default: log.golog.SetPrefix("UNKN ") } if mylog == log { calldepth = calldepth + 1 } if typ == FATAL || typ == WARNING || typ == ERROR { log.golog.Output(calldepth, prestr+fmt.Sprintf(format+"\x1b[0m\n", v...)) } else if typ == NOTICE { calldepth = calldepth + 2 log.golog.Output(calldepth, prestr+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(prestr+format+"\x1b[0m. Panic stack:\n%s\n", v...) log.golog.Output(calldepth, panicstr) panic(ErrLogPanic) } else { log.golog.Output(calldepth, prestr+fmt.Sprintf(format+"\n", v...)) } } func needWriteLogToFile() bool { if cls, ok := os.LookupEnv("QOOLOG_CLOSE_FILELOG"); ok && cls != "N" && cls != "n" { return false } else if ink8s, ok := os.LookupEnv("KUBERNETES_PORT"); ok && ink8s != "" { return false } return true } // ////////////////////////////////////////////////////////////////////////////////// // ///////////////////////Initialize///////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////// var once sync.Once = sync.Once{} // init func init() { once.Do(initlog) } // initlog func initlog() { // Step 1. create base writer & cache writer for ALL log var ( logDir = "./logs/" logName = getExeFilename() logDirsOptional = []string{"./log", "/var/log"} logSplitTag = "20060102" //20060102150405 baseWriter = NewBaseWriter(nil, os.Stdout, nil) gologFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile ) // Step 2. init golog(golang std logger) & mylog(myself defined logger) golog = log.New(NewCacheWriter(baseWriter), "", gologFlags) mylog = New("system") // Step 3. backgroud split log (log rotate) if need if needWriteLogToFile() { go func() { baseWriter.OpenLogFile(logDir, logName, logDirsOptional) for { tag := time.Now().Local().Format(logSplitTag) baseWriter.TryBackupLogFile(tag) time.Sleep(2 * time.Minute) } }() } }