Go语言基础之log实现

目录结构

my_logger.go

package mylogger

import (
	"path"
	"runtime"
	"strings"
)

type LogLevel uint8 // 值越小, 表示权限越大

const (
	DEBUG = iota
	INFO
	WARNING
	ERROR
	FATAL
)

// 获取运行时的一些信息
func getRunInfo(skip int) (funcName, fileName string, lineNo int) {
	pc, file, lineNo, ok := runtime.Caller(skip)
	if !ok {
		return
	}
	funcName = strings.Split(runtime.FuncForPC(pc).Name(), ".")[1]
	fileName = path.Base(file)
	return
}

console_logger.go

package mylogger

import (
	"fmt"
	"strings"
	"time"
)

type ConsoleLogger struct {
	level LogLevel
}

func NewConsoleLogger(level string) ConsoleLogger {
	var tmp LogLevel
	switch level := strings.ToLower(level); level {
	case "debug":
		tmp = DEBUG
	case "info":
		tmp = INFO
	case "warning":
		tmp = WARNING
	case "error":
		tmp = ERROR
	case "fatal":
		tmp = FATAL
	default:
		panic("不支持的级别类型")
	}
	return ConsoleLogger{tmp}
}

func log(l ConsoleLogger, level LogLevel, message string, a ...interface{}) {
	message = fmt.Sprintf(message, a...)

	keyMap := map[LogLevel]string{
		DEBUG:   "DEBUG",
		INFO:    "INFO",
		WARNING: "WARING",
		ERROR:   "ERROR",
		FATAL:   "FATAL",
	}

	strLevel := keyMap[level]

	// 获取运行时的一些信息
	funcName, fileName, lineNo := getRunInfo(3)

	if l.level <= level {
		fmt.Printf("[%s][%s][%s/%s:%d]: %s 
", time.Now().Format("2006/01/02 15:04:05"), strLevel, funcName, fileName, lineNo, message)
	}
}

func (l ConsoleLogger) Debug(message string, a ...interface{}) {
	//if l.level <= DEBUG {
	//	fmt.Printf("[%s][%s]: %s 
", time.Now().Format("2006/01/02 15:04:05"), "DEBUG", message)
	//}
	log(l, DEBUG, message, a...)
}

func (l ConsoleLogger) Info(message string, a ...interface{}) {
	//if l.level <= INFO {
	//	fmt.Printf("[%s][%s]: %s 
", time.Now().Format("2006/01/02 15:04:05"), "INFO", message)
	//}
	log(l, INFO, message, a...)
}

func (l ConsoleLogger) Warning(message string, a ...interface{}) {
	//if l.level <= WARNING {
	//	fmt.Printf("[%s][%s]: %s 
", time.Now().Format("2006/01/02 15:04:05"), "WARNING", message)
	//}
	log(l, WARNING, message, a...)
}

func (l ConsoleLogger) Error(message string, a ...interface{}) {
	//if l.level <= ERROR {
	//	fmt.Printf("[%s][%s]: %s 
", time.Now().Format("2006/01/02 15:04:05"), "ERROR", message)
	//}
	log(l, ERROR, message, a...)
}

func (l ConsoleLogger) Fatal(message string, a ...interface{}) {
	//if l.level <= FATAL {
	//	fmt.Printf("[%s][%s]: %s 
", time.Now().Format("2006/01/02 15:04:05"), "FATAL", message)
	//}
	log(l, FATAL, message, a...)
}

file_logger.go

package mylogger

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

type FileLogger struct {
	level       LogLevel
	basePath    string
	fileName    string
	errFileName string
	fileObj     *os.File
	errFileObj  *os.File
	fileMaxSize int64
}

func NewFileLogger(level, basePath, fileName, errFileName string, fileMaxSize int64) (FileLogger, error) {
	var tmp LogLevel
	switch level := strings.ToLower(level); level {
	case "debug":
		tmp = DEBUG
	case "info":
		tmp = INFO
	case "warning":
		tmp = WARNING
	case "error":
		tmp = ERROR
	case "fatal":
		tmp = FATAL
	default:
		panic("不支持的级别类型")
	}

	fileLogger := FileLogger{
		level:       tmp,
		basePath:    basePath,
		fileName:    fileName,
		errFileName: errFileName,
		fileMaxSize: fileMaxSize,
	}

	// 初始化`[标准日志]文件`和`[错误日志]文件`
	// 打开[标准日志]文件
	fileObj, err := os.OpenFile(path.Join(basePath, fileName), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("打开文件[fileObj]失败")
		return fileLogger, err
	}
	fileLogger.fileObj = fileObj
	// 打开[错误日志]文件
	errFileObj, err := os.OpenFile(path.Join(basePath, errFileName), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("打开文件[errFileObj]失败")
		return fileLogger, err
	}
	fileLogger.errFileObj = errFileObj

	return fileLogger, nil
}

func splitFile(basePath, fileName string, fileObj *os.File, fileMaxSize int64) *os.File {
	// ----------做文件截断操作------------
	// todo 做文件截断操作
	// 1. 判断当前打开的文件的大小是否超过设置的长度限制
	fileInfo, err2 := fileObj.Stat()
	if err2 != nil {
		fmt.Println("获取文件信息失败")
		fmt.Println("err", err2)
		panic("程序又崩溃了")
	}
	fileSize := fileInfo.Size()
	if fileSize >= fileMaxSize{
		fmt.Println("正在将文件截断")
		// 2. 如果超过, 则 2.1:关闭当前文件操作句柄, 2.2:并重命名当前文件  2.3:打开一个新的文件操作句柄
		// 2.1
		err2 := fileObj.Close()
		if err2 != nil {
			fmt.Println("文件关闭失败[1]")
			panic("程序报错了[2]")
		}
		// 2.2
		timeStr := time.Now().Format("20060102150405000")
		oldName := path.Join(basePath, fileName)
		newName := oldName + ".bak_"+timeStr
		err2 = os.Rename(oldName, newName)
		if err2 != nil {
			fmt.Println("文件重命名失败[1]")
			fmt.Println("err", err2)
			panic("重命名失败[1]")
		}
		// 2.3
		newFileObj, err2 := os.OpenFile(path.Join(basePath, fileName), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err2 != nil {
			fmt.Println("文件已被截断, 重新打开新文件失败[1]")
			panic("程序又又蹦啦")
		}
		//l.fileObj = newFileObj
		return newFileObj
	}
	// 3. 长度未超出限制, 则啥事也不做, pass

	// ----------做文件截断操作------------
	return nil
}

func fileLog(l *FileLogger, level LogLevel, message string, a ...interface{}) {
	message = fmt.Sprintf(message, a...)

	keyMap := map[LogLevel]string{
		DEBUG:   "DEBUG",
		INFO:    "INFO",
		WARNING: "WARING",
		ERROR:   "ERROR",
		FATAL:   "FATAL",
	}

	strLevel := keyMap[level]

	// 获取运行时的一些信息
	funcName, fileName, lineNo := getRunInfo(3)

	// 写入[标准日志]
	if l.level <= level {  // 判断log句柄有没有权限, 如果有权限, 则输出

		// 截断文件
		newFileObj := splitFile(l.basePath, l.fileName, l.fileObj, l.fileMaxSize)
		if newFileObj != nil{
			l.fileObj = newFileObj
		}

		_, err := fmt.Fprintf(l.fileObj, "[%s][%s][%s/%s:%d]: %s 
", time.Now().Format("2006/01/02 15:04:05"), strLevel, funcName, fileName, lineNo, message)
		if err != nil {
			fmt.Println("文件写入失败")
			return
		}
	}
	// 写入[错误日志]
	if level >= ERROR{
		// todo 做文件截断
		// 截断文件
		newFileObj := splitFile(l.basePath, l.errFileName, l.errFileObj, l.fileMaxSize)
		if newFileObj != nil{
			l.errFileObj = newFileObj
		}

		_, err := fmt.Fprintf(l.errFileObj, "[%s][%s][%s/%s:%d]: %s 
", time.Now().Format("2006/01/02 15:04:05"), strLevel, funcName, fileName, lineNo, message)
		if err != nil {
			fmt.Println("文件写入失败")
			return
		}
	}
}

func (l *FileLogger) Debug(message string, a ...interface{}) {
	fileLog(l, DEBUG, message, a...)
}

func (l *FileLogger) Info(message string, a ...interface{}) {
	fileLog(l, INFO, message, a...)
}

func (l *FileLogger) Warning(message string, a ...interface{}) {
	fileLog(l, WARNING, message, a...)
}

func (l *FileLogger) Error(message string, a ...interface{}) {
	fileLog(l, ERROR, message, a...)
}

func (l *FileLogger) Fatal(message string, a ...interface{}) {
	fileLog(l, FATAL, message, a...)
}

测试

package main

import (
	"fmt"
	"time"

	"code.xxx.com/studygolang/day06/mylogger"
)

func main() {
	// 1. 向控制台输出的
	//log := mylogger.NewConsoleLogger("error") // 初始化

	// 2. 向文件输出的
	log, err := mylogger.NewFileLogger("info", "./resources/logs/", "mylogger.txt", "errorlogger.txt", 1024*1024*10)
	if err != nil {
		fmt.Println("日志文件初始化失败")
		return
	}

	for {
		// 打印日志
		log.Info("你好呀, 这是一条info信息")
		log.Debug("你好呀, 这是一条debug信息")
		log.Warning("你好呀, 这是一条waring信息")
		log.Error("你好呀, 这是一条error信息  id: %d  name: %s", 1, "sch")
		log.Fatal("你好呀, 这是一条fatal信息")
		time.Sleep(1000)
	}
}
原文地址:https://www.cnblogs.com/sunch/p/14725131.html