问题描述
在使用 gin-swagger
的过程中, 经常会发生因为缺少 json
等 tag
而导致的异常。 由于 gin-swagger
是并发执行的, 输出的日志本身是错位的。 这就导致无法定义是哪一个结构体缺少 tag
导致的。
一般而言, 这种时候只能一个个点开去检查。
解决方案
思路 : 要是每行日志带当前 goroutine_id
的话, 是不是就可以准确定位到报错的 goroutine
他打印的日志是哪些了呢!
说做就做
实现思路
- 查看当前日志是怎么打印的
发现 gin-swagger
日志直接调用的 golang
的标准库 log
由于没有对log初始化, 所以默认使用的是 stdout
。
log.Printf("Picking operation from %s\n", aurora.Blue(funType.FullName()))
- 如果想要给日志中添加
goroutine_id
的话, 就需要在调用log.Printf
的时候获取当前goroutine
的 id , 所以首先要解决的是怎么获取goroutine_id
的问题。
调研后发现了集中常见的获取 goroutine_id
的方法:
2.1 通过栈信息解析后获取
func GetGID() uint64 {
b := make([]byte, 64)
b = b[:runtime.Stack(b, false)]
b = bytes.TrimPrefix(b, []byte("goroutine "))
b = b[:bytes.IndexByte(b, ' ')]
n, _ := strconv.ParseUint(string(b), 10, 64)
return n
}
2.2 修改 Go 源码获取
# src/runtime/runtime2.go
func Goid() int64 {
_g_ := getg()
return _g_.goid
}
2.3 通过 CGO 获取
文件 id.c
#include "runtime.h"
int64 ·Id(void) {
return g->goid;
}
文件 id.go
package id
func Id() int64
另外还可以通过汇编获取 goroutine_id
由于go的版本不同,goroutine的结构也可能不同, 所以此处我直接调用一个开源实现:
https://github.com/petermattis/goid
- 修改
gin-swagger
main.go
源码, 修改log
Write
的实现
修改前
func main() {
cmd.Execute()
}
修改后
type Out os.File
func (o Out) Write(b []byte) (int, error) {
prefix := fmt.Sprintf("gid-%d: ", goid.Get())
all := make([]byte, len(b)+len(prefix))
all = []byte(prefix)
all = append(all, b...)
f := os.File(o)
return f.Write(all)
}
func main() {
var out Out
out = Out(os.Stdout)
log.SetOutput(out)
cmd.Execute()
这样修改后,每次 gin-swagger
调用 log
打印日志都时候, 都会使用我定义的 Write
方法, Write
方法中每次打印前都会先查询出当前的 goroutine_id
,然后作为日志的前缀。
修改后的日志效果
从中可以看到每行日志开头,都已经加上了 goroutine_id
。 只要使用 panic
goroutine
的 id
做一次 grep
, 即可筛选出需要的日志了,极大的方便了定位问题。
cat /tmp/gin-swagger.log | grep 7647
到此这篇关于Golang 并发下的问题定位的文章就介绍到这了,更多相关Golang并发问题内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!