Browse Source

添加zinx日志模块zlog接口及实现

master
aceld 5 years ago
parent
commit
9d145107c9
  1. 105
      zlog/stdzlog.go
  2. 386
      zlog/zlogger.go
  3. 39
      zlog/zlogger_test.go

105
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...)
}

386
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++
}
}

39
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 !!!!")
}
Loading…
Cancel
Save