基于go语言的日志库开发(源码)

本文介绍了如何使用Go语言构建一个日志库,包括定义日志等级、解析日志级别、获取调用堆栈信息以及实现控制台和文件日志输出。示例代码展示了ConsoleLogger和FileLogger两个结构体,分别用于控制台和文件日志,并支持日志级别控制和文件大小检查。
摘要由CSDN通过智能技术生成

基于go的日志库开发源码

只有源码,详解请看 《基于go的日志库开发详解》


mylogger.go

package mylogger

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

// LogLever 日志等级类型
type LogLever uint16

// 日志类型 等级变量
const (
	UNKNOWN LogLever = iota
	DEBUG
	TRACE
	INFO
	WARNING
	ERROR
	FATAL
)

// 将字符串转换成为日志等级对象   "INFO"  -->  INFO(LogLever)
func parseLogLever(s string) (LogLever, error) {
	s = strings.ToUpper(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
	}
}

// 将日志等级对象转为字符串对象   INFO(LogLever)  -->  "INFO"
func getLogString(lv LogLever) string {
	switch lv {
	case DEBUG:
		return "DEBUG"
	case TRACE:
		return "TRACE"
	case INFO:
		return "INFO"
	case ERROR:
		return "ERROR"
	case WARNING:
		return "WARNING"
	case FATAL:
		return "FATAL"
	default:
		return "DEBUG"
	}
}

// 获取文件信息
func getInfo(skip int) (funcName, fileName string, lineNo int) {
	pc, file, line, ok := runtime.Caller(skip)
	if !ok {
		fmt.Println("runtime.Caller failed")
		return
	}
	funcName = runtime.FuncForPC(pc).Name()
	fileName = path.Base(file)
	funcName = strings.Split(funcName, ".")[1]
	lineNo = line
	return
}

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




console.go

package mylogger

import (
	"fmt"
	"time"
)

// Logger 日志结构体
type ConsoleLogger struct {
	Lever LogLever
}

// NewLog 构造函数
func NewConsoleLog(leverStr string) ConsoleLogger {
	lever, err := parseLogLever(leverStr)
	if err != nil {
		panic(err)
	}
	return ConsoleLogger{
		Lever: lever,
	}
}

func (c ConsoleLogger) log(lv LogLever, format string, a ...interface{}) {
	if c.enable(lv) {
		msg := fmt.Sprintf(format, a...)
		nowTime := time.Now().Format("2006-01-02 03:04:05")
		funcName, fileName, lineNo := getInfo(3)
		fmt.Printf("[%s] [%s] [%s:%s:%d] %s\n", nowTime, getLogString(lv), fileName, funcName, lineNo, msg)
	}
}

// 打印日志的条件控制
// 打印日志等级为设置日志之下的
func (c ConsoleLogger) enable(loglever LogLever) bool {
	return loglever >= c.Lever
}

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

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

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

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

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

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



file.go

package mylogger

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

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

// NewFileLogger 生成日志文件构造器
func NewFileLogger(leverStr, fp, fn string, maxSize int64) *FileLogger {
	logLever, err := parseLogLever(leverStr)
	if err != nil {
		panic(err)
	}
	f1 := &FileLogger{
		Level:       logLever,
		filePath:    fp,
		fileName:    fn,
		maxFileSize: maxSize,
	}
	err1 := f1.initFile()
	if err != nil {
		panic(err1)
	}
	return f1
}

// 初始化保存的日志文件和错误日志文件
func (f *FileLogger) initFile() error {
	fullFileName := path.Join(f.filePath, f.fileName)
	fileObj, err1 := os.OpenFile(fullFileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err1 != nil {
		fmt.Printf("open log file failed! err:%v", err1)
		return err1
	}
	errFileObj, err2 := os.OpenFile(fullFileName+".err", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err2 != nil {
		fmt.Printf("open err log file failed! err:%v", err2)
		return err2
	}
	f.fileObj = fileObj
	f.errFileObj = errFileObj
	return nil
}

// 关闭文件操作
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:%v", err)
		return false
	}
	// 看下当前的日志文件是否 大于或等于 日志文件规定的max大小
	return fileInfo.Size() >= f.maxFileSize
}

// 对文件进行分割
func (f *FileLogger) splitFile(file *os.File) (*os.File, error) {
	// 需要进行文件分割
	nowStr := time.Now().Format("20060102150405.000")
	fileInfo, err := file.Stat()
	if err != nil {
		fmt.Printf("get file info failed! err:%v\n", err)
		return nil, err
	}
	logName := path.Join(f.filePath, fileInfo.Name()) // 获取老的文件名
	newLogName := fmt.Sprintf("%s.bak%s", logName, nowStr)

	// 1. 关闭当前文件
	f.fileObj.Close()
	// 2. 对当前文件进行重命名 rename 就是备份一下  xx.log ->xx.log.bak202110241409
	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 log file failed! err:%v\n", err)
		return nil, err
	}
	// 4. 将打开的文件重新赋值给f.fileObj
	return fileObj, nil
}

// 记录日志的方法
func (f *FileLogger) log(lv LogLever, format string, a ...interface{}) {
	if f.enable(lv) {
		msg := fmt.Sprintf(format, a...)
		nowTime := time.Now().Format("2006-01-02 03:04:05")
		funcName, fileName, lineNo := getInfo(3)
		if f.checkSize(f.fileObj) { // 判断下是否超了,超了就要进行文件分割
			newFile, err := f.splitFile(f.fileObj) // 切割日志文件
			if err != nil {
				fmt.Printf("split file failed! err:%v\n", err)
				return
			}
			f.fileObj = newFile
		}
		fmt.Fprintf(f.fileObj, "[%s] [%s] [%s:%s:%d] %s\n", nowTime, getLogString(lv), fileName, funcName, lineNo, msg)
		if lv >= ERROR {
			// 源文件过大就要进行切割
			if f.checkSize(f.errFileObj) {
				newFile, err := f.splitFile(f.errFileObj) // 切割日志文件
				if err != nil {
					fmt.Printf("split file failed! err:%v\n", err)
					return
				}
				f.errFileObj = newFile
			}
			//如果要记录的日志大于ERROR了,我还要再错误日志里面记录一次
			fmt.Fprintf(f.errFileObj, "[%s] [%s] [%s:%s:%d] %s\n", nowTime, getLogString(lv), fileName, funcName, lineNo, msg)
		}
	}
}

// 打印日志的条件控制
// 打印日志等级为设置日志之下的
func (f *FileLogger) enable(loglever LogLever) bool {
	return loglever >= f.Level
}

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

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

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

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木木不会

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值