// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://gitee.com/johng/gf. // @author john, zseeker package glog import ( "os" "io" "time" "fmt" "strings" "runtime" "gitee.com/johng/gf/g/os/gfile" "gitee.com/johng/gf/g/util/gregex" "gitee.com/johng/gf/g/container/gtype" "gitee.com/johng/gf/g/os/gmlock" "gitee.com/johng/gf/g/os/gfpool" "sync" "gitee.com/johng/gf/g/os/gtime" ) type Logger struct { mu sync.RWMutex pr *Logger // 父级Logger io io.Writer // 日志内容写入的IO接口 path *gtype.String // 日志写入的目录路径 file *gtype.String // 日志文件名称格式 level *gtype.Int // 日志输出等级 btEnabled *gtype.Bool // 是否当打印错误时同时开启backtrace打印 alsoStdPrint *gtype.Bool // 控制台打印开关,当输出到文件/自定义输出时也同时打印到终端 } const ( gDEFAULT_FILE_FORMAT = `{Y-m-d}.log` gDEFAULT_FILE_POOL_FLAGS = os.O_CREATE|os.O_WRONLY|os.O_APPEND ) // 默认的日志换行符 var ln = "\n" // 初始化日志换行符 func init() { if runtime.GOOS == "windows" { ln = "\r\n" } } // 新建自定义的日志操作对象 func New() *Logger { return &Logger { io : nil, path : gtype.NewString(), file : gtype.NewString(gDEFAULT_FILE_FORMAT), level : gtype.NewInt(defaultLevel.Val()), btEnabled : gtype.NewBool(true), alsoStdPrint : gtype.NewBool(true), } } // Logger深拷贝 func (l *Logger) Clone() *Logger { return &Logger { pr : l, io : l.GetIO(), path : l.path.Clone(), file : l.file.Clone(), level : l.level.Clone(), btEnabled : l.btEnabled.Clone(), alsoStdPrint : l.alsoStdPrint.Clone(), } } // 设置日志记录等级 func (l *Logger) SetLevel(level int) { l.level.Set(level) } // 获取日志记录等级 func (l *Logger) GetLevel() int { return l.level.Val() } // 快捷方法,打开或关闭DEBU日志信息 func (l *Logger) SetDebug(debug bool) { if debug { l.level.Set(l.level.Val() | LEVEL_DEBU) } else { l.level.Set(l.level.Val() & ^LEVEL_DEBU) } } func (l *Logger) SetBacktrace(enabled bool) { l.btEnabled.Set(enabled) } // 可自定义IO接口,IO可以是文件输出、标准输出、网络输出 func (l *Logger) SetIO(w io.Writer) { l.mu.Lock() l.io = w l.mu.Unlock() } // 返回自定义的IO,默认为nil func (l *Logger) GetIO() io.Writer { l.mu.RLock() r := l.io l.mu.RUnlock() return r } // 获取默认的文件IO func (l *Logger) getFilePointer() *gfpool.File { if path := l.path.Val(); path != "" { // 文件名称中使用"{}"包含的内容使用gtime格式化 file, _ := gregex.ReplaceStringFunc(`{.+?}`, l.file.Val(), func(s string) string { return gtime.Now().Format(strings.Trim(s, "{}")) }) fpath := path + gfile.Separator + file if fp, err := gfpool.Open(fpath, gDEFAULT_FILE_POOL_FLAGS, 0666); err == nil { return fp } else { fmt.Fprintln(os.Stderr, err) } } return nil } // 设置日志文件的存储目录路径 func (l *Logger) SetPath(path string) error { // 如果目录不存在,则递归创建 if !gfile.Exists(path) { if err := gfile.Mkdir(path); err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf(`glog mkdir "%s" failed: %s`, path, err.Error())) return err } } l.path.Set(strings.TrimRight(path, gfile.Separator)) return nil } // 日志文件名称 func (l *Logger) SetFile(file string) { l.file.Set(file) } // 设置写日志时开启or关闭控制台打印,默认是关闭的 func (l *Logger) SetStdPrint(enabled bool) { l.alsoStdPrint.Set(enabled) } // 这里的写锁保证统一时刻只会写入一行日志,防止串日志的情况 func (l *Logger) print(std io.Writer, s string) { // 优先使用自定义的IO输出 str := l.format(s) writer := l.GetIO() if writer == nil { // 如果设置的IO为空,那么其次判断是否有文件输出设置 // 内部使用了内存锁,保证在glog中对同一个日志文件的并发写入不会串日志 if f := l.getFilePointer(); f != nil { defer f.Close() key := l.path.Val() gmlock.Lock(key) _, err := io.WriteString(f, str) gmlock.Unlock(key) if err != nil { fmt.Fprintln(os.Stderr, err.Error()) } } } else { if _, err := io.WriteString(writer, str); err != nil { fmt.Fprintln(os.Stderr, err.Error()) } } if l.alsoStdPrint.Val() { if _, err := io.WriteString(std, str); err != nil { fmt.Fprintln(os.Stderr, err.Error()) } } } // 核心打印数据方法(标准输出) func (l *Logger) stdPrint(s string) { l.print(os.Stdout, s) } // 核心打印数据方法(标准错误) func (l *Logger) errPrint(s string) { // 记录调用回溯信息 if l.btEnabled.Val() { tracestr := l.GetBacktrace() if tracestr != "" { backtrace := "Backtrace:" + ln + tracestr if s[len(s) - 1] == byte('\n') { s = s + backtrace + ln } else { s = s + ln + backtrace + ln } } } l.print(os.Stderr, s) } // 直接打印回溯信息 func (l *Logger) PrintBacktrace() { l.Println(l.GetBacktrace()) } // 获取文件调用回溯字符串 func (l *Logger) GetBacktrace() string { backtrace := "" index := 1 for i := 1; i < 10000; i++ { if _, cfile, cline, ok := runtime.Caller(i); ok { // 不打印出go源码路径及glog包文件路径 if !gregex.IsMatchString("/g/os/glog/glog.+$", cfile) && !gregex.IsMatchString("^" + gfile.GoRootOfBuild(), cfile) && !gregex.IsMatchString(``, cfile) { backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, cfile, cline, ln) index++ } } else { break } } return backtrace } func (l *Logger) format(s string) string { return time.Now().Format("2006-01-02 15:04:05.000 ") + s } func (l *Logger) Print(v ...interface{}) { l.stdPrint(fmt.Sprintln(v...)) } func (l *Logger) Printf(format string, v ...interface{}) { l.stdPrint(fmt.Sprintf(format, v...)) } func (l *Logger) Println(v ...interface{}) { l.stdPrint(fmt.Sprintln(v...)) } func (l *Logger) Printfln(format string, v ...interface{}) { l.stdPrint(fmt.Sprintf(format + ln, v...)) } func (l *Logger) Fatal(v ...interface{}) { l.errPrint(fmt.Sprintln(v...)) os.Exit(1) } func (l *Logger) Fatalf(format string, v ...interface{}) { l.errPrint(fmt.Sprintf(format, v...)) os.Exit(1) } func (l *Logger) Fatalln(v ...interface{}) { l.errPrint(fmt.Sprintln(v...)) os.Exit(1) } func (l *Logger) Fatalfln(format string, v ...interface{}) { l.errPrint(fmt.Sprintf(format + ln, v...)) os.Exit(1) } func (l *Logger) Panic(v ...interface{}) { s := fmt.Sprintln(v...) l.errPrint(s) panic(s) } func (l *Logger) Panicf(format string, v ...interface{}) { s := fmt.Sprintf(format, v...) l.errPrint(s) panic(s) } func (l *Logger) Panicln(v ...interface{}) { s := fmt.Sprintln(v...) l.errPrint(s) panic(s) } func (l *Logger) Panicfln(format string, v ...interface{}) { s := fmt.Sprintf(format + ln, v...) l.errPrint(s) panic(s) } func (l *Logger) Info(v ...interface{}) { if l.checkLevel(LEVEL_INFO) { l.stdPrint("[INFO] " + fmt.Sprintln(v...)) } } func (l *Logger) Infof(format string, v ...interface{}) { if l.checkLevel(LEVEL_INFO) { l.stdPrint("[INFO] " + fmt.Sprintf(format, v...)) } } func (l *Logger) Infofln(format string, v ...interface{}) { if l.checkLevel(LEVEL_INFO) { l.stdPrint("[INFO] " + fmt.Sprintf(format, v...) + ln) } } func (l *Logger) Debug(v ...interface{}) { if l.checkLevel(LEVEL_DEBU) { l.stdPrint("[DEBU] " + fmt.Sprintln(v...)) } } func (l *Logger) Debugf(format string, v ...interface{}) { if l.checkLevel(LEVEL_DEBU) { l.stdPrint("[DEBU] " + fmt.Sprintf(format, v...)) } } func (l *Logger) Debugfln(format string, v ...interface{}) { if l.checkLevel(LEVEL_DEBU) { l.stdPrint("[DEBU] " + fmt.Sprintf(format, v...) + ln) } } func (l *Logger) Notice(v ...interface{}) { if l.checkLevel(LEVEL_NOTI) { l.errPrint("[NOTI] " + fmt.Sprintln(v...)) } } func (l *Logger) Noticef(format string, v ...interface{}) { if l.checkLevel(LEVEL_NOTI) { l.errPrint("[NOTI] " + fmt.Sprintf(format, v...)) } } func (l *Logger) Noticefln(format string, v ...interface{}) { if l.checkLevel(LEVEL_NOTI) { l.errPrint("[NOTI] " + fmt.Sprintf(format, v...) + ln) } } func (l *Logger) Warning(v ...interface{}) { if l.checkLevel(LEVEL_WARN) { l.errPrint("[WARN] " + fmt.Sprintln(v...)) } } func (l *Logger) Warningf(format string, v ...interface{}) { if l.checkLevel(LEVEL_WARN) { l.errPrint("[WARN] " + fmt.Sprintf(format, v...)) } } func (l *Logger) Warningfln(format string, v ...interface{}) { if l.checkLevel(LEVEL_WARN) { l.errPrint("[WARN] " + fmt.Sprintf(format, v...) + ln) } } func (l *Logger) Error(v ...interface{}) { if l.checkLevel(LEVEL_ERRO) { l.errPrint("[ERRO] " + fmt.Sprintln(v...)) } } func (l *Logger) Errorf(format string, v ...interface{}) { if l.checkLevel(LEVEL_ERRO) { l.errPrint("[ERRO] " + fmt.Sprintf(format, v...)) } } func (l *Logger) Errorfln(format string, v ...interface{}) { if l.checkLevel(LEVEL_ERRO) { l.errPrint("[ERRO] " + fmt.Sprintf(format, v...) + ln) } } func (l *Logger) Critical(v ...interface{}) { if l.checkLevel(LEVEL_CRIT) { l.errPrint("[CRIT] " + fmt.Sprintln(v...)) } } func (l *Logger) Criticalf(format string, v ...interface{}) { if l.checkLevel(LEVEL_CRIT) { l.errPrint("[CRIT] " + fmt.Sprintf(format, v...)) } } func (l *Logger) Criticalfln(format string, v ...interface{}) { if l.checkLevel(LEVEL_CRIT) { l.errPrint("[CRIT] " + fmt.Sprintf(format, v...) + ln) } } // 判断给定level是否满足 func (l *Logger) checkLevel(level int) bool { return l.level.Val() & level > 0 }