日志项目升级

跟新前的日志项目: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")
}