问题描述
在使用 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并发问题内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!
黄山市民网:https://www.huangshanshimin.com/