GO语言-控制台版日志系统

一、背景

春节期间在B站学习了GO语言的函数,方法,指针,切片,结构体,文件,Map,接口,time等基础知识,需要一个项目进行综合性练手来更好的理解这些基础,所以编写了一个日志系统,供正在学习GO语言的同学参考。

二、系统原理解析

2.1 目录结构

  • main包下的main.go

 

  • mylogger包下的 console.go,file.go,mylogger.go

2.2 main包下的go文件

2.2.1 main.go

  • 通过调用mylogger包下的不同的构造方法【NewConsoleLogger()或NewFileLogger()】,会返回不同的对象
  • 返回的ConsoleLogger或*FileLogger类型的对象可以赋值给mylogger包下的LoggerInterface接口,因为这两类对象全部实现了接口中定义的方法【Debug,Info,Warn...】
  • 通过写一个for{}循环模拟日志输出,输出到console还是文件根据调用的构造方法返回不同类型的对象决定
package main

import (
	"time"
	mylogger "github.com/studygo/day01/07_mylogger"
)

var log mylogger.LoggerInterface //在main包里面定义log变量,可以在main包的其他文件中使用

func main() {
	log := mylogger.NewConsoleLogger("Info")
	// log = mylogger.NewFileLogger("info", "./", "accessTest.log", 10*1024*1024)
	for {
		log.Debug("这是一条Debug日志")
		log.Info("这是一条Info日志")
		log.Warn("这是一条Warn日志")
		id := 10010
		name := "kelly"
		log.Error("这是一条Error日志,id:%d,name:%s", id, name)
		log.Fatal("这是一条Fetal日志")
		// fmt.Printf("id:%d,name:%s", id, name)
		log.Fetal("这是一条Fetal日志")
		time.Sleep(2 * time.Second)
	}
}

2.3 mylogger包下的go文件

2.2.1 日志系统的公共变量和方法

1. mylogger.go

  • 定义了一个自定义类型为LogLevel,不同于类型别名的是自定义类型在编译完成之后这种类型依然存在。详细参考:类型别名和自定义类型区别
  • 定义DEBUG,INFO,WARN,ERROR,FATAL等LogLevel类型的常量
  • 定义LoggerInterface接口,这个接口下定义了五大抽象方法,只要实现了这些方法的对象就可以称为LoggerInterface类型。详细参考:GO语言接口
  • 定义将字符串解析为LogLevel类型的方法和将LogLevel类型方法转换成string类型的方法
package mylogger

/*
1.支持往不同的地方输出日志
2.日志分级别
	1.Debug
	2.Trace
	3.Info
	4.Warning
	5.Error
	6.Fatal
3.日志需要开关控制,比如说开发的时候什么级别都能输出,但是上线之后只能有INFO级别往下的日志才可以输出
4.日志要有时间,行号(runtime.Caller()),文件名,日志级别,日志信息
5.日志文件要切割
	5.1 按文件大小切割
	5.2 按文件日期区分
*/

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

import (
	"errors"
	"strings"
)

//LogLevel 等级
type LogLevel uint16

const (
	// UNKNOW switch中的默认参数
	UNKNOW LogLevel = iota
	//DEBUG ...
	DEBUG
	//TRACE ...
	TRACE
	//INFO ...
	INFO
	//WARN ...
	WARN
	//ERROR ...
	ERROR
	//FATAL ...
	FATAL
)

//LoggerInterface 日志接口,可以用来接收console和file的返回值
type LoggerInterface interface {
	Debug(msg string, arg ...interface{})
	Info(msg string, arg ...interface{})
	Warn(msg string, arg ...interface{})
	Error(msg string, arg ...interface{})
	Fatal(msg string, arg ...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 "warn":
		return WARN, nil
	case "error":
		return ERROR, nil
	case "fatal":
		return FATAL, nil
	default:
		err := errors.New("无效的日志级别")
		return UNKNOW, err
	}
}

func getLogString(lv LogLevel) string {

	switch lv {
	case DEBUG:
		return "DEBUG"
	case TRACE:
		return "TRACE"
	case INFO:
		return "INFO"
	case WARN:
		return "WARING"
	case ERROR:
		return "ERROR"
	case FATAL:
		return "FATAL"
	default:
		return "UNKNOW"
	}
}

2.2.2 控制台输出日志信息

1. console.go

  • 使用console.go文件下的Debug,Info,Warn,Error等方法,这些方法会调用enable进行判断是否高于指定日志级别【本文中在构造函数中传入的日志级别为Info】,如果返回为true则调用具体的log方法在控制台记录日志信息,反之则不会打印
  • log方法定义了详细的日志格式,通过fmt.printf()直接输出到控制台
package mylogger

import (
	"fmt"
	"path"
	"runtime"
	"strings"
	"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, //把解析得到的level赋值给结构体的Level变量
	}
}

//判断是否需要记录该日志
func (c ConsoleLogger) enable(logLevel LogLevel) bool {
	return logLevel >= c.Level
}

//获取函数名,文件名,行号的方法
func getInfo(skip int) (funcName, fileName string, lineNo int) {
	pc, file, lineNo, ok := runtime.Caller(skip)
	if !ok {
		fmt.Printf("runtime.Caller() failed\n")
	}
	funcName = runtime.FuncForPC(pc).Name()
	fileName = path.Base(file)
	funcName = strings.Split(funcName, ".")[1]
	return
}

//具体记录日志的方法
func log(lv LogLevel, msg string, arg ...interface{}) {
	// fmt.Println(msg)
	fullMsg := fmt.Sprintf(msg, arg...) //Sprintf根据format参数生成格式化的字符串并返回该字符串
	fmt.Println(fullMsg)
	now := time.Now()
	funcName, fileName, lineNo := getInfo(3)
	fmt.Printf("[%s] [%s] [%s:%s:%d] %s\n", now.Format("2006-01-02 15:04:05"), getLogString(lv), funcName, fileName, lineNo, fullMsg)
}

//Debug 方法
func (c ConsoleLogger) Debug(msg string, arg ...interface{}) {
	if c.enable(DEBUG) {
		log(DEBUG, msg, arg...)
	}
}

//Info 方法
func (c ConsoleLogger) Info(msg string, arg ...interface{}) {
	if c.enable(INFO) {
		log(INFO, msg, arg...)
	}
}

//Warn 方法
func (c ConsoleLogger) Warn(msg string, arg ...interface{}) {
	if c.enable(WARN) {
		log(INFO, msg, arg...)
	}
}

//Error 方法
func (c ConsoleLogger) Error(msg string, arg ...interface{}) {
	if c.enable(ERROR) {
		log(ERROR, msg, arg...)
	}
}

//Fatal 方法
func (c ConsoleLogger) Fatal(msg string, arg ...interface{}) {
	if c.enable(FATAL) {
		log(FATAL, msg, arg...)
	}
}

2.日志输出到控制台结果验证

  • 验证console日志输出在控制台的效果
  • 控制台输出了INFO,WARN,ERROR,FATAL级别的日志,我们传入到构造函数的类型是INFO,ConsoleLogger的五个方法会分别和这个类型做比较,大于或等于INFO值的日志才会被打印出来
  • 从输出结果可以看出符合我们的预期

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值