日志
1.记录用户操作,猜测用户行为
2.记录bug,方便维护维修
Gin框架自带的日志系统
package main
import "github.com/gin-gonic/gin"
func main() {
router := gin.Default()
//查看default的内置日志
router.GET("/index", func(c *gin.Context) {})
router.POST("/user", func(c *gin.Context) {})
router.POST("/article", func(c *gin.Context) {})
router.Run(":80")
}
↑上述代码的gin.Default
的内置日志,运行生成出来就是下面这样↓
[GIN-debug] GET /index --> main.main.func1 (3 handlers)
[GIN-debug] POST /user --> main.main.func2 (3 handlers)
[GIN-debug] POST /article --> main.main.func3 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :80
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :80
现在输出还在控制台里,我们可以把它改到一个日志文件里,这里就需要用到一个名为gin.DefaultWriter
,这是一个IO,它的源码会这样的:
var DefaultWriter io.Writer = os.Stdout
(注意,gin.DefaultWriter
要放在gin.Default
之前)
package main
import (
"github.com/gin-gonic/gin"
"io"
"os"
)
func main() {
//将日志生成进一个叫gin.log的文件中
file, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(file)
router := gin.Default()
//查看default的内置日志
router.GET("/index", func(c *gin.Context) {})
router.POST("/user", func(c *gin.Context) {})
router.POST("/article", func(c *gin.Context) {})
api := router.Group("api")
api.GET("/test", func(c *gin.Context) {})
router.Run(":80")
}
执行了上述代码后,就会发现控制台的内置日志没了,转而会发现根目录下生成出了一个gin.log
的文件,里面存放着原本在控制台输出的内置日志。(日志会根据运行及时写入进gin.log
文件中)
如果想要控制台和gin.log
文件中都有日志输出,则可以将gin.DefaultWriter
改成:
gin.DefaultWriter = io.MultiWriter(file, os.Stdout)
自定义路由格式
默认的路由格式:其实认真看默认的内置日志会发现我们写的路由都有在里面:
方法 请求的路径 对应的匿名函数
[GIN-debug] GET /index --> main.main.func1 (3 handlers)
[GIN-debug] POST /user --> main.main.func2 (3 handlers)
[GIN-debug] POST /article --> main.main.func3 (3 handlers)
[GIN-debug] GET /api/test --> main.main.func4 (3 handlers)
匿名函数里面的内容包括了main包、main函数中、func1函数、处理数量(一般来说默认为2,也就是没有新增自订处理的时候)
自定义路由格式:新加一个gin.DebugPrintRouteFunc
gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
log.Printf("[ RoLingG %s %s %s %s \n]", httpMethod, absolutePath, handlerName, nuHandlers)
}
package main
import (
"github.com/gin-gonic/gin"
"log"
)
func main() {
gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
log.Printf("[ RoLingG ] %s %s %s %s \n", httpMethod, absolutePath, handlerName, nuHandlers)
}
router := gin.Default()
//查看default的内置日志
router.GET("/index")
router.POST("/user", func(c *gin.Context) {})
router.POST("/article", func(c *gin.Context) {})
api := router.Group("api")
api.GET("/test", func(c *gin.Context) {})
router.Run(":80")
}
这样控制台的输出就会是:
//注意:index路径是什么都没写,Gin框架会给他一个默认函数进行日志生成。
2023/10/15 12:47:10 [ RoLingG ] GET /index github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1 %!s(int=2)
2023/10/15 12:47:10 [ RoLingG ] POST /user main.main.func2 %!s(int=3)
2023/10/15 12:47:10 [ RoLingG ] POST /article main.main.func3 %!s(int=3)
2023/10/15 12:47:10 [ RoLingG ] GET /api/test main.main.func4 %!s(int=3)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :80
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :80
查询路由:router.Route()
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
//查看default的内置日志
router.GET("/index")
router.POST("/user", func(c *gin.Context) {})
router.POST("/article", func(c *gin.Context) {})
api := router.Group("api")
api.GET("/test", func(c *gin.Context) {})
//查询路由
fmt.Println(router.Routes())
router.Run(":80")
}
这样控制台就会将路由打印出来:
[GIN-debug] GET /index --> github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1 (2 handlers)
[GIN-debug] POST /user --> main.main.func1 (3 handlers)
[GIN-debug] POST /article --> main.main.func2 (3 handlers)
[GIN-debug] GET /api/test --> main.main.func3 (3 handlers)
//打印出来的所有路由
[{GET /index github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1 0x7d91c0} {GET /api/test main.main.func3 0x7df2a0} {POST /user main.main.func1 0x7df260} {POST /article main.main.func2 0x7df280}]
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :80
用for range循环打印方式:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
//查看default的内置日志
router.GET("/index")
router.POST("/user", func(c *gin.Context) {})
router.POST("/article", func(c *gin.Context) {})
api := router.Group("api")
api.GET("/test", func(c *gin.Context) {})
////查询路由
//fmt.Println(router.Routes())
for _, info := range router.Routes() {
fmt.Println(info.Path, info.Method, info.Handler)
}
router.Run(":80")
}
这样的方式打印出来在控制台是这样的:
/index GET github.com/gin-gonic/gin.CustomRecoveryWithWriter.func1
/api/test GET main.main.func3
/user POST main.main.func1
/article POST main.main.func2
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :80
如果想让它显示这么多的Gin-Debug,可以用gin.SetMode(gin.ReleaseMode)
,因为默认的环境就是Debug模式。(内置日志里有提示)
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
加了gin.SetMode(gin.ReleaseMode)
之后的效果如下:
GOROOT=D:\GoLand\SDK\go1.20.3 #gosetup
GOPATH=D:\GoLand #gosetup
D:\GoLand\SDK\go1.20.3\bin\go.exe build -o C:\Users\RoLingG\AppData\Local\Temp\GoLand\___go_build_Gogin_Gogin_.exe D:\GoLand\Gogin\Gogin\视图\11.日志.go #gosetup
C:\Users\RoLingG\AppData\Local\Temp\GoLand\___go_build_Gogin_Gogin_.exe
有请求才有日志生成。
修改log的显示
一般的log日志:
[GIN] 2023/10/15 - 20:02:08 | 404 | 0s | 127.0.0.1 | GET "/api/login"
[GIN] 2023/10/15 - 20:02:30 | 404 | 0s | 127.0.0.1 | GET "/api/index"
[GIN] 2023/10/15 - 20:02:36 | 200 | 0s | 127.0.0.1 | GET "/index"
[GIN] 2023/10/15 - 20:02:51 | 404 | 0s | 127.0.0.1 | POST "/users"
[GIN] 2023/10/15 - 20:02:56 | 200 | 0s | 127.0.0.1 | POST "/user"
一般来说ip得是请求的客户ip,但一般内置的log不能这样显示,这时候就需要自己改,将客户的ip通过请求头的方式传递过来。
自定义log:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
//New方便自定义log
router := gin.New()
router.Use(gin.LoggerWithFormatter(func(params gin.LogFormatterParams) string {
//自定义log在控制台的输出格式
return fmt.Sprintf("[RoLingG] %s - %d - %s - %s - %s \n", params.TimeStamp.Format("2006-01-02 15:04:05"), params.StatusCode, params.ClientIP, params.Method, params.Path)
}))
//查看default的内置日志
router.GET("/index")
router.POST("/user", func(c *gin.Context) {})
router.POST("/article", func(c *gin.Context) {})
api := router.Group("api")
api.GET("/test", func(c *gin.Context) {})
router.Run(":80")
}
//自定义的log输出形式
[RoLingG] 2023-10-15 20:13:44 - 200 - 127.0.0.1 - GET - /index
但自己自定义的没有高亮显示,要自己搞一搞,涉及到Gin框架的源码以及控制台的特殊颜色输出写法。
↓下面就是还原gin.Default
的那种高亮显示的代码↓:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
//New方便自定义log
router := gin.New()
router.Use(gin.LoggerWithFormatter(func(params gin.LogFormatterParams) string {
return fmt.Sprintf("[RoLingG] %s - %d - %s - %s%s%s - %s \n",
params.TimeStamp.Format("2006-01-02 15:04:05"),
params.StatusCodeColor(), params.StatusCode, params.ResetColor(),
params.ClientIP,
params.MethodColor(), params.Method, params.ResetColor(),
params.Path)
}))
//查看default的内置日志
router.GET("/index")
router.POST("/user", func(c *gin.Context) {})
router.POST("/article", func(c *gin.Context) {})
api := router.Group("api")
api.GET("/test", func(c *gin.Context) {})
router.Run(":80")
}
%s%s%s
的原因是因为params.MethodColor()
传过来的是类似\033[97;42m
这样的一串特殊的控制台颜色输出代码,在这种代码后面的字符串都会有相对应的颜色,所以接上params.Method
就会有类似高亮显示的效果,但之后的显示又不要高亮显示,则需要params.ResetColor()
进行颜色重置。(为了美观可以%s %s %s
之间隔开来,会让颜色散开一点)
为了代码简洁明显化,可以把LogFormatterParams
函数化:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func LogFormatterParams(params gin.LogFormatterParams) string {
return fmt.Sprintf("[RoLingG] %s - %s %d %s - %s - %s %s %s - %s \n",
params.TimeStamp.Format("2006-01-02 15:04:05"),
params.StatusCodeColor(), params.StatusCode, params.ResetColor(),
params.ClientIP,
params.MethodColor(), params.Method, params.ResetColor(),
params.Path)
}
func main() {
//New方便自定义log
router := gin.New()
router.Use(gin.LoggerWithFormatter(LogFormatterParams))
//查看default的内置日志
router.GET("/index")
router.POST("/user", func(c *gin.Context) {})
router.POST("/article", func(c *gin.Context) {})
api := router.Group("api")
api.GET("/test", func(c *gin.Context) {})
router.Run(":80")
}
另一种写法(目前我没看出区别,但肯定有):
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func LogFormatterParams(params gin.LogFormatterParams) string {
return fmt.Sprintf("[RoLingG] %s - %s %d %s - %s - %s %s %s - %s \n",
params.TimeStamp.Format("2006-01-02 15:04:05"),
params.StatusCodeColor(), params.StatusCode, params.ResetColor(),
params.ClientIP,
params.MethodColor(), params.Method, params.ResetColor(),
params.Path)
}
func main() {
//New方便自定义log
router := gin.New()
//router.Use(gin.LoggerWithFormatter(LogFormatterParams))
//另一种写法,在这里效果基本一样
router.Use(gin.LoggerWithConfig(gin.LoggerConfig{Formatter: LogFormatterParams}))
//查看default的内置日志
router.GET("/index")
router.POST("/user", func(c *gin.Context) {})
router.POST("/article", func(c *gin.Context) {})
api := router.Group("api")
api.GET("/test", func(c *gin.Context) {})
////查询路由
//fmt.Println(router.Routes())
//for _, info := range router.Routes() {
// fmt.Println(info.Path, info.Method, info.Handler)
//}
router.Run(":80")
}
——————————————————————————————————————————————
Logrus
目前比较火的一个第三方日志组件库。
package main
import "github.com/sirupsen/logrus"
func main() {
logrus.Error("出错了")
logrus.Warnln("警告")
logrus.Infof("信息")
logrus.Debugf("Debug")
logrus.Println("输出")
}
但是会发现输出只有四行,debug没了:
time="2023-10-15T21:22:36+08:00" level=error msg="出错了"
time="2023-10-15T21:22:36+08:00" level=warning msg="警告"
time="2023-10-15T21:22:36+08:00" level=info msg="信息"
time="2023-10-15T21:22:36+08:00" level=info msg="输出"
原因是因为日志默认等级的问题,用logrus.GetLevel()
可以看最低优先级,会发现最低是info,则比info小的都不会输出。
源码优先级设定:
const (
// PanicLevel level, highest level of severity. Logs and then calls panic with the
// message passed to Debug, Info, ...
PanicLevel Level = iota
// FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the
// logging level is set to Panic.
FatalLevel
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
// Commonly used for hooks to send errors to an error tracking service.
ErrorLevel
// WarnLevel level. Non-critical entries that deserve eyes.
WarnLevel
// InfoLevel level. General operational entries about what's going on inside the
// application.
InfoLevel
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
//下两个无输出
DebugLevel
// TraceLevel level. Designates finer-grained informational events than the Debug.
TraceLevel
)
//从上到下优先级依次降低
//默认输出最低优先级是info,debug和trace都不算在里面,因为没有默认的输出。
我们可以通过logrus.SetLevel()
进行优先级的更改
package main
import (
"fmt"
"github.com/sirupsen/logrus"
)
func main() {
//将最底线优先改为Debug
logrus.SetLevel(logrus.DebugLevel)
logrus.Error("出错了")
logrus.Warnln("警告")
logrus.Infof("信息")
logrus.Debugf("Debug")
logrus.Println("输出")
fmt.Println(logrus.GetLevel())
}
//这样会发现优先级底线是Debug,其他也能输出在控制台中。
//但如果本身优先级就高的设置为底线优先级,则比它低的都不会输出出来。
设置特定的字段
package main
import "github.com/sirupsen/logrus"
func main() {
//↓会返回一个Entry
log := logrus.WithField("app", "study")
log.Errorf("你好,但是报错")
}
//上述代码会给日志输出加上自定义的字段
time="2023-10-15T21:38:55+08:00" level=error msg="你好,但是报错" app=study
甚至它还可以链式写入:
log := logrus.WithField("app", "study").WithField("service", "logrus")
写多个特定字段也可以用logrus.WithFields
:
package main
import "github.com/sirupsen/logrus"
func main() {
//↓会返回一个Entry
log := logrus.WithField("app", "study").WithField("service", "logrus")
//它的写入是以个map,它可以用在中间件获取ip直接利用。
logs := logrus.WithFields(logrus.Fields{
"user_name": "RoLingG",
"user_age": 26,
"ip": "192.168.1.1",
})
log.Errorf("你好,但是报错")
logs.Errorf("你好啊家人,但是报错")
}
输出是这样:
time="2023-10-15T21:48:21+08:00" level=error msg="你好,但是报错" app=study service=logrus
time="2023-10-15T21:48:21+08:00" level=error msg="你好啊家人,但是报错" ip=192.168.1.1 user_age=26 user_name=RoLingG
当然还可以套娃:
package main
import "github.com/sirupsen/logrus"
func main() {
log1 := logrus.WithField("app", "study").WithField("service", "logrus")
//它的写入是以个map,它可以用在中间件获取ip直接利用。
logs := log1.WithFields(logrus.Fields{
"user_name": "RoLingG",
"user_age": 26,
"ip": "192.168.1.1",
})
log1.Errorf("你好,但是报错")
logs.Errorf("你好啊家人,但是报错")
}
通常在一个应用中、或者应用的一部分中,都有一些固定的Field,比如在处理用户的http请求时,行下文中,所有的日志都会有request_id和user_ip,为了避免每次记录日志都要会用log.WithFields({"request_id": request_id, "user_ip": user_ip})
,我们可以创建一个logrus.Entry的实例,为这个实例设置默认的Field,在上下文使用这个实例记录日志即可,免去麻烦繁琐的步骤。
显示样式
一般默认来说,logrus默认以text
的形式去显示日志,但我们也可以通过更改将其用json
的方式显示出来。
在正式使用前加入logrus.SetFormatter(&logrus.JSONFormatter{})
或logrus.SetFormatter(&logrus.TextFormatter{})
将其改为json格式或者text格式。
自定义颜色输出
原理:ANSI 控制码,用于设置文本颜色。\033 是控制码的开始,是八进制数字,[31m 表示将文本设置为红色。ANSI 控制码是用于在终端和控制台中控制文本格式和颜色的一种标准。它们通常用于在命令行界面 (CLI) 程序中输出彩色文本或者在文本模式下的图形界面 (GUI) 中输出文本。
颜色列表:
\033[30m 黑色 \033[0m
\033[31m 红色 \033[0m
\033[32m 绿色 \033[0m
\033[33m 黄色 \033[0m
\033[34m 蓝色 \033[0m
\033[35m 紫色 \033[0m
\033[36m 青色 \033[0m
\033[37m 灰色 \033[0m
// 背景色
\033[40m 黑色 \033[0m
\033[41m 红色 \033[0m
\033[42m 绿色 \033[0m
\033[43m 黄色 \033[0m
\033[44m 蓝色 \033[0m
\033[45m 紫色 \033[0m
\033[46m 青色 \033[0m
\033[47m 灰色 \033[0m
注意:首先日志输出格式得是text格式。
输入:logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
这样可以在控制台输出里有颜色了:
package main
import "github.com/sirupsen/logrus"
func main() {
//↓会返回一个Entry
log := logrus.WithField("app", "study").WithField("service", "logrus")
//它的写入是以个map,它可以用在中间件获取ip直接利用。
logs := logrus.WithFields(logrus.Fields{
"user_name": "RoLingG",
"user_age": 26,
"ip": "192.168.1.1",
})
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
log.Errorf("你好,但是报错")
logs.Errorf("你好啊家人,但是报错")
logrus.Error("出错了")
logrus.Warnln("警告")
logrus.Infof("信息")
logrus.Debugf("Debug")
logrus.Println("输出")
}
还有一些别的配置:
ForceColors:是否强制使用颜色输出。
DisableColors:是否禁用颜色输出。
ForceQuote:是否强制引用所有值。
DisableQuote:是否禁用引用所有值。
DisableTimestamp:是否禁用时间戳记录。
FullTimestamp:是否在连接到 TTY 时输出完整的时间戳。
TimestampFormat:用于输出完整时间戳的时间戳格式。
更改之后就能简单的达到添加出完整时间戳的样式啦:
package main
import "github.com/sirupsen/logrus"
func main() {
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true, TimestampFormat: "2006-01-02 15:04:05", FullTimestamp: true})
logrus.Error("出错了")
logrus.Warnln("警告")
logrus.Infof("信息")
logrus.Debugf("Debug")
logrus.Println("输出")
}
通过上述这个颜色列表,我们可以使用它们用来自定义输出颜色。
例如把Error的输出内容改成紫色:logrus.Error("\033[35m 出错了 \033[0m")
为了方便颜色的设置,我们还可以通过自定义函数去实现:
package main
import (
"fmt"
"github.com/sirupsen/logrus"
)
// 这里是根据源码定义颜色的序号
const (
cBlack = 0
cRed = 1
cGreen = 2
cYello = 3
cBlue = 4
cPurple = 5
cCyan = 6
cGray = 7
)
// 自定义颜色输出
func PrintColor(colorCode int, text string, isBackground bool) {
if isBackground != true {
fmt.Printf("\033[3%dm %s \033[0m", colorCode, text)
} else {
fmt.Printf("\033[4%dm %s \033[0m", colorCode, text)
}
// 也可以这样写,少一个括号和一个else,精简代码
// if isBackground != true {
// fmt.Printf("\033[3%dm %s \033[0m", colorCode, text)
// return
// }
// fmt.Printf("\033[4%dm %s \033[0m", colorCode, text)
}
func main() {
PrintColor(4, "内容颜色是蓝色", false)
PrintColor(4, "背景和内容都是蓝色", true)
// 也可以用const内的变量名做colorCode
PrintColor(cBlue, "背景和内容都是蓝色", true)
}
行号显示
没有行号,就无法准确获取日志的位置在哪
logrus.SetReportCaller(true)
控制台日志显示如下:
GOROOT=D:\GoLand\SDK\go1.20.3 #gosetup
GOPATH=D:\GoLand #gosetup
D:\GoLand\SDK\go1.20.3\bin\go.exe build -o C:\Users\RoLingG\AppData\Local\Temp\GoLand\___go_build_2__go.exe D:\GoLand\Gogin\Gogin\logrus\2.设置特定字段.go #gosetup
C:\Users\RoLingG\AppData\Local\Temp\GoLand\___go_build_2__go.exe
ERRO[2023-10-17 21:09:41]D:/GoLand/Gogin/Gogin/logrus/2.设置特定字段.go:52 main.main() 你好,但是报错 app=study service=logrus
ERRO[2023-10-17 21:09:41]D:/GoLand/Gogin/Gogin/logrus/2.设置特定字段.go:53 main.main() 你好啊家人,但是报错 ip=192.168.1.1 user_age=26 user_name=RoLingG
ERRO[2023-10-17 21:09:41]D:/GoLand/Gogin/Gogin/logrus/2.设置特定字段.go:55 main.main() 出错了
WARN[2023-10-17 21:09:41]D:/GoLand/Gogin/Gogin/logrus/2.设置特定字段.go:56 main.main() 警告
INFO[2023-10-17 21:09:41]D:/GoLand/Gogin/Gogin/logrus/2.设置特定字段.go:57 main.main() 信息
INFO[2023-10-17 21:09:41]D:/GoLand/Gogin/Gogin/logrus/2.设置特定字段.go:59 main.main() 输出
颜色是蓝色 颜色是蓝色
评论(0)