log/log.go
2022-11-07 17:28:31 +08:00

292 lines
7.5 KiB
Go

// 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 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 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 Logger struct {
module string
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}
return modulelogs[module]
}
}
func (log *Logger) Print(prefix string, v interface{}) {
preety, _ := json.Marshal(v)
log.logwrite("DEBUGE", 3, prefix+"%s\n", preety)
}
func (log *Logger) PrintPretty(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) 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 string, calldepth int, format string, v ...interface{}) {
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 "NOTICE":
log.logger.SetPrefix("NOTICE " + idstr + "[" + log.module + "] ")
case "DEBUGE":
log.logger.SetPrefix("DEBUGE " + 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)
}
}
}()
}