golang-日志库

需求分析

在这里插入图片描述

file.go(将log写入文件内)

package mylogger

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

// 往文件里面下写日志相关代码

//logger日志结构体
type FileLogger struct {
	Level       LogLevel
	filePath    string //日志文件保存的路径
	fileName    string //日志文件保存恶的文件名
	fileObj		*os.File 
	errFileObj	*os.File
	maxFileSize int64  // 最大文件大小
}

//NewFileLogger 构造函数
func NewFileLogger(leverStr, fp, fn string, maxSize int64) *FileLogger {
	LogLevel, err := parseLogLevel(leverStr)
	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 :=path.Join(f.filePath,f.fileName)//将两个目标对象拼接起来
	fileObj,err := os.OpenFile(fullFileName,os.O_APPEND|os.O_CREATE|os.O_WRONLY,0644)
	if err != nil{
		fmt.Printf("open log file failed,err:%v",err)
		return err
	}
	errFileObj,err := os.OpenFile(fullFileName+".err",os.O_APPEND|os.O_CREATE|os.O_WRONLY,0644)
	if err != nil{
		fmt.Printf("open err file failed,err:%v",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: %v\n", err)
		return false
	}
	//如果当前文件大小 大小等于 日志文件的最大值 就应该返回true
	if fileInfo.Size() >=  f.maxFileSize{
		return true
	}else{
		return false
	}
}
//切割文件
func (f *FileLogger)splitFile(file *os.File)(*os.File,error){
	// 需要切割日志文件
	nowStr := time.Now().Format("20060102150405000")
	fileInfo,err := file.Stat()
	if err != nil {
		fmt.Printf("get file info failed: %v\n", err)
		return nil,err
	}
	logName := path.Join(f.filePath,fileInfo.Name())  //拿到当前日志 文件完整路径
	fmt.Println(logName)
	newLogName := fmt.Sprintf("%s.bak%s",logName,nowStr) //拼接一个日志文件备份的名字
	fmt.Println("2")
	//1.关闭当前的日志文件
	file.Close()
	//2.备份,rename  xx.log -> xx.log.bak21908091709
	os.Rename(logName,newLogName)
	fmt.Println(logName)
	//3.打开一个新的日志文件
	fileObj,err := os.OpenFile(logName,os.O_APPEND|os.O_CREATE|os.O_WRONLY,0644)
	fmt.Println("1")
	if err != nil {
		fmt.Printf("open new log file failed,err:%v",err)
		return nil,err
	}
	//4.将打开的新日志文件对象赋值给f.fileobj
	return fileObj,nil
}
// 记录日志的方法
func (f *FileLogger) log(lv LogLevel, format string, a ...interface{}) {
	if f.enable(lv){
		msg := fmt.Sprintf(format, a...)
		now := time.Now()
		funcName, fileName, linNo := getInfo(3) // 得到的是main函数里面的行数
		//fmt.Println(f.checkSize(f.fileObj))
		if f.checkSize(f.fileObj){
			//fmt.Fprintf(f.fileObj,"---------------------------------------------\n", now.Format("2006-01-02 15:04:05"), getLogString(lv), funcName, fileName, linNo, msg)
			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.Format("2006-01-02 15:04:05"), getLogString(lv), funcName, fileName, linNo, msg)
		if lv >= ERROR{
			if f.checkSize(f.fileObj){
				newFile ,err := f.splitFile(f.errFileObj)
				if err != nil {
					return
				}
				f.errFileObj = newFile
			}
			//如果要记录的日志大于等于ERROR级别,还要在errFileObj中再记录一遍
			fmt.Fprintf(f.errFileObj,"[%s][%s][%s:%s:%d] %s\n", now.Format("2006-01-02 15:04:05"), getLogString(lv), funcName, fileName, linNo, msg)
		}	
	} 
}

func (f *FileLogger) enable(LogLevel LogLevel) bool {
	//fmt.Println(l.level)
	return LogLevel >= f.Level
}

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

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

func (f *FileLogger) Info(format string, a ...interface{}) {
		f.log(INFO, 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...)
}

func (f *FileLogger) Close(){
	f.fileObj.Close()
	f.errFileObj.Close()
}


console.go(将文件显示在终端上)

package mylogger

import (
	"fmt"
	"time"
)

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

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

func (c ConsoleLogger) enable(LogLevel LogLevel) bool {
	//fmt.Println(c.level)
	return LogLevel >= c.level
}

func (c ConsoleLogger) log(lv LogLevel, format string, a ...interface{}) {
	if c.enable(lv){
	msg := fmt.Sprintf(format, a...)
	now := time.Now()
	funcName, fileName, linNo := getInfo(3) // 得到的是main函数里面的行数
	fmt.Printf("[%s][%s][%s:%s:%d] %s\n", now.Format("2006-01-02 15:04:05"), getLogString(lv), funcName, fileName, linNo, msg)
	}
}

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

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

func (c ConsoleLogger) Info(format string, a ...interface{}) {
		c.log(INFO, 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...)
}

mylogger.go(部分函数)

package mylogger

//自定义一个日志库
import (
	"errors"
	"fmt"
	"path"
	"runtime"
	"strings"
)

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

type LogLevel uint16 //正整数

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

type Logger interface {
	Debug(fromat string,a...interface{})
	Info(fromat string,a...interface{})
	Warning(fromat string,a...interface{})
	Error(fromat string,a...interface{})
	FATAL(fromat string,a...interface{})
}

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
	}
}

//根据loglevel获取对应的字符串名称
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,file,lineNo,ok :=runtime.Caller(skip) // 封装几次为几
	if !ok {
		fmt.Printf("runtime.Caller() failed")
		return
	}
	funcName = runtime.FuncForPC(pc).Name()
	filename = path.Base(file) 
	return
}	

main.go(主函数)

package main

import (
	"go_work/awesomeProject/study07/mylogger"
)



//测试我们自己写的日志库的程序
func main() {
	for{
		//log := mylogger.NewLog("debug")
		log := mylogger.NewFileLogger("Info","./","huasheng.log",10*1024)
		id := 1001
		name := "华生"
		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日志")
		log.Info("这是一条Info日志")
		log.Fatal("这是一条Fatal日志")
		//time.Sleep( * time.Second)
	}


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Golang每日一是一个系列的文章,旨在介绍Golang中的各种优秀的第三方。其中一篇文章介绍了zap和logrus。\[1\]zapGolang中性能最高的日志,而logrus虽然已经不再维护和更新,但在使用的简易程度方面更好用一些。\[1\]除了这两个,还有很多其他的日志可以选择。另外,flag也是Golang中常用的之一,用于处理命令行参数。\[2\]\[3\]flag提供了多种选项类型,包括自定义选项类型和时间间隔类型。自定义选项类型可以根据需求进行定义,而时间间隔类型支持多种格式,如"300ms"、"-1.5h"、"2h45m"等。\[2\]\[3\]这些都是Golang开发中常用的工具,可以帮助开发者更高效地进行日志记录和命令行参数处理。 #### 引用[.reference_title] - *1* [Golang一日一之logrus](https://blog.csdn.net/Zuko_chen/article/details/130212672)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Go每日一之Flag](https://blog.csdn.net/weixin_43999327/article/details/130701461)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值