gf/g/os/glog/glog_logger.go
2018-10-18 17:47:54 +08:00

450 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 // 日志输出等级
btSkip *gtype.Int // 错误产生时的backtrace回调信息skip条数
btEnabled *gtype.Bool // 是否当打印错误时同时开启backtrace打印
printHeader *gtype.Bool // 是否不打印前缀信息(时间,级别等)
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"
// 标准输出互斥锁,防止标准输出串日志
stdMu = sync.RWMutex{}
)
// 初始化日志换行符
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()),
btSkip : gtype.NewInt(),
btEnabled : gtype.NewBool(true),
printHeader : gtype.NewBool(true),
alsoStdPrint : gtype.NewBool(true),
}
}
// Logger深拷贝
func (l *Logger) Clone() *Logger {
return &Logger {
pr : l,
io : l.GetWriter(),
path : l.path.Clone(),
file : l.file.Clone(),
level : l.level.Clone(),
btSkip : l.btSkip.Clone(),
btEnabled : l.btEnabled.Clone(),
printHeader : l.printHeader.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)
}
// 设置BacktraceSkip
func (l *Logger) SetBacktraceSkip(skip int) {
l.btSkip.Set(skip)
}
// 可自定义IO接口IO可以是文件输出、标准输出、网络输出
func (l *Logger) SetWriter(w io.Writer) {
l.mu.Lock()
l.io = w
l.mu.Unlock()
}
// 返回自定义的IO默认为nil
func (l *Logger) GetWriter() 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) GetPath() string {
return l.path.Val()
}
// 日志文件名称
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输出
if l.printHeader.Val() {
s = l.format(s)
}
writer := l.GetWriter()
if writer == nil {
// 如果设置的writer为空那么其次判断是否有文件输出设置
// 内部使用了内存锁保证在glog中对同一个日志文件的并发写入不会串日志(并发安全)
if f := l.getFilePointer(); f != nil {
defer f.Close()
key := l.path.Val()
gmlock.Lock(key)
_, err := io.WriteString(f, s)
gmlock.Unlock(key)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
}
}
} else {
l.doStdLockPrint(writer, s)
}
// 是否允许输出到标准输出
if l.alsoStdPrint.Val() {
l.doStdLockPrint(std, s)
}
}
// 并发安全打印到标准输出
func (l *Logger) doStdLockPrint(std io.Writer, s string) {
stdMu.Lock()
if _, err := std.Write([]byte(s)); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
}
stdMu.Unlock()
}
// 核心打印数据方法(标准输出)
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
}
}
}
// 防止串日志情况这里不使用stderr而是使用stdout
l.print(os.Stdout, s)
}
// 直接打印回溯信息参数skip表示调用端往上多少级开始回溯
func (l *Logger) PrintBacktrace(skip...int) {
l.Println(l.GetBacktrace(skip...))
}
// 获取文件调用回溯字符串参数skip表示调用端往上多少级开始回溯
func (l *Logger) GetBacktrace(skip...int) string {
customSkip := 0
if len(skip) > 0 {
customSkip = skip[0]
}
backtrace := ""
index := 1
from := 0
// 首先定位业务文件开始位置
for i := 0; i < 10; i++ {
if _, cfile, _, ok := runtime.Caller(i); ok {
if !gregex.IsMatchString("/g/os/glog/glog.+$", cfile) {
from = i
break
}
}
}
// 从业务文件开始位置根据自定义的skip开始backtrace
goroot := gfile.GoRootOfBuild()
for i := from + customSkip + l.btSkip.Val(); i < 10000; i++ {
if _, cfile, cline, ok := runtime.Caller(i); ok && cfile != "" {
// 不打印出go源码路径及glog包文件路径日志打印必须从业务源码文件开始且从glog包文件开始检索
if (goroot == "" || !gregex.IsMatchString("^" + goroot, cfile)) && !gregex.IsMatchString(`<autogenerated>`, 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
}