51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

go 日志库案例最终版

主要涉及

main.go

mylogger.go( 接口文件)

outputconsol.go(输出日志到终端)

outputfile.go (输出 日志到 文件)


mylogger.go( 接口文件)


package main

import (
	"mylogger/logger"
	"time"
)

var log logger.Logger // 声明一个全局的接口变量

// 测试自己写的日志相关的内容
func main() {
	//log = logger.NewConsoleLogger("info")
	log = logger.NewFileLogger("error", "./", "baimei.log", 10*1024*1024) // 10M =10*1024*1024

	for {
		id := 1222
		name := "baimeidashu"
		log.Debug("这是一条debug日志,id :%d, name: %s,", id, name)
		log.Trace("这是一条Trace日志,id :%d, name: %s,", id, name)
		log.Info("这是一条 info 日志,id :%d, name: %s,", id, name)
		log.Warning("这是一条 Warning 日志,id :%d, name: %s,", id, name)

		log.Error("这是一条 Error 日志 ,id :%d, name: %s,", id, name)
		log.Fatal("这是一条 Fatal 日志,id :%d, name: %s,", id, name)
		time.Sleep(time.Second)
	}

}

mylogger.go( 接口文件)

package logger

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

//author  baimeidashu.com

// 往终端写日志相关内容

type LogLevel uint16

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

const (
	UNKONWN LogLevel = iota
	DEBUG
	TRACE
	INFO
	WARNING
	ERROR
	FATAL
)

func parseLogLeve(s string) (LogLevel, error) {
	s = strings.ToLower(s)
	//fmt.Printf("%s", 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 UNKONWN, err
	}

}

// 把 int 转换为 字符串
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"
	}
	return "DEBUG"
}

// 获取执行go 文件的信息

func getInfo(skip int) (funcName string, fileName string, lineNumber int) {
	pc, file, line, ok := runtime.Caller(skip)
	if !ok {
		fmt.Println("runtime.caller() failed\n")
		return

	}
	funcName = runtime.FuncForPC(pc).Name()
	fileName = path.Base(file)
	funcName = strings.Split(funcName, ".")[1]
	lineNumber = line

	return
}

outputconsol.go(输出日志到终端)

package logger

import (
	"fmt"
	"time"
)

//author  baimeidashu.com

// 往终端写日志相关内容

// Logger 日志结构体
type ConsoleLogger struct {
	Level LogLevel
}

// NewConsoleLogger 构造函数
func NewConsoleLogger(leverStr string) ConsoleLogger {
	lever, err := parseLogLeve(leverStr)
	fmt.Println("s%", lever)
	fmt.Println("s%", lever)
	if err != nil {
		panic(err)
	}
	return ConsoleLogger{
		Level: lever,
	}
}

// 第2版,由 函数 改为 方法 ,这样这个方法 在这个构造体内就可以使用了,
func (c ConsoleLogger) log(lv LogLevel, format string, a ...interface{}) {

	if c.enable(lv) {
		msg := fmt.Sprintf(format, a...)
		now := time.Now()
		funcName, fileName, lineNum := getInfo(3)
		fmt.Printf("[%s] --[%s] --[%s:%s:%d]%s   \n ", now.Format("2006-01-02 15:04:05"), getLogString(lv), fileName, funcName, lineNum, msg)
	}
}

// 接收者变量 接收者类型
func (c ConsoleLogger) enable(level LogLevel) bool {
	return c.Level >= level

}

// 接收者变量 接收者类型  给 Logger 添加 方法
func (c ConsoleLogger) Debug(fomat string, a ...interface{}) {
	c.log(DEBUG, fomat, a)

}
func (c ConsoleLogger) Trace(fomat string, a ...interface{}) {

	c.log(TRACE, fomat, a)

}

func (c ConsoleLogger) Info(fomat string, a ...interface{}) {

	c.log(INFO, fomat, a)

}

func (c ConsoleLogger) Warning(fomat string, a ...interface{}) {

	c.log(WARNING, fomat, a)

}

func (c ConsoleLogger) Error(fomat string, a ...interface{}) {

	c.log(ERROR, fomat, a)

}

func (c ConsoleLogger) Fatal(fomat string, a ...interface{}) {

	c.log(FATAL, fomat, a)

}

outputfile.go (输出 日志到 文件)

package logger

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

// 向文件中输出日志

type FileLogger struct {
	Level       LogLevel
	filePath    string // 日志文件保存路径
	fileName    string // 日志文件名称
	errFileName string // 错误文件名称
	maxFileSize int64
	fileObj     *os.File
	errFileObj  *os.File
}

// NewFileLogger 构造函数
func NewFileLogger(leverString, fpath, fname string, maxSize int64) *FileLogger {
	logLevel, err := parseLogLeve(leverString)
	if err != nil {
		panic(err)
	}
	fl := &FileLogger{
		Level:       logLevel,
		filePath:    fpath,
		fileName:    fname,
		maxFileSize: maxSize,
	}

	err = fl.initFile() //初始化文件, 先打开文件
	if err != nil {
		panic(err)
	}

	return fl
}
func (f *FileLogger) initFile() error {
	fp := path.Join(f.filePath, f.fileName)

	fileObj, err := os.OpenFile(fp, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("open log file failed, err:%v \n", err)
		return err
	}

	errfileObj, err := os.OpenFile(fp+".err", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("open errlog file failed, err:%v \n", err)
		return err
	}

	//日志文件都打开了
	f.fileObj = fileObj
	f.errFileObj = errfileObj

	return nil

}

// 检测日志文件的大小
func (f *FileLogger) checkSize(file *os.File) bool {
	fileInfo, err := file.Stat()
	if err != nil {
		fmt.Printf("get file info failed, err : %v\n", err)
		return false
	}
	return fileInfo.Size() >= f.maxFileSize
}

// 输出日志
func (f *FileLogger) log(lv LogLevel, format string, a ...interface{}) {

	if f.enable(lv) {
		msg := fmt.Sprintf(format, a...)
		now := time.Now()
		funcName, fileName, lineNum := getInfo(3)
		if f.checkSize(f.fileObj) {
			newFile, err := f.splitFile(f.fileObj)
			if err != nil {
				fmt.Printf("open new log file faild , err: %v\n", err)
				return
			}
			f.fileObj = newFile
		}

		fmt.Fprintf(f.fileObj, "[%s] --[%s] --[%s:%s:%d]%s   \n ", now.Format("2006-01-02 15:04:05"), getLogString(lv), fileName, funcName, lineNum, msg)

		// 如果 logleve 级别大于 EROR 级别, 我还要再 err 日志文件中再记录一遍
		if lv >= ERROR {
			if f.checkSize(f.errFileObj) {
				newFile, err := f.splitFile(f.errFileObj)
				if err != nil {
					fmt.Printf("open new log file faild , err: %v\n", err)
					return
				}
				f.errFileObj = newFile
			}
			fmt.Fprintf(f.errFileObj, "[%s] --[%s] --[%s:%s:%d]%s   \n ", now.Format("2006-01-02 15:04:05"), getLogString(lv), fileName, funcName, lineNum, msg)
		}

	}
}

// 切割文件
func (f *FileLogger) splitFile(file *os.File) (*os.File, error) {
	//需要切割日志文件

	//2. 备份以下  rename  xx.lgo -> xx.log.bak20230514
	nowStr := time.Now().Format("20060514")
	fileInfo, err := file.Stat()
	if err != nil {
		fmt.Printf("open new log file faild , err: %v\n", err)
		return nil, err
	}

	logName := path.Join(f.filePath, fileInfo.Name())      // 当前日志文件路径
	newLogName := fmt.Sprintf("%s.bak%s", logName, nowStr) // 拼接一个日志文件备份的名字

	// 1.关闭当前的日志文件
	file.Close()

	os.Rename(logName, newLogName)

	//3. 打开一个新日志文件,
	fileObj, err := os.OpenFile(logName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Printf("open new log file faild , err: %v\n", err)
		return nil, err
	}

	//4. 将打开的新日志文件对象赋值给 f.fileObj
	return fileObj, err

}

// 接收者变量 接收者类型
func (f *FileLogger) enable(level LogLevel) bool {
	return f.Level >= level

}

// 接收者变量 接收者类型  给 Logger 添加 方法
func (f *FileLogger) Debug(fomat string, a ...interface{}) {

	f.log(DEBUG, fomat, a)

}
func (f *FileLogger) Trace(fomat string, a ...interface{}) {

	f.log(TRACE, fomat, a)

}

func (f *FileLogger) Info(fomat string, a ...interface{}) {

	f.log(INFO, fomat, a)

}

func (f *FileLogger) Warning(fomat string, a ...interface{}) {

	f.log(WARNING, fomat, a)

}

func (f *FileLogger) Error(fomat string, a ...interface{}) {

	f.log(ERROR, fomat, a)

}

func (f *FileLogger) Fatal(fomat string, a ...interface{}) {

	f.log(FATAL, fomat, a)

}

func (f *FileLogger) Close() {
	f.fileObj.Close()
	f.errFileObj.Close()
}
赞(5)
未经允许不得转载:工具盒子 » go 日志库案例最终版