go logo
Go,  技术,  编程语言

从零开始搭建go项目(gin框架)(三) – 引入logrus日志

上篇文章,我们已经给项目加上了路由,并且走通了接口,这篇文章我们再给项目加上一项必不可少的内容 —— 日志.

目前go的日志记录,用的最多的还是 logrus, 我们这次就将logrus以一个中间件的方式,引入我们的项目。

第一步 引入logrus

logrus项目的git地址: https://github.com/sirupsen/logrus

go get -u github.com/sirupsen/logrus

第二步 增加日志配置读取逻辑

在根目录创建配置文件夹,并创建配置文件config.yaml,以及配置解析文件conf.go

mkdir conf

./conf/config.yaml :

log: # 日志配置
  # 日志路径
  dir: "./log"

./conf/conf.go :

package conf

import (
	"gopkg.in/yaml.v3"
	"io/ioutil"
)

var BaseConf Config

type LogConf struct {
	Dir string `yaml:"dir"`
}

type Config struct {
	Log LogConf `yaml:"log"`
}

func InitConf()  {
	confPath := "./conf/config.yaml"
	if yamlFile, err := ioutil.ReadFile(confPath); err != nil {
		panic("read conf error: " + err.Error())
	} else if err = yaml.Unmarshal(yamlFile, &BaseConf); err != nil {
		panic("conf file unmarshal error: " + err.Error())
	}
}

注意: ioutil.ReadFile(),可以是相对路径,也可以是绝对路径,但相对路径的话,./ 指的是根目录,跟当前文件所在目录无关

第四步 初始化配置

在main函数中,初始化配置 ./main.go :

package main

import (
	"fusheng-admin/conf"
	"fusheng-admin/router"
	"github.com/gin-gonic/gin"
)

func main()  {
	// 1.创建路由
	r := gin.Default()

	// 初始化配置
	conf.InitConf()

	// 2.绑定路由规则,执行的函数
	// gin.Context,封装了request和response
	router.Http(r)

	// 3.监听端口,默认在8080
	// Run("里面不指定端口号默认为8088")
	r.Run(":8088")
}

第四步 封装logrus

创建log封装文件 ./library/log/log.go

mkdir library/log

library/log/log.go

package log

import (
	"fmt"
	"github.com/sirupsen/logrus"
	"os"
	"path"
	"fusheng-admin/conf"
)

func init()  {
	// 设置日志格式为json格式
	logrus.SetFormatter(&logrus.JSONFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})
	logrus.SetReportCaller(true)
}

func Debug(fields logrus.Fields, args ...interface{})  {
	setOutPutFile(logrus.DebugLevel)
	logrus.WithFields(fields).Debug(args)
}

func Info(fields logrus.Fields, args ...interface{})  {
	setOutPutFile(logrus.InfoLevel)
	logrus.WithFields(fields).Info(args)
}

func Warn(fields logrus.Fields, args ...interface{})  {
	setOutPutFile(logrus.WarnLevel)
	logrus.WithFields(fields).Warn(args)
}

func Fatal(fields logrus.Fields, args ...interface{})  {
	setOutPutFile(logrus.FatalLevel)
	logrus.WithFields(fields).Fatal(args)
}

func Error(fields logrus.Fields, args ...interface{})  {
	setOutPutFile(logrus.ErrorLevel)
	logrus.WithFields(fields).Error(args)
}

func Panic(fields logrus.Fields, args ...interface{})  {
	setOutPutFile(logrus.PanicLevel)
	logrus.WithFields(fields).Panic(args)
}

func Trace(fields logrus.Fields, args ...interface{})  {
	setOutPutFile(logrus.TraceLevel)
	logrus.WithFields(fields).Trace(args)
}

func setOutPutFile(level logrus.Level) {
	if _, err := os.Stat(conf.BaseConf.Log.Dir); os.IsNotExist(err) {
		err = os.MkdirAll(conf.BaseConf.Log.Dir, 0777)
		if err != nil {
			panic(fmt.Errorf("create log dir '%s' error: %s", conf.BaseConf.Log.Dir, err))
		}
	}
	name := ""
	switch level {
	case logrus.DebugLevel:
		name = "debug"
	case logrus.InfoLevel:
		name = "info"
	case logrus.WarnLevel:
		name = "warn"
	case logrus.FatalLevel:
		name = "fatal"
	case logrus.ErrorLevel:
		name = "error"
	case logrus.PanicLevel:
		name = "panic"
	case logrus.TraceLevel:
		name = "trace"
	default:
		panic(fmt.Errorf("invaild log level error %d", logrus.ErrorLevel))
	}
	fileName := path.Join(conf.BaseConf.Log.Dir, name + ".log")


	var err error
	os.Stderr, err = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		fmt.Println("open log file err", err)
	}
	logrus.SetOutput(os.Stderr)
	logrus.SetLevel(level)
	return
}

第五步 创建log中间件

通过创建log中间件,是框架能够自动的记录请求前后的一些信息,便于日志追查。

创建中间件文件, ./library/middleware/logger.go

mkdir library/middleware

library/middleware/logger.go

package middleware

import (
	"bytes"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"time"
	"word-dect-go/library/log"
)

func LoggerToFile() gin.HandlerFunc {

	return func(ctx *gin.Context) {
		// 开始时间
		start := time.Now()

		// 请求报文
		var requestBody []byte
		if ctx.Request.Body != nil {
			var err error
			requestBody, err = ctx.GetRawData()
			if err != nil {
				log.Warn(map[string]interface{}{"err": err.Error()}, "get http request body error")
			}
			ctx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(requestBody))
		}

		// 处理请求
		ctx.Next()

		// 结束时间
		end := time.Now()

		log.Info(map[string]interface{}{
			"statusCode": ctx.Writer.Status(),
			"cost": float64(end.Sub(start).Nanoseconds()/1e4) / 100.0,
			"clientIp": ctx.ClientIP(),
			"method": ctx.Request.Method,
			"uri": ctx.Request.RequestURI,
		})
	}
}

第六步 引入中间件

r.Use(middleware.LoggerToFile())

./main.go :

package main

import (
	"fusheng-admin/conf"
	"fusheng-admin/library/middleware"
	"fusheng-admin/router"
	"github.com/gin-gonic/gin"
)

func main()  {
	// 1.创建路由
	r := gin.Default()
	r.Use(middleware.LoggerToFile())

	// 初始化配置
	conf.InitConf()

	// 2.绑定路由规则,执行的函数
	// gin.Context,封装了request和response
	router.Http(r)

	// 3.监听端口,默认在8080
	// Run("里面不指定端口号默认为8088")
	r.Run(":8088")
}

第七步 常规调用

./service/user.go : (我们增加个接口参数name, 并将其打印到日志)

// 请求参数结构体
type UserRequestParams struct {
	Name string `json:"name"`
	Ctx *gin.Context
}

func UserAdd(param *UserRequestParams, responseBody *library.ResponseBody)  {
	log.Info(map[string]interface{}{"name": param.Name})
	return
}

第七步 接口调用,查看日志

curl -H "Content-Type: application/json;" -X POST "http://127.0.0.1:8088/api/v1/useradd" -d '{"name":"user1"}'

可以看到,根目录下自动创建了 ./log 目录,且生成了日志文件info.log :

{"file":"/Users/ethanxu/Ethan/Work/other/fusheng-admin/library/log/log.go:26","func":"fusheng-admin/library/log.Info","level":"info","msg":"[]","name":"user1","time":"2021-06-28 20:46:09"}
{"clientIp":"127.0.0.1","cost":1.3,"file":"/Users/ethanxu/Ethan/Work/other/fusheng-admin/library/log/log.go:26","func":"fusheng-admin/library/log.Info","level":"info","method":"POST","msg":"[]","statusCode":200,"time":"2021-06-28 20:46:09","uri":"/api/v1/useradd"}

至此,我们的项目就可以方便的使用日志啦!

ps: 日志相关我们并不需要上传至git,所以可以添加.gitignore文件,这样git就会忽略掉日志文件了。该文件可以自行定义,不需要上传至git的文件/文件夹都可以在这里进行配置。(.gitignore文件本身记得上传至git哦~) (bin 为之前创建的编译后程序存放目录,.idea为所用IDE自动生成的目录)

.gitignore :

bin
log
.idea


精品课程


guest

0 评论
内联反馈
查看所有评论