golang 日志庫的實現(xiàn)

~~
實現(xiàn)功能:
日志庫接口設(shè)計
文件日志庫開發(fā)
Console日志開發(fā)
管道異步日志庫開發(fā)
日志分割按小時

A. Debug級別:用來調(diào)試程序,日志最詳細(xì)。對程序性能影響比較大。
B. Trace級別:用來追蹤問題。
C. Info級別:打印程序運行過程中比較重要的信息,比如訪問日志
D. Warn級別:警告日志,說明程序運行出現(xiàn)了潛在的問題
E. Error級別:錯誤日志,程序運行發(fā)生錯誤,但不影響程序運行。
F. Fatal級別:嚴(yán)重錯誤日志,發(fā)生的錯誤會導(dǎo)致程序退出

實現(xiàn)打印各個level的日志
實現(xiàn)設(shè)置級別
實現(xiàn)異步打印日志到文件和管道
~~

og_interface.go

package logger

type LogInterface interface {
    Init()
    SetLevel(level int)
    Debug(format string, args ...interface{})
    Trace(format string, args ...interface{})
    Info(format string, args ...interface{})
    Warn(format string, args ...interface{})
    Error(format string, args ...interface{})
    Fatal(format string, args ...interface{})
    Close()
}


file.go

package logger

import (
    "fmt"
    "os"
    "strconv"
    "time"
)

//2018/3/26 0:01.383 DEBUG logDebug.go:29 this is a debug log
//2006-01-02 15:04:05.999
type FileLogger struct {
    level         int
    logPath       string
    logName       string
    file          *os.File
    warnFile      *os.File
    LogDataChan   chan *LogData
    logSplitType  int
    logSplitSize  int64
    lastSplitHour int
}

func NewFileLogger(config map[string]string) (log LogInterface, err error) {
    logPath, ok := config["log_path"]
    if !ok {
        err = fmt.Errorf("not found log_path ")
        return
    }

    logName, ok := config["log_name"]
    if !ok {
        err = fmt.Errorf("not found log_name ")
        return
    }

    logLevel, ok := config["log_level"]
    if !ok {
        err = fmt.Errorf("not found log_level ")
        return
    }

    logChanSize, ok := config["log_chan_size"]
    if !ok {
        logChanSize = "50000"
    }

    var logSplitType int = LogSplitTypeHour
    var logSplitSize int64
    logSplitStr, ok := config["log_split_type"]
    if !ok {
        logSplitStr = "hour"
    } else {
        if logSplitStr == "size" {
            logSplitSizeStr, ok := config["log_split_size"]
            if !ok {
                logSplitSizeStr = "104857600"
            }

            logSplitSize, err = strconv.ParseInt(logSplitSizeStr, 10, 64)
            if err != nil {
                logSplitSize = 104857600
            }

            logSplitType = LogSplitTypeSize
        } else {
            logSplitType = LogSplitTypeHour
        }
    }

    chanSize, err := strconv.Atoi(logChanSize)
    if err != nil {
        chanSize = 50000
    }

    level := getLogLevel(logLevel)
    log = &FileLogger{
        level:         level,
        logPath:       logPath,
        logName:       logName,
        LogDataChan:   make(chan *LogData, chanSize),
        logSplitSize:  logSplitSize,
        logSplitType:  logSplitType,
        lastSplitHour: time.Now().Hour(),
    }

    return
}

func (f *FileLogger) Init() {

    filename := fmt.Sprintf("%s/%s.log", f.logPath, f.logName)
    file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
    if err != nil {
        panic(fmt.Sprintf("open faile %s failed, err:%v", filename, err))
    }

    f.file = file

    //寫錯誤日志和fatal日志的文件
    filename = fmt.Sprintf("%s/%s.log.wf", f.logPath, f.logName)
    file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
    if err != nil {
        panic(fmt.Sprintf("open faile %s failed, err:%v", filename, err))
    }

    f.warnFile = file
    go f.writeLogBackground()
}

func (f *FileLogger) splitFileHour(warnFile bool) {
    now := time.Now()
    hour := now.Hour()
    if hour == f.lastSplitHour {
        return
    }

    f.lastSplitHour = hour
    var backupFilename string
    var filename string

    if warnFile {
        backupFilename = fmt.Sprintf("%s/%s.log.wf_%04d%02d%02d%02d",
            f.logPath, f.logName, now.Year(), now.Month(), now.Day(), f.lastSplitHour)

        filename = fmt.Sprintf("%s/%s.log.wf", f.logPath, f.logName)
    } else {
        backupFilename = fmt.Sprintf("%s/%s.log_%04d%02d%02d%02d",
            f.logPath, f.logName, now.Year(), now.Month(), now.Day(), f.lastSplitHour)
        filename = fmt.Sprintf("%s/%s.log", f.logPath, f.logName)
    }

    file := f.file
    if warnFile {
        file = f.warnFile
    }

    file.Close()
    os.Rename(filename, backupFilename)

    file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
    if err != nil {
        return
    }

    if warnFile {
        f.warnFile = file
    } else {
        f.file = file
    }
}

func (f *FileLogger) splitFileSize(warnFile bool) {

    file := f.file
    if warnFile {
        file = f.warnFile
    }

    statInfo, err := file.Stat()
    if err != nil {
        return
    }

    fileSize := statInfo.Size()
    if fileSize <= f.logSplitSize {
        return
    }

    var backupFilename string
    var filename string

    now := time.Now()
    if warnFile {
        backupFilename = fmt.Sprintf("%s/%s.log.wf_%04d%02d%02d%02d%02d%02d",
            f.logPath, f.logName, now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())

        filename = fmt.Sprintf("%s/%s.log.wf", f.logPath, f.logName)
    } else {
        backupFilename = fmt.Sprintf("%s/%s.log_%04d%02d%02d%02d%02d%02d",
            f.logPath, f.logName, now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
        filename = fmt.Sprintf("%s/%s.log", f.logPath, f.logName)
    }

    file.Close()
    os.Rename(filename, backupFilename)

    file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
    if err != nil {
        return
    }

    if warnFile {
        f.warnFile = file
    } else {
        f.file = file
    }
}

func (f *FileLogger) checkSplitFile(warnFile bool) {

    if f.logSplitType == LogSplitTypeHour {
        f.splitFileHour(warnFile)
        return
    }

    f.splitFileSize(warnFile)
}

func (f *FileLogger) writeLogBackground() {
    for logData := range f.LogDataChan {
        var file *os.File = f.file
        if logData.WarnAndFatal {
            file = f.warnFile
        }

        f.checkSplitFile(logData.WarnAndFatal)

        fmt.Fprintf(file, "%s %s (%s:%s:%d) %s\n", logData.TimeStr,
            logData.LevelStr, logData.Filename, logData.FuncName, logData.LineNo, logData.Message)
    }
}

func (f *FileLogger) SetLevel(level int) {
    if level < LogLevelDebug || level > LogLevelFatal {
        level = LogLevelDebug
    }
    f.level = level
}

func (f *FileLogger) Debug(format string, args ...interface{}) {
    if f.level > LogLevelDebug {
        return
    }

    logData := writeLog(LogLevelDebug, format, args...)
    select {
    case f.LogDataChan <- logData:
    default:
    }
}

func (f *FileLogger) Trace(format string, args ...interface{}) {
    if f.level > LogLevelTrace {
        return
    }
    logData := writeLog(LogLevelTrace, format, args...)
    select {
    case f.LogDataChan <- logData:
    default:
    }
}

func (f *FileLogger) Info(format string, args ...interface{}) {
    if f.level > LogLevelInfo {
        return
    }
    logData := writeLog(LogLevelInfo, format, args...)
    select {
    case f.LogDataChan <- logData:
    default:
    }
}

func (f *FileLogger) Warn(format string, args ...interface{}) {
    if f.level > LogLevelWarn {
        return
    }

    logData := writeLog(LogLevelWarn, format, args...)
    select {
    case f.LogDataChan <- logData:
    default:
    }
}

func (f *FileLogger) Error(format string, args ...interface{}) {
    if f.level > LogLevelError {
        return
    }

    logData := writeLog(LogLevelError, format, args...)
    select {
    case f.LogDataChan <- logData:
    default:
    }
}

func (f *FileLogger) Fatal(format string, args ...interface{}) {
    if f.level > LogLevelFatal {
        return
    }

    logData := writeLog(LogLevelFatal, format, args...)
    select {
    case f.LogDataChan <- logData:
    default:
    }
}

func (f *FileLogger) Close() {
    f.file.Close()
    f.warnFile.Close()
}

constant.go

package logger

const (
    LogLevelDebug = iota
    LogLevelTrace
    LogLevelInfo
    LogLevelWarn
    LogLevelError
    LogLevelFatal
)

const (
    LogSplitTypeHour = iota
    LogSplitTypeSize
)

func getLevelText(level int) string {
    switch level {
    case LogLevelDebug:
        return "DEBUG"
    case LogLevelTrace:
        return "TRACE"
    case LogLevelInfo:
        return "INFO"
    case LogLevelWarn:
        return "WARN"
    case LogLevelError:
        return "ERROR"
    case LogLevelFatal:
        return "FATAL"
    }
    return "UNKNOWN"
}

func getLogLevel(level string) int {
    switch level {
    case "debug":
        return LogLevelDebug
    case "trace":
        return LogLevelTrace
    case "info":
        return LogLevelInfo
    case "warn":
        return LogLevelWarn
    case "error":
        return LogLevelError
    case "fatal":
        return LogLevelFatal
    }
    return LogLevelDebug
}

console.go

package logger

import (
    "fmt"
    "os"
)

type ConsoleLogger struct {
    level int
}

func NewConsoleLogger(config map[string]string) (log LogInterface, err error) {
    logLevel, ok := config["log_level"]
    if !ok {
        err = fmt.Errorf("not found log_level ")
        return
    }

    level := getLogLevel(logLevel)
    log = &ConsoleLogger{
        level: level,
    }
    return
}

func (c *ConsoleLogger) Init() {

}

func (c *ConsoleLogger) SetLevel(level int) {
    if level < LogLevelDebug || level > LogLevelFatal {
        level = LogLevelDebug
    }

    c.level = level
}

func (c *ConsoleLogger) Debug(format string, args ...interface{}) {
    if c.level > LogLevelDebug {
        return
    }

    logData := writeLog(LogLevelDebug, format, args...)
    fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s\n", logData.TimeStr,
        logData.LevelStr, logData.Filename, logData.FuncName, logData.LineNo, logData.Message)
}

func (c *ConsoleLogger) Trace(format string, args ...interface{}) {
    if c.level > LogLevelTrace {
        return
    }

    logData := writeLog(LogLevelTrace, format, args...)
    fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s\n", logData.TimeStr,
        logData.LevelStr, logData.Filename, logData.FuncName, logData.LineNo, logData.Message)
}
func (c *ConsoleLogger) Info(format string, args ...interface{}) {
    if c.level > LogLevelInfo {
        return
    }

    logData := writeLog(LogLevelInfo, format, args...)
    fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s\n", logData.TimeStr,
        logData.LevelStr, logData.Filename, logData.FuncName, logData.LineNo, logData.Message)
}

func (c *ConsoleLogger) Warn(format string, args ...interface{}) {
    if c.level > LogLevelWarn {
        return
    }

    logData := writeLog(LogLevelWarn, format, args...)
    fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s\n", logData.TimeStr,
        logData.LevelStr, logData.Filename, logData.FuncName, logData.LineNo, logData.Message)
}

func (c *ConsoleLogger) Error(format string, args ...interface{}) {
    if c.level > LogLevelError {
        return
    }

    logData := writeLog(LogLevelError, format, args...)
    fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s\n", logData.TimeStr,
        logData.LevelStr, logData.Filename, logData.FuncName, logData.LineNo, logData.Message)
}
func (c *ConsoleLogger) Fatal(format string, args ...interface{}) {
    if c.level > LogLevelFatal {
        return
    }

    logData := writeLog(LogLevelFatal, format, args...)
    fmt.Fprintf(os.Stdout, "%s %s (%s:%s:%d) %s\n", logData.TimeStr,
        logData.LevelStr, logData.Filename, logData.FuncName, logData.LineNo, logData.Message)
}

func (c *ConsoleLogger) Close() {

}

logger.go

package logger

import (
    "fmt"
)

var log LogInterface

/*
file, "初始化一個文件日志實例"
console, "初始化console日志實例"
*/
func InitLogger(name string, config map[string]string) (err error) {
    switch name {
    case "file":
        log, err = NewFileLogger(config)
    case "console":
        log, err = NewConsoleLogger(config)
    default:
        err = fmt.Errorf("unsupport logger name:%s", name)
    }

    return
}

func Debug(format string, args ...interface{}) {
    log.Debug(format, args...)
}

func Trace(format string, args ...interface{}) {
    log.Trace(format, args...)
}

func Info(format string, args ...interface{}) {
    log.Info(format, args...)
}

func Warn(format string, args ...interface{}) {
    log.Warn(format, args...)
}

func Error(format string, args ...interface{}) {
    log.Error(format, args...)
}

func Fatal(format string, args ...interface{}) {
    log.Fatal(format, args...)
}

util.go

package logger

import (
    "fmt"
    "path"
    "runtime"
    "time"
)

type LogData struct {
    Message      string
    TimeStr      string
    LevelStr     string
    Filename     string
    FuncName     string
    LineNo       int
    WarnAndFatal bool
}

//util.go 10

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

/*
1. 當(dāng)業(yè)務(wù)調(diào)用打日志的方法時,我們把日志相關(guān)的數(shù)據(jù)寫入到chan(隊列)
2. 然后我們有一個后臺的線程不斷的從chan里面獲取這些日志,最終寫入到文件。
*/
func writeLog(level int, format string, args ...interface{}) *LogData {
    now := time.Now()
    nowStr := now.Format("2006-01-02 15:04:05.999")
    levelStr := getLevelText(level)

    fileName, funcName, lineNo := GetLineInfo()
    fileName = path.Base(fileName)
    funcName = path.Base(funcName)
    msg := fmt.Sprintf(format, args...)

    logData := &LogData{
        Message:      msg,
        TimeStr:      nowStr,
        LevelStr:     levelStr,
        Filename:     fileName,
        FuncName:     funcName,
        LineNo:       lineNo,
        WarnAndFatal: false,
    }

    if level == LogLevelError || level == LogLevelWarn || level == LogLevelFatal {
        logData.WarnAndFatal = true
    }

    return logData
    //fmt.Fprintf(file, "%s %s (%s:%s:%d) %s\n", nowStr, levelStr, fileName, funcName, lineNo, msg)
}

最后單元測試 logger_test.go

package logger

import (
    "testing"
)

//TestFileLogger:12
//Debug:79
//writeLog:68
//GetLineInfo:10
//runtime.Caller(3)
func TestFileLogger(t *testing.T) {
    logger := NewFileLogger(LogLevelDebug, "c:/logs/", "test")
    logger.Debug("user id[%d] is come from china", 324234)
    logger.Warn("test warn log")
    logger.Fatal("test fatal log")
    logger.Close()
}

//TestFileLogger:12
//Debug:79
//writeLog:68
//GetLineInfo:10
//runtime.Caller(3)
func TestConsoleLogger(t *testing.T) {
    logger := NewConsoleLogger(LogLevelDebug)
    logger.Debug("user id[%d] is come from china", 324234)
    logger.Warn("test warn log")
    logger.Fatal("test fatal log")
    logger.Close()
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容