Golang 并发下的问题定位及解决方案
来源:脚本之家    时间:2022-03-15 17:27:50
目录
问题描述解决方案实现思路2.1通过栈信息解析后获取2.2修改Go源码获取2.3通过CGO获取

问题描述

在使用 gin-swagger的过程中, 经常会发生因为缺少 jsontag而导致的异常。 由于 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-swaggermain.go源码, 修改 logWrite的实现

修改前

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。 只要使用 panicgoroutineid做一次 grep, 即可筛选出需要的日志了,极大的方便了定位问题。

cat /tmp/gin-swagger.log | grep 7647

到此这篇关于Golang 并发下的问题定位的文章就介绍到这了,更多相关Golang并发问题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

关键词: 解决方案 问题描述 希望大家 相关文章 使用的是

X 关闭

X 关闭