diff --git a/zlog/stdzlog.go b/zlog/stdzlog.go new file mode 100644 index 0000000..9f11b7e --- /dev/null +++ b/zlog/stdzlog.go @@ -0,0 +1,105 @@ +package zlog + +/* + 全局默认提供一个Log对外句柄,可以直接使用API系列调用 + 全局日志对象 StdZinxLog + */ + +import "os" + +var StdZinxLog = NewZinxLog(os.Stderr, "", BitDefault) + +//获取StdZinxLog 标记位 +func Flags() int { + return StdZinxLog.Flags() +} + +//设置StdZinxLog标记位 +func ResetFlags(flag int) { + StdZinxLog.ResetFlags(flag) +} + +//添加flag标记 +func AddFlag(flag int) { + StdZinxLog.AddFlag(flag) +} + +//设置StdZinxLog 日志头前缀 +func SetPrefix(prefix string) { + StdZinxLog.SetPrefix(prefix) +} + +//设置StdZinxLog绑定的日志文件 +func SetLogFile(fileDir string, fileName string) { + StdZinxLog.SetLogFile(fileDir, fileName) +} + +//设置关闭debug +func CloseDebug() { + StdZinxLog.CloseDebug() +} + +//设置打开debug +func OpenDebug() { + StdZinxLog.OpenDebug() +} + +// ====> Debug <==== +func Debugf(format string, v ...interface{}) { + StdZinxLog.Debugf(format, v...) +} + +func Debug(v ...interface{}) { + StdZinxLog.Debug(v...) +} + +// ====> Info <==== +func Infof(format string, v ...interface{}) { + StdZinxLog.Infof(format, v...) +} + +func Info(v ...interface{}) { + StdZinxLog.Info(v...) +} + +// ====> Warn <==== +func Warnf(format string, v ...interface{}) { + StdZinxLog.Warnf(format, v...) +} + +func Warn(v ...interface{}) { + StdZinxLog.Warn(v...) +} + +// ====> Error <==== +func Errorf(format string, v ...interface{}) { + StdZinxLog.Errorf(format, v...) +} + +func Error(v ...interface{}) { + StdZinxLog.Error(v...) +} + +// ====> Fatal 需要终止程序 <==== +func Fatalf(format string, v ...interface{}) { + StdZinxLog.Fatalf(format, v...) +} + +func Fatal(v ...interface{}) { + StdZinxLog.Fatal(v...) +} + +// ====> Panic <==== +func Panicf(format string, v ...interface{}) { + StdZinxLog.Panicf(format, v...) +} + +func Panic(v ...interface{}) { + StdZinxLog.Panic(v...) +} + +// ====> Stack <==== +func Stack(v ...interface{}) { + StdZinxLog.Stack(v...) +} + diff --git a/zlog/zlogger.go b/zlog/zlogger.go new file mode 100644 index 0000000..858b09d --- /dev/null +++ b/zlog/zlogger.go @@ -0,0 +1,386 @@ +package zlog + +/* + 日志类全部方法 及 API + + Add By Aceld(刘丹冰) 2019-4-23 +*/ + +import ( + "bytes" + "fmt" + "io" + "os" + "runtime" + "sync" + "time" +) + +const ( + LOG_MAX_BUF = 1024 * 1024 +) + +//日志头部信息标记位,采用bitmap方式,用户可以选择头部需要哪些标记位被打印 +const ( + BitDate = 1 << iota //日期标记位 2019/01/23 + BitTime //时间标记位 01:23:12 + BitMicroSeconds //微秒级标记位 01:23:12.111222 + BitLongFile // 完整文件名称 /home/go/src/zinx/server.go + BitShortFile // 最后文件名 server.go + BitLevel // 当前日志级别: 0(Debug), 1(Info), 2(Warn), 3(Error), 4(Panic), 5(Fatal) + BitStdFlag = BitDate | BitTime //标准头部日志格式 + BitDefault = BitLevel | BitShortFile | BitStdFlag //默认日志头部格式 +) + +//日志级别 +const ( + LogDebug = iota + LogInfo + LogWarn + LogError + LogPanic + LogFatal +) + +//日志级别对应的显示字符串 +var levels = []string{ + "[DEBUG]", + "[INFO]", + "[WARN]", + "[ERROR]", + "[PANIC]", + "[FATAL]", +} + +type ZinxLogger struct { + mu sync.Mutex //确保多协程读写文件,防止文件内容混乱,做到协程安全 + prefix string //每行log日志的前缀字符串,拥有日志标记 + flag int //日志标记位 + out io.Writer //日志输出的文件描述符 + buf bytes.Buffer //输出的缓冲区 + file *os.File //当前日志绑定的输出文件 + debugClose bool //是否打印调试debug信息 +} + +/* + 创建一个日志 +*/ +func NewZinxLog(out io.Writer, prefix string, flag int) *ZinxLogger { + + zlog := &ZinxLogger{out: out, prefix: prefix, flag: flag, file:nil, debugClose:false} + //设置log对象 回收资源 析构方法(不设置也可以,go的Gc会自动回收,强迫症没办法) + runtime.SetFinalizer(zlog, CleanZinxLog) + return zlog +} + +/* + 回收日志处理 + */ + func CleanZinxLog(log *ZinxLogger) { + log.closeFile() + } + + +/* + 制作当条日志数据的 格式头信息 +*/ +func (log *ZinxLogger) formatHeader(buf *bytes.Buffer, t time.Time, file string, line int, level int) { + //如果当前前缀字符串不为空,那么需要先写前缀 + if log.prefix != "" { + buf.WriteByte('<') + buf.WriteString(log.prefix) + buf.WriteByte('>') + } + + //已经设置了时间相关的标识位,那么需要加时间信息在日志头部 + if log.flag&(BitDate|BitTime|BitMicroSeconds) != 0 { + //日期位被标记 + if log.flag&BitDate != 0 { + year, month, day := t.Date() + itoa(buf, year, 4) + buf.WriteByte('/') // "2019/" + itoa(buf, int(month), 2) + buf.WriteByte('/') // "2019/04/" + itoa(buf, day, 2) + buf.WriteByte(' ') // "2019/04/11 " + } + + //时钟位被标记 + if log.flag&(BitTime|BitMicroSeconds) != 0 { + hour, min, sec := t.Clock() + itoa(buf, hour, 2) + buf.WriteByte(':') // "11:" + itoa(buf, min, 2) + buf.WriteByte(':') // "11:15:" + itoa(buf, sec, 2) // "11:15:33" + //微秒被标记 + if log.flag&BitMicroSeconds != 0 { + buf.WriteByte('.') + itoa(buf, t.Nanosecond()/1e3, 6) // "11:15:33.123123 + } + buf.WriteByte(' ') + } + + // 日志级别位被标记 + if log.flag&BitLevel != 0 { + buf.WriteString(levels[level]) + } + + //日志当前代码调用文件名名称位被标记 + if log.flag&(BitShortFile|BitLongFile) != 0 { + //短文件名称 + if log.flag&BitShortFile != 0 { + short := file + for i := len(file) - 1; i > 0; i-- { + if file[i] == '/' { + //找到最后一个'/'之后的文件名称 如:/home/go/src/zinx.go 得到 "zinx.go" + short = file[i+1:] + break + } + } + file = short + } + buf.WriteString(file) + buf.WriteByte(':') + itoa(buf, line, -1) //行数 + buf.WriteString(": ") + } + } +} + +/* + 输出日志文件,原方法 +*/ +func (log *ZinxLogger) OutPut(level int, s string) error { + + now := time.Now() // 得到当前时间 + var file string //当前调用日志接口的文件名称 + var line int //当前代码行数 + log.mu.Lock() + defer log.mu.Unlock() + + if log.flag&(BitShortFile|BitLongFile) != 0 { + log.mu.Unlock() + var ok bool + //得到当前调用者的文件名称和执行到的代码行数 + _, file, line, ok = runtime.Caller(2) + if !ok { + file = "unknown-file" + line = 0 + } + log.mu.Lock() + } + + //清零buf + log.buf.Reset() + //写日志头 + log.formatHeader(&log.buf, now, file, line, level) + //写日志内容 + log.buf.WriteString(s) + //补充回车 + if len(s) > 0 && s[len(s)-1] != '\n' { + log.buf.WriteByte('\n') + } + + //将填充好的buf 写到IO输出上 + _, err := log.out.Write(log.buf.Bytes()) + return err +} + +// ====> Debug <==== +func (log *ZinxLogger) Debugf(format string, v ...interface{}) { + if log.debugClose == true { + return + } + _ = log.OutPut(LogDebug, fmt.Sprintf(format, v...)) +} + +func (log *ZinxLogger) Debug(v ...interface{}) { + if log.debugClose == true { + return + } + _ = log.OutPut(LogDebug, fmt.Sprintln(v...)) +} + +// ====> Info <==== +func (log *ZinxLogger) Infof(format string, v ...interface{}) { + _ = log.OutPut(LogInfo, fmt.Sprintf(format, v...)) +} + +func (log *ZinxLogger) Info(v ...interface{}) { + _ = log.OutPut(LogInfo, fmt.Sprintln(v...)) +} + +// ====> Warn <==== +func (log *ZinxLogger) Warnf(format string, v ...interface{}) { + _ = log.OutPut(LogWarn, fmt.Sprintf(format, v...)) +} + +func (log *ZinxLogger) Warn(v ...interface{}) { + _ = log.OutPut(LogWarn, fmt.Sprintln(v...)) +} + +// ====> Error <==== +func (log *ZinxLogger) Errorf(format string, v ...interface{}) { + _ = log.OutPut(LogError, fmt.Sprintf(format, v...)) +} + +func (log *ZinxLogger) Error(v ...interface{}) { + _ = log.OutPut(LogError, fmt.Sprintln(v...)) +} + +// ====> Fatal 需要终止程序 <==== +func (log *ZinxLogger) Fatalf(format string, v ...interface{}) { + _ = log.OutPut(LogFatal, fmt.Sprintf(format, v...)) + os.Exit(1) +} + +func (log *ZinxLogger) Fatal(v ...interface{}) { + _ = log.OutPut(LogFatal, fmt.Sprintln(v...)) + os.Exit(1) +} + +// ====> Panic <==== +func (log *ZinxLogger) Panicf(format string, v ...interface{}) { + s := fmt.Sprintf(format, v...) + _ = log.OutPut(LogPanic, fmt.Sprintf(format, s)) + panic(s) +} + +func (log *ZinxLogger) Panic(v ...interface{}) { + s := fmt.Sprintln(v...) + _ = log.OutPut(LogPanic, s) + panic(s) +} + +// ====> Stack <==== +func (log *ZinxLogger) Stack(v ...interface{}) { + s := fmt.Sprint(v...) + s += "\n" + buf := make([]byte, LOG_MAX_BUF) + n := runtime.Stack(buf, true) //得到当前堆栈信息 + s += string(buf[:n]) + s += "\n" + _ = log.OutPut(LogError, s) +} + +//获取当前日志bitmap标记 +func (log *ZinxLogger) Flags() int { + log.mu.Lock() + defer log.mu.Unlock() + return log.flag +} + +//重新设置日志Flags bitMap 标记位 +func (log *ZinxLogger) ResetFlags(flag int) { + log.mu.Lock() + defer log.mu.Unlock() + log.flag = flag +} + +//添加flag标记 +func (log *ZinxLogger) AddFlag(flag int) { + log.mu.Lock() + defer log.mu.Unlock() + log.flag |= flag +} + +//设置日志的 用户自定义前缀字符串 +func (log *ZinxLogger) SetPrefix(prefix string){ + log.mu.Lock() + defer log.mu.Unlock() + log.prefix = prefix +} + + +//设置日志文件输出 +func (log *ZinxLogger) SetLogFile(fileDir string, fileName string) { + var file *os.File + + //创建日志文件夹 + _ = mkdirLog(fileDir) + + fullPath := fileDir + "/" + fileName + if log.checkFileExist(fullPath) { + //文件存在,打开 + file, _ = os.OpenFile(fullPath, os.O_APPEND|os.O_RDWR, 0644) + } else { + //文件不存在,创建 + file, _ = os.OpenFile(fullPath, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) + } + + log.mu.Lock() + defer log.mu.Unlock() + + //关闭之前绑定的文件 + log.closeFile() + log.file = file + log.out = file +} + +//关闭日志绑定的文件 +func (log *ZinxLogger) closeFile() { + if log.file != nil { + _ = log.file.Close() + log.file = nil + log.out = os.Stderr + } +} + +func (log *ZinxLogger) CloseDebug() { + log.debugClose = true +} + +func (log *ZinxLogger) OpenDebug() { + log.debugClose = false +} + +// ================== 以下是一些工具方法 ========== + +//判断日志文件是否存在 +func (log *ZinxLogger) checkFileExist(filename string) bool { + exist := true + if _, err := os.Stat(filename); os.IsNotExist(err) { + exist = false + } + return exist +} + +func mkdirLog(dir string) (e error) { + _, er := os.Stat(dir) + b := er == nil || os.IsExist(er) + if !b { + if err := os.MkdirAll(dir, 0775); err != nil { + if os.IsPermission(err) { + e = err + } + } + } + return +} + +//将一个整形转换成一个固定长度的字符串,字符串宽度应该是大于0的 +//要确保buffer是有容量空间的 +func itoa(buf *bytes.Buffer, i int, wid int) { + var u uint = uint(i) + if u == 0 && wid <= 1 { + buf.WriteByte('0') + return + } + + // Assemble decimal in reverse order. + var b [32]byte + bp := len(b) + for ; u > 0 || wid > 0; u /= 10 { + bp-- + wid-- + b[bp] = byte(u%10) + '0' + } + + // avoid slicing b to avoid an allocation. + for bp < len(b) { + buf.WriteByte(b[bp]) + bp++ + } +} + diff --git a/zlog/zlogger_test.go b/zlog/zlogger_test.go new file mode 100644 index 0000000..ee7d7f3 --- /dev/null +++ b/zlog/zlogger_test.go @@ -0,0 +1,39 @@ +package zlog + +import ( + "testing" +) + +func TestStdZLog(t *testing.T) { + + //测试 默认debug输出 + Debug("zinx debug content1") + Debug("zinx debug content2") + + Debugf(" zinx debug a = %d\n",10) + + //设置log标记位,加上长文件名称 和 微秒 标记 + ResetFlags(BitDate|BitLongFile|BitLevel) + Info("zinx info content") + + //设置日志前缀,主要标记当前日志模块 + SetPrefix("MODULE") + Error("zinx error content") + + //添加标记位 + AddFlag(BitShortFile|BitTime) + Stack(" Zinx Stack! ") + + //设置日志写入文件 + SetLogFile("./log", "testfile.log") + Debug("===> zinx debug content ~~666") + Debug("===> zinx debug content ~~888") + Error("===> zinx Error!!!! ~~~555~~~") + + //关闭debug调试 + CloseDebug() + Debug("===> 我不应该出现~!") + Debug("===> 我不应该出现~!") + Error("===> zinx Error after debug close !!!!") + +}