日志项目升级

跟新前的日志项目:https://blog.51cto.com/13812615/2489258

  • 升级功能点
    • 异步刷盘
    • 日志切分

项目结构

xlog/
├── console.go      #console日志输出
├── file.go              #文件输出(本次优化点)
├── level.go           #日志级别类
├── log.go             #日志库
├── log_base.go   # 基类库
└── tool.go            #工具库,用于获取文件名,函数名,所在行

代码拆解

  • log.go
package xlog

//定义全局默认的日志类型
var logger XLog = newXLog(XLogTypeFile,XLogLevelDebug,"","default")

type XLog interface {
	Init() error   //文件初始化
	LogDebug(fmt string, args ...interface{})
	LogTrace(fmt string, args ...interface{})
	LogInfo(fmt string, args ...interface{})
	LogWarn(fmt string, args ...interface{})
	LogError(fmt string, args ...interface{})
	LogFatal(fmt string, args ...interface{})
	Close()
	SetLevel(level int)
}

func newXLog(logType, level int, filename, module string) XLog {
	//定义接口
	var logger XLog
	switch logType {
	case XLogTypeFile:
		logger = NewXFile(level,filename, module)
	case XLogTypeConsole:
		logger = NewXConsole(level, module)
	default:
		logger = NewXFile(level,filename, module)
	}
	return logger
}

//封装接口,后期可以直接调用

func Init(logType, level int, filename, module string) (err error){
	logger = newXLog(logType,level,filename,module)
	return logger.Init()
}

func LogDebug(fmt string, args ...interface{}) {
	logger.LogDebug(fmt, args...)
}

func LogTrace(fmt string, args ...interface{}) {
	logger.LogTrace(fmt, args...)
}

func LogInfo(fmt string, args ...interface{}) {
	logger.LogInfo(fmt, args...)
}

func LogWarn(fmt string, args ...interface{}) {
	logger.LogWarn(fmt, args...)
}

func LogError(fmt string, args ...interface{}) {
	logger.LogError(fmt, args...)
}

func LogFatal(fmt string, args ...interface{}) {
	logger.LogFatal(fmt, args...)
}

func Close() {
	logger.Close()
}

func SetLevel(level int) {
	logger.SetLevel(level)
}
  • log_base.go
package xlog

import (
	"fmt"
	"os"
	"path/filepath"
	"time"
)

type LogData struct {
	timeStr  string
	levelStr string
	module   string
	filename string
	funcName string
	lineNo   int
	data     string
}

type XLogBase struct {
	level   int
	module  string
}

func (l *XLogBase) writeLog(file *os.File,logData *LogData) {
	fmt.Fprintf(file,"%s %s %s (%s:%s:%d) %s\n",
		logData.timeStr, logData.levelStr, logData.module,
		logData.filename, logData.funcName, logData.lineNo, logData.data)
}

func (l *XLogBase) formatLogger(level int, module, format string, args ...interface{}) *LogData {
	now := time.Now()
	timeStr := now.Format("2006-01-02 15:04:05.000")
	levelStr := getLevelStr(level)
	filename, funcName, lineNo := GetLineInfo(5)
	filename = filepath.Base(filename)
	data := fmt.Sprintf(format, args...)
	//fmt.Printf("%s %s %s (%s:%s:%d) %s\n",timeStr,leveStr,module,filename,funcName,lineNo,data)
	return &LogData{
		timeStr:  timeStr,
		levelStr: levelStr,
		module:   module,
		filename: filename,
		funcName: funcName,
		lineNo:   lineNo,
		data:     data,
	}
}
  • level.go
package xlog

const (
	XLogLevelDebug = iota
	XLogLevelTrace
	XLogLevelInfo
	XLogLevelWarn
	XLogLevelError
	XLogLevelFatal
)

const (
	XLogTypeFile = iota
	XLogTypeConsole
)

func getLevelStr(level int) string {
	switch level {
	case XLogLevelDebug:
		return "DEBUG"
	case XLogLevelTrace:
		return "TRACE"
	case XLogLevelInfo:
		return "INFO"
	case XLogLevelWarn:
		return "WARN"
	case XLogLevelError:
		return "ERROR"
	case XLogLevelFatal:
		return "FATAL"
	default:
		return "UNKNOWN"
	}
}
  • console.go 本次无更新
package xlog

import (
	"os"
)

type XConsole struct {
	*XLogBase //指针实现
}


func NewXConsole(level int, module string) XLog {
	logger := &XConsole{}
	//初始化指针,防止panic
	logger.XLogBase = &XLogBase{
		level: level,
		module: module,
	}
	return logger
}

//不需要初始化文件写入
func (c *XConsole)Init() error {
	return nil
}

func (c *XConsole) LogDebug(format string, args ...interface{}) {
	if c.level > XLogLevelDebug {
		return
	}
	logData := c.formatLogger(XLogLevelDebug, c.module, format, args...)
	c.writeLog(os.Stdout,logData)
}

func (c *XConsole) LogTrace(format string, args ...interface{}) {
	if c.level > XLogLevelTrace {
		return
	}
	logData := c.formatLogger(XLogLevelTrace, c.module, format, args...)
	c.writeLog(os.Stdout,logData)
}

func (c *XConsole) LogInfo(format string, args ...interface{}) {
	if c.level > XLogLevelInfo {
		return
	}
	logData := c.formatLogger(XLogLevelInfo, c.module, format, args...)
	c.writeLog(os.Stdout,logData)
}

func (c *XConsole) LogWarn(format string, args ...interface{}) {
	if c.level > XLogLevelWarn {
		return
	}
	logData := c.formatLogger(XLogLevelWarn, c.module, format, args...)
	c.writeLog(os.Stdout,logData)
}

func (c *XConsole) LogError(format string, args ...interface{}) {
	if c.level > XLogLevelError {
		return
	}
	logData := c.formatLogger(XLogLevelError, c.module, format, args...)
	c.writeLog(os.Stdout,logData)
}

func (c *XConsole) LogFatal(format string, args ...interface{}) {
	if c.level > XLogLevelFatal {
		return
	}
	logData := c.formatLogger(XLogLevelFatal, c.module, format, args...)
	c.writeLog(os.Stdout,logData)
}
func (c *XConsole) SetLevel(level int) {
	c.level = level
}

func (c *XConsole) Close() {}
  • file.go 本次更新异步刷盘&日志切分
package xlog

import (
	"fmt"
	"os"
	"sync"
	"time"
)

type XFile struct {
	filename string
	file     *os.File
	*XLogBase
	logChan chan *LogData
	wg *sync.WaitGroup
	curDay int   //也可以按小时去切割,curHour int
}

func (c *XFile) Init() (err error) {
	c.file,err = os.OpenFile(c.filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY,0755)
	if err != nil {
		return
	}
	return
}

func NewXFile(level int, filename, module string) XLog {
	logger := &XFile{
		filename: filename,
	}
	logger.XLogBase = &XLogBase{
		module: module,
		level: level,
	}

	logger.curDay = time.Now().Day()           // 初始化结构体logger.curHour = time.Now().Hour()
	logger.wg = &sync.WaitGroup{}              //防止主进程退出,不执行子进程
	logger.logChan = make(chan *LogData,10000) //管道初始化

	//异步刷日志到磁盘
	logger.wg.Add(1)
	go logger.syncLog()
	return logger
}

func (c *XFile) syncLog() {
	//从管道读取日志,然后写入文件
	for data := range c.logChan {
		c.splitLog()  //调用切分日志的操作
		c.writeLog(c.file,data)
	}
	c.wg.Done()
}

func (c *XFile) splitLog() {
	now := time.Now()
	if now.Day() == c.curDay {
		return
	}
	c.curDay = now.Day() //更新时间 //按小时切分的配置,c.curHour = now.Hour()
	c.file.Sync()
	c.file.Close()

	newFilename := fmt.Sprintf("%s-%04d-%02d-%02d",c.filename,
		now.Year(),now.Month(),now.Day())
	/*
	按小时切分配置
	newFilename := fmt.Sprintf("%s-%04d-%02d-%02d-%02d", c.filename,now.Year(), now.Month(), now.Day(), now.Hour())
	 */
	os.Rename(c.filename,newFilename)
	c.Init()
}

func (c *XFile) writeToChan(level int,module string,format string,args ...interface{})  {
	logData := c.formatLogger(level, module, format, args...)
	select {
	case c.logChan <- logData:
	default:
	}
}

func (c *XFile) LogDebug(format string, args ...interface{}) {
	if c.level > XLogLevelDebug {
		return
	}
	c.writeToChan(XLogLevelDebug, c.module, format, args...)
}

func (c *XFile) LogTrace(format string, args ...interface{}) {
	if c.level > XLogLevelTrace {
		return
	}
	c.writeToChan(XLogLevelTrace, c.module, format, args...)
}

func (c *XFile) LogInfo(format string, args ...interface{}) {
	if c.level > XLogLevelInfo {
		return
	}
	c.writeToChan(XLogLevelInfo, c.module, format, args...)
}

func (c *XFile) LogWarn(format string, args ...interface{}) {
	if c.level > XLogLevelWarn {
		return
	}
	c.writeToChan(XLogLevelWarn, c.module, format, args...)
}

func (c *XFile) LogError(format string, args ...interface{}) {
	if c.level > XLogLevelError {
		return
	}
	c.writeToChan(XLogLevelError, c.module, format, args...)
}

func (c *XFile) LogFatal(format string, args ...interface{}) {
	if c.level > XLogLevelFatal {
		return
	}
	c.writeToChan(XLogLevelFatal, c.module, format, args...)
}

func (c *XFile) SetLevel(level int) {
	c.level = level
}

func (c *XFile)Close()  {
	//管道为空要关闭
	if c.logChan != nil {
		close(c.logChan)
	}
	c.wg.Wait()
	if c.file != nil {
		c.file.Sync()  //同步写磁盘
		c.file.Close()
	}
}
  • tool.go
package xlog

import "runtime"

func GetLineInfo(skip int) (filename, funcName string, lineNo int) {
	pc, file, line, ok := runtime.Caller(skip)
	if ok {
		fun := runtime.FuncForPC(pc)
		funcName = fun.Name()
	}
	filename = file
	lineNo = line
	return
}

测试样例

  • 注意事项:
    • 可以通过xlog.Debug("xxx")直接打印
    • 不同的模块与需要初始化操作
打印落盘需要定义好对应的日志路径以及,模块名
err := xlog.Init(logType, xlog.XLogLevelDebug, "./xlog.log", "xlog_example")
	if err != nil {
		fmt.Printf("logger init failed\n")
		return
	}

  • xlog_example/main.go
package main

import (
	"flag"
	"fmt"
	_"fmt"
	"oldBoy/xlog"
)

//写日志测试
func logic() {
	for {
		xlog.LogDebug("dads1,user_id:%d,username:%s", 12331, "sadsaf")
		xlog.LogTrace("dads2")
		xlog.LogInfo("dads3")
		xlog.LogWarn("dads4")
		xlog.LogError("sss1")
		xlog.LogFatal("sss2")
	}
}

func main() {
	var logTypeStr string
	flag.StringVar(&logTypeStr, "type", "console",  "please input logger type")
	flag.Parse()

	var logType int
	if (logTypeStr == "file") {
		logType = xlog.XLogTypeFile
	} else {
		logType = xlog.XLogTypeConsole

	}

	xlog.LogDebug("log type is %v", logType)

	_ = logType
	err := xlog.Init(logType, xlog.XLogLevelDebug, "./xlog.log", "xlog_example")
	if err != nil {
		fmt.Printf("logger init failed\n")
		return
	}
	logic()
	xlog.Close()
	fmt.Printf("close return")
}