加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Go 自定义日志库

发布时间:2020-12-16 09:20:20 所属栏目:大数据 来源:网络整理
导读:日志库 自定义一个日志库。 知识储备 runtime.Caller() 该方法能够获取到打印的位置,文件的信息,行数等。 以下是该方法的使用,不必纠结太多,照着用就行。 唯一注意的是 caller() 中值的放入,该值会影响行数的显示,多测试几遍你就大概明白了。 package

日志库

   自定义一个日志库。

知识储备

runtime.Caller()

   该方法能够获取到打印的位置,文件的信息,行数等。

   以下是该方法的使用,不必纠结太多,照着用就行。

   唯一注意的是caller()中值的放入,该值会影响行数的显示,多测试几遍你就大概明白了。

package main

import (
	"fmt"
	"runtime"
	"path/filepath"
)

func f1() {
	pc,file,line,ok := runtime.Caller(1) // 当被其他函数调用,则设置为1. 当无其他函数调用则设置为0
	if !ok {
		fmt.Println("获取信息时出错")
		return
	}
	funcName := runtime.FuncForPC(pc).Name() // 获取调用该函数的函数名字
	fmt.Println(funcName) // 打印调用者姓名
	fmt.Println(filepath.Base(file)) // 打印文件
	fmt.Println(line) // 打印行
}

func main() {
	f1()
}

errors.New()

   该方法用于在函数中返回一个error对象时,添加一个新错误。

package main

import (
	"errors"
	"fmt"
)

func f1() (string,error) {
	return "哈哈哈",errors.New("新错误")
}

func main() {
	str,err := f1()
	fmt.Println(err)  // 新错误
	fmt.Println(str) // 哈哈哈
}

具体代码

myLogger.go

  

package mylogger

import (
	"errors"
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
)

// LogLevel 日志级别 (自类型)
type LogLevel uint16

// Logger 接口
type Logger interface {
	Debug(format string,args ...interface{})
	Trace(format string,args ...interface{})
	Info(format string,args ...interface{})
	Warning(format string,args ...interface{})
	Error(format string,args ...interface{})
	Fatal(format string,args ...interface{})
}

// 定义日志级别
const (
	UNKNOWN LogLevel = iota
	DEBUG
	TRACE
	INFO
	WARNING
	ERROR
	FATAL
)

func parseLogLevel(s string) (LogLevel,error) {
	s = strings.ToLower(s)
	switch s {
	case "debug":
		return DEBUG,nil
	case "trace":
		return TRACE,nil
	case "info":
		return INFO,nil
	case "warning":
		return WARNING,nil
	case "error":
		return ERROR,nil
	case "fatal":
		return FATAL,nil
	default:
		err := errors.New("无效的日志级别")
		return UNKNOWN,err
	}

}

func getLogString(lv LogLevel) string {

	switch lv {
	case DEBUG:
		return "DEBUG"
	case TRACE:
		return "TRACE"
	case INFO:
		return "INFO"
	case WARNING:
		return "WARNING"
	case ERROR:
		return "ERROR"
	case FATAL:
		return "FATAL"
	default:
		return "DEBUG"
	}
}

func getInfo(skip int) (funcName,fileName string,lineNo int) {
	pc,ok := runtime.Caller(skip)
	if !ok {
		fmt.Printf("runtime.Caller() failedn")
		return
	}
	funcName = runtime.FuncForPC(pc).Name()
	funcName = strings.Split(funcName,".")[1]
	fileName = filepath.Base(file)
	lineNo = line
	return funcName,fileName,lineNo
}

file.go

package mylogger

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

// FileLogger 往文件里面写日志相关代码
type FileLogger struct {
	level       LogLevel
	filePath    string // 日志文件保存路径
	fileName    string // 日志文件名
	maxFileSize int64  // 最大的文件大小
	fileObj     *os.File
	errFileObj  *os.File
}

// NewFileLogger ...
func NewFileLogger(levelStr,fp,fn string,maxSize int64) *FileLogger {
	logLevel,err := parseLogLevel(levelStr)
	if err != nil {
		panic(err)
	}

	fl := &FileLogger{
		level:       logLevel,filePath:    fp,fileName:    fn,maxFileSize: maxSize,}

	err = fl.initFile() // 打开文件,获取文件对象
	if err != nil {
		panic(err)
	}
	return fl
}

func (f *FileLogger) initFile() error {
	// 创建记录正确的日志文件
	fullFileName := filepath.Join(f.filePath,f.fileName)
	fileObj,err := os.OpenFile(fullFileName+".log",os.O_APPEND|os.O_CREATE|os.O_WRONLY,0644)
	if err != nil {
		fmt.Printf("open log file faild,err:%v",err)
		return err
	}
	// 创建错误的日志文件
	errFileObj,err := os.OpenFile(fullFileName+"_err.log",0644)
	if err != nil {
		fmt.Printf("open err log file faild,err)
		return err
	}
	f.fileObj = fileObj
	f.errFileObj = errFileObj
	return nil
}

func (f *FileLogger) enable(logLevel LogLevel) bool {
	return f.level <= logLevel
}

func (f *FileLogger) log(lv LogLevel,format string,args ...interface{}) {
	if f.enable(lv) {
		msg := fmt.Sprintf(format,args...)      // 合并输出
		funcName,lineNo := getInfo(3) // 三层调用
		now := time.Now().Format("2006-01-02 03:04:06")
		lvStr := getLogString(lv)
		if f.checkSize(f.fileObj) {
			newFile,err := f.splitFile(f.fileObj)
			if err != nil {
				return
			}
			f.fileObj = newFile
		}
		fmt.Fprintf(f.fileObj,"[%s] [%s] [%s:%s:%d] %s n",now,lvStr,funcName,lineNo,msg)
		if lv >= ERROR {
			if f.checkSize(f.errFileObj) {
				newFile,err := f.splitFile(f.errFileObj)
				if err != nil {
					return
				}
				f.errFileObj = newFile
			}
			// 如果记录日志级别大于或等于ERROR,则再记录一份到LogErr的文件中
			fmt.Fprintf(f.errFileObj,msg)
		}
	}
}

// Debug ...
func (f *FileLogger) Debug(format string,args ...interface{}) {
	f.log(DEBUG,format,args...)
}

// Trace ...
func (f *FileLogger) Trace(format string,args ...interface{}) {
	f.log(TRACE,args...)
}

// Info ...
func (f *FileLogger) Info(format string,args ...interface{}) {
	f.log(INFO,args...)
}

// Warning ...
func (f *FileLogger) Warning(format string,args ...interface{}) {
	f.log(WARNING,args...)
}

// Error ...
func (f *FileLogger) Error(format string,args ...interface{}) {
	f.log(ERROR,args...)
}

// Fatal ...
func (f *FileLogger) Fatal(format string,args ...interface{}) {
	f.log(FATAL,args...)
}

// Close 关闭文件资源
func (f *FileLogger) Close() {
	f.fileObj.Close()
	f.errFileObj.Close()
}

// 获取文件大小,判断是否要进行切割
func (f *FileLogger) checkSize(file *os.File) bool {
	fileInfo,err := file.Stat()
	if err != nil {
		fmt.Printf("get file info failed,err%vn",err)
		return false
	}
	// 如果当前文件的size大于设定的size,则返回true,否则返回false
	return fileInfo.Size() >= f.maxFileSize
}

func (f *FileLogger) splitFile(file *os.File) (*os.File,error) {
	nowStr := time.Now().Format("20060102150405000")
	fileInfo,err:%vn",err)
		return nil,err
	}
	logName := filepath.Join(f.filePath,fileInfo.Name())
	newlogName := fmt.Sprintf("%s.%s.bak",logName,nowStr)
	// 1. 关闭当前文件
	file.Close()
	// 2. 备份一个 rename
	os.Rename(logName,newlogName)
	// 3. 打开一个新的日志文件

	fileObj,err := os.OpenFile(logName,0644)
	if err != nil {
		fmt.Printf("open log file failed,err
	}
	// 4. 将打开的文件赋值给 fl.FileObj
	return fileObj,nil
}

consloe.go

package mylogger

import (
	"fmt"
	"time"
)

// 往终端写日志

// ConsoleLogger ...
type ConsoleLogger struct {
	level LogLevel
}

// NewConsoleLogger 构造函数 ...
func NewConsoleLogger(levelStr string) ConsoleLogger {
	level,err := parseLogLevel(levelStr)
	if err != nil {
		panic(err)
	}
	return ConsoleLogger{
		level: level,}
}

func (c ConsoleLogger) enable(logLevel LogLevel) bool {
	return c.level <= logLevel
}

func (c ConsoleLogger) log(lv LogLevel,args ...interface{}) {
	if c.enable(lv) {
		msg := fmt.Sprintf(format,lineNo := getInfo(3) // 三层调用
		now := time.Now().Format("2006-01-02 03:04:06")
		lvStr := getLogString(lv)
		fmt.Printf("[%s] [%s] [%s:%s:%d] %s n",msg)
	}
}

// Debug ...
func (c ConsoleLogger) Debug(format string,args ...interface{}) {
		c.log(DEBUG,args...)
}

// TRACE ...
func (c ConsoleLogger) Trace(format string,args ...interface{}) {
	c.log(TRACE,args...)
}

// Info ...
func (c ConsoleLogger) Info(format string,args ...interface{}) {
	c.log(INFO,args...)
}

// Warning ...
func (c ConsoleLogger) Warning(format string,args ...interface{}) {
	c.log(WARNING,args...)
}

// Error ...
func (c ConsoleLogger) Error(format string,args ...interface{}) {
	c.log(ERROR,args...)

}

// Fatal ...
func (c ConsoleLogger) Fatal(format string,args ...interface{}) {
	c.log(FATAL,args...)
}



使用案例

   日志库分为往屏幕打印与往文件写入两种。

   通过构造函数实例化不同的对象然后进行操作,支持格式化。

   支持文件切割,可指定每个日志文件的大小。

package main

import (
	mylogger "yunya.com/module"
)


func main() {
	fileLog := mylogger.NewFileLogger("debug","./","test",3*1024) // 向文件打印
	consoleLog := mylogger.NewConsoleLogger("debug")
	
	for {
		fileLog.Debug("Debug%v","试试")
		fileLog.Info("Info")
		fileLog.Warning("Warning")
		fileLog.Error("Error")
		fileLog.Fatal("Fatal")

		consoleLog.Debug("Debug")
	}

}

异步写入

  以下是对写入文件的代码进行优化。会自动开一个goroutine来写入内容。

package mylogger

import (
    "fmt"
    ospath/filepathtime"
)

// FileLogger 往文件里面写日志相关代码
type FileLogger struct {
    level       LogLevel
    filePath    string  日志文件保存路径
    fileName     日志文件名
    maxFileSize int64   最大的文件大小
    fileObj     *os.File
    errFileObj  *os.File
    logChan     chan *logMsg
}

type logMsg  {
    Level     LogLevel
    msg       string
    funcName  
    line      int
    fileName  
    timestamp  时间戳
}

 NewFileLogger ...
func NewFileLogger(levelStr,fn string,maxSize int64) *FileLogger {
    logLevel,err := parseLogLevel(levelStr)
    if err != nil {
        panic(err)
    }

    fl := &FileLogger{
        level:       logLevel,logChan:     make(chan *logMsg,50000), 容量大小五万的日志通道
    }

    err = fl.initFile()  打开文件,获取文件对象
     nil {
        panic(err)
    }
    return fl
}

func (f *FileLogger) initFile() error {
     创建记录正确的日志文件
    fullFileName := filepath.Join(f.filePath,f.fileName)
    fileObj,err := os.OpenFile(fullFileName+.log",1)">0644)
     nil {
        fmt.Printf(open log file faild,err:%v,err)
         err
    }
     创建错误的日志文件
    errFileObj,err := os.OpenFile(fullFileName+_err.logopen err log file faild,1)"> err
    }
    f.fileObj = fileObj
    f.errFileObj = errFileObj
     开启后台goroutine写日志
    go f.writeLogBackground()
     nil
}

func (f *FileLogger) enable(logLevel LogLevel) bool {
    return f.level <= logLevel
}

func (f *FileLogger) writeLogBackground() {

    for {
        if f.checkSize(f.fileObj) {
            newFile,1)"> f.splitFile(f.fileObj)
             nil {
                
            }
            f.fileObj = newFile
        }

        selectcase logTmp := <-f.logChan:
            logInfo := fmt.Sprintf([%s] [%s] [%s:%s:%d] %s nif logTmp.Level >= ERROR {
                 f.checkSize(f.errFileObj) {
                    newFile,1)"> f.splitFile(f.errFileObj)
                     nil {
                        
                    }
                    f.errFileObj = newFile
                }
                 如果记录日志级别大于或等于ERROR,则再记录一份到LogErr的文件中
                fmt.Fprintf(f.fileObj,logInfo)
            }
        default:
             等五百毫秒
            time.Sleep(time.Millisecond * 500)
        }

    }
}

func (f *FileLogger) log(lv LogLevel,format interface{}) {
     f.enable(lv) {
        msg := fmt.Sprintf(format,args...)       合并输出
        funcName,lineNo := getInfo(3)  三层调用
        now := time.Now().Format(2006-01-02 03:04:06)
         日志发送到通道中
        logTmp := &logMsg{
            Level:     lv,msg:       msg,funcName:  funcName,fileName:  fileName,timestamp: now,line:      lineNo,}
        case f.logChan <- logTmp:
             信息,写入通道
         如果写满了通道就不写了,丢掉写不进去的日志
        }

    }
}

 Debug ...
func (f *FileLogger) Debug(format {}) {
    f.log(DEBUG,args...)
}

 Trace ...
func (f *FileLogger) Trace(format {}) {
    f.log(TRACE,1)"> Info ...
func (f *FileLogger) Info(format {}) {
    f.log(INFO,1)"> Warning ...
func (f *FileLogger) Warning(format {}) {
    f.log(WARNING,1)"> Error ...
func (f *FileLogger) Error(format {}) {
    f.log(ERROR,1)"> Fatal ...
func (f *FileLogger) Fatal(format {}) {
    f.log(FATAL,1)"> Close 关闭文件资源
func (f *FileLogger) Close() {
    f.fileObj.Close()
    f.errFileObj.Close()
}

 获取文件大小,判断是否要进行切割
func (f *FileLogger) checkSize(file *os.File)  {
    fileInfo,1)"> file.Stat()
    get file info failed,err%vnreturn false
    }
     如果当前文件的size大于设定的size,则返回true,否则返回false
    return fileInfo.Size() >= f.maxFileSize
}

func (f *FileLogger) splitFile(file *os.File) (*os.File,error) {
    nowStr := time.Now().Format(20060102150405000)
    fileInfo,err:%vn nil,err
    }
    logName :=%s.%s.bak 1. 关闭当前文件
    file.Close()
     2. 备份一个 rename
    os.Rename(logName,newlogName)
     3. 打开一个新的日志文件

    fileObj,err := os.OpenFile(logName,1)">open log file failed,err
    }
     4. 将打开的文件赋值给 fl.FileObj
     fileObj,nil
}

?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读