Log顾名思义就是纪录,通常在Backend当中会将可预期或非预期的错误记录在此,除此之外也会记录些测试用资料以及警讯资料用以帮忙我们除错/纪录/开发。
那Backend Log的产生都是发生在Server端,目前大部分的Backend设计也都透过API, Application,并采用Client/Broswer与Server的架构完成,也因此可将Backend Log分为三大种类
定义使用者在介面上的操作,像是新增资料、上传档案、发送讯息等。,通常表示一个业务逻辑的执行与实现,那里头每次的动作可能会包含多次的API呼叫,而每次的呼叫都属於一项操作。Event Log的目的就是纪录使用者的操作记录,以此可以用来检测、除错与统计资料等。
由Server执行过程中所产生的Log,用来追踪系统实际运行状况,与资料的流动情形。而且在Exception发生时就是纪录在此,好让後续可以追踪分析与解决。
人如其名,就是Request在传递时所记录的讯息,但由於Requests繁多导致纪录成本庞大,因此大部分情况只会纪录HTTP Header甚至是不纪录Request,只有在发生Error时才会纪录完整Request资料。
TRACE
<DEBUG
<INFO
<WARN
<ERROR
<FATAL
<PANIC
Log除了类型以外也有优先序的差别,当log讯息等级高於你所设定的日志等级,
则该讯息不会纪录输出。
当你设定越高的层级,你的纪录成本也会越大,那通常在production environment只会预设为ERROR
或是WARN
而已。那下面就依照不同的Level依序介绍
最高等级的log,主要用来追踪某些小事。
纪录有用的debug资讯。
当有些有用的事件发生时,会纪录的资讯。
警示讯息,虽然不会对系统有重大影响,但需要注意一下该讯息。
发生了错误,但系统依旧在运作。
最紧急的错误,一但发生需要立即hotflix。
最低等级的log,当调用Panic时发生。
这边我们选用github.com/sirupsen/logrus 这个相当多人使用的log package将Log实作在Gin Backend Server当中。那logrus也有着以下的几个优点:
go get -u github.com/sirupsen/logrus
/app/middleware/log.go
package middleware
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"os"
"path"
"time"
)
func Logger() *logrus.Logger {
now := time.Now()
logFilePath := ""
if dir, err := os.Getwd(); err == nil {
logFilePath = dir + "/logs/"
}
if err := os.MkdirAll(logFilePath, 0777); err != nil {
fmt.Println(err.Error())
}
logFileName := now.Format("2006-01-02") + ".log"
fileName := path.Join(logFilePath, logFileName)
if _, err := os.Stat(fileName); err != nil {
if _, err := os.Create(fileName); err != nil {
fmt.Println(err.Error())
}
}
src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
fmt.Println("err", err)
}
logger := logrus.New()
logger.Out = src
logger.SetLevel(logrus.DebugLevel)
logger.SetFormatter(&logrus.TextFormatter{
TimestampFormat: "2021-01-01 11:11:11",
})
return logger
}
func LoggerToFile() gin.HandlerFunc {
logger := Logger()
return func(c *gin.Context) {
startTime := time.Now()
c.Next()
endTime := time.Now()
latencyTime := endTime.Sub(startTime)
reqMethod := c.Request.Method
reqUri := c.Request.RequestURI
statusCode := c.Writer.Status()
clientIP := c.ClientIP()
logger.Infof("| %3d | %13v | %15s | %s | %s |",
statusCode,
latencyTime,
clientIP,
reqMethod,
reqUri,
)
}
}
Logger()
: 主要是设定输出档案的位置、输出格式、档案命名等设定,在init好LogFile之後藉由logrus.New()
实体化一个logrus物件,并设定所需的log level与TIme Format,并将log写入LogFile之中。
LoggerToFile()
: Gin的log middleware,我们会将时间相关资讯与gin.Context
中的Request Method, URL, StatusCode与IP等资讯也一并写入log之中。main.go
server := gin.Default()
server.Use(middleware.LoggerToFile())
server.Use(middleware.CORSMiddleware())
server.GET("/hc", func(c *gin.Context) {
c.String(http.StatusOK, fmt.Sprintf("Health Check"))
middleware.Logger().WithFields(logrus.Fields{
"name": "Flynn Sun",
}).Info("Health Check", "Info")
})
我们让Gin使用LoggerToFile()
这个Middleware,并在health check endpoint当中写入INFO Level的log加以测试。
docker-compose.yaml
ironman-2021:
container_name: ironman-2021
ports:
- "8080:8080"
build:
context: ./
dockerfile: Dockerfile
command: ./main
restart: always
networks:
- backend-bridge
depends_on:
- postgresql
- redis
volumes:
- ./logs/:/usr/local/go/src/ironman-2021/logs
我们在Gin的Container中将logs folder的log file都mount出来,让我们比较容易去观察与纪录log。
最後我们则是来启动docker-compose —build 并 GET http://localhost:8080/hc 进行测试。
然後会在/logs/2021-10-09.log当中得到以下内容
time="90910-10-10 1010:1010:1010" level=info msg="Health CheckInfo" name="Flynn Sun"
time="90910-10-10 1010:1010:1010" level=info msg="| 200 | 6.7669ms | 192.168.160.1 | GET | /hc |"
这也证实我们的logger实装完成!
Logger在Backend Server当中是必不可缺的一环,因为再怎麽完美的系统也一定会有着漏洞,如果届时没有logger的话,会相当难找到问题所在!
这次的程序码我也会放在下方连结,欢迎大家享用。
https://github.com/Neskem/Ironman-2021/tree/Day-26
>>: Day 26: Behavioral patterns - Strategy
今天应该是爬虫的最後一篇了。我们要把爬下来的资料做成「每日铁人赛热门 Top 10」。 来看看爬下来...
何谓提升(Hoisting)? 提升(Hoisting) 其实主要是为了厘清 JavaScript ...
Abrstract 叮咚!今晚我想来点可不可的.....,不对,是Async,这是在Spring 2...
接续将关注焦点来到 Model 的主题,在您阅读本系列文章之前,您或许已有建模经验,在用於生产的机...
本来是想要实现config工具的,然而比较好用的配置文件的格式是xml,於是就先做一个xml的工具;...