Go 语言如果没有 ctx 传递,如何让 Trace ID 连贯传递呢? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
RedisMasterNode

Go 语言如果没有 ctx 传递,如何让 Trace ID 连贯传递呢?

  •  
  •   RedisMasterNode 2023 年 8 月 24 日 3167 次点击
    这是一个创建于 966 天前的主题,其中的信息可能已经有所发展或是发生改变。

    RT 对于 Go (这类)没有 ThreadLocal 的语言,如果在服务方法调用之间缺漏了 ctx context.Context 的传递,Trace 的信息就无法传递下去。

    通常来说对于平台、工具方来说(例如做 Trace 平台的人),想推动所有业务都去改造是相当困难的,请教一下了解的 V 友有什么办法能 “改善” 这种情况?

    28 条回复    2023-09-07 12:31:12 +08:00
    juzzle
        1
    juzzle  
       2023 年 8 月 24 日
    用一个全局的 map ,key 是 goroutine 的唯一 Id ,value 是 RequestId
    RedisMasterNode
        2
    RedisMasterNode  
    OP
       2023 年 8 月 24 日
    @juzzle 我想了一下,这种方案可以在流量进来和流量出去的时候为(可能是空白的) ctx 补充 trace id ,但是印象中:
    1. goroutine 没有唯一 ID ,之前了解到是故意这样设计的,不知道这种情况后来有没有改进
    2. goroutine 之间的 trace id (可能)不容易传递

    其实更想了解有没有一些无侵入,业务不感知的方案,能够达到同 goroutine 内补全 trace id 的效果(也就是可以不考虑问题 2 )
    danbai
        3
    danbai  
    PRO
       2023 年 8 月 24 日 via Android
    流量进来从堆栈拿 gid 出去再判断。这是魔法
    Nazz
        5
    Nazz  
       2023 年 8 月 24 日
    错误往上抛, 最后在中间件里面打印日志, 中间件里面可以访问 ctx 拿到 trace_id
    RedisMasterNode
        6
    RedisMasterNode  
    OP
       2023 年 8 月 24 日
    @Nazz 不是,哥们,那你这个 trace id 如何传递给下一个调用方呢?
    RedisMasterNode
        7
    RedisMasterNode  
    OP
       2023 年 8 月 24 日
    @danbai 记下魔法了,跟 1 楼其实是一样的。让我们猜猜业务方愿不愿意改 orz
    JustSong
        8
    JustSong  
       2023 年 8 月 24 日 via Android
    ctx 肯定要传啊
    RedisMasterNode
        9
    RedisMasterNode  
    OP
       2023 年 8 月 24 日
    @JustSong 这个东西就跟你和刚用 Redis 的人讨论可不可以使用 `KEYS` 一样的,不可能要求所有人立刻就知道这个东西重要不重要,可行与否,以及是否强制要传。等发现问题要改的时候往往是很难改得动,所以才会有很强的需求寻找如何优雅解决,而不是反推他们 “全部做对”(当然,全部做对肯定是最好最好的,不能因为难就否认)。
    qloog
        10
    qloog  
       2023 年 8 月 24 日
    如果真不想全改,可以改一些有网络请求的组件,比如数据库,http client, redis 之类的,调用方只要升级核心组件即可,其他方法有好多其实也可以不用传递,毕竟没有网络请求。如果真想全部 trace, 建议加上 ctx, 长期来看还是有好处的,长痛不如短痛。
    Nazz
        11
    Nazz  
       2023 年 8 月 24 日 via Android
    @RedisMasterNode 得传递 ctx
    odirus
        12
    odirus  
       2023 年 8 月 24 日
    负责过类似的工作,手动传最稳定、可靠。要是担心的话在测试环境拦截出口、入口日志,分析是否有缺失的,有缺失的就告警出来。
    kkhaike
        13
    kkhaike  
       2023 年 8 月 24 日   2
    使用 golangci-lint 的 contextcheck
    https://golangci-lint.run/usage/linters/
    https://github.com/kkHAIKE/contextcheck

    这个是我写的
    crysislinux
        14
    crysislinux  
       2023 年 8 月 24 日
    我倾向于显示传递,其实五楼推荐的办法是合适的,业务抛出业务相关的数据。中间件里输出信息的时候带上 reqId 。但是那种业务上要传的比如 tenant id 就没办法了。显式的传好了。
    RedisMasterNode
        15
    RedisMasterNode  
    OP
       2023 年 8 月 24 日
    @odirus
    @kkhaike
    谢谢两位朋友的建议,是个可以实施并且可以慢慢推的方式,可能落地起来还需要搭配一些宣讲一点点改善
    RedisMasterNode
        16
    RedisMasterNode  
    OP
       2023 年 8 月 24 日
    @crysislinux 五楼这个是不好的解决方案,视角很局限,我们想解决的是业务间传递的问题,而对于单一的服务,如果它自己有这样的需求,它肯定有办法独立解决,向上抛错误也好,补齐 ctx 也罢。

    而在跨团队协作里面推动所有人完成一件事是比较困难的,一个单元没解决好,大家都没办法拿到最好的结果。

    另外回归到方案本身,它是不太有实践价值的,试想你现在只在 request log 显示了 trace id ,其他所有日志都没 trace id ,你如何串联所有日志?那,这个 trace id 只出现在 request log 的意义又是什么,岂不是跟没有 id 一样了。
    crysislinux
        17
    crysislinux  
       2023 年 8 月 24 日 via Android
    @RedisMasterNode 这个需求其实很普遍,一搜遍地都是,但是绝大部分框架都没提供一个方案,Java 我不是很熟悉不说了。nodejs 这边 async context 没进标准,nodejs 自己的 async hooks 可靠性不够,这个 feature 本身推进的也慢。可能业界并不觉得这是一个很值得解决的问题。。不过从业务的角度来说有这个东西确实会很方便。
    matrix1010
        18
    matrix1010  
       2023 年 8 月 24 日
    teli
        19
    teli  
       2023 年 8 月 24 日
    有个办法,拿到 goroutine 的 id ,不知道还有没有效
    xiaocaiji111
        20
    xiaocaiji111  
       2023 年 8 月 25 日
    如果想要 threadLocal 这种效果只能用魔法拿到 gid ,不过好像不太稳定
    我们开始项目小也没有考虑过 traceId ,后面项目中函数参数不管用不用的到默认第一个就是 context ,再后来做电商生态等原因回归了 java 。
    mrgeneral
        21
    mrgeneral  
       2023 年 8 月 25 日
    无它,规范耳。

    另外一个就是放在 mesh 层做,对业务透明。
    RedisMasterNode
        22
    RedisMasterNode  
    OP
       2023 年 8 月 25 日
    @mrgeneral mesh 层如何将 in & out 的流量进行关联呢?
    例如我是服务 B ,收到了一个服务 A 的请求,处理,然后向服务 C 发一个请求,mesh 的基础设施如何保证发给 C 的请求携带的 TraceID 与 A 发给我的 TraceID 一样呢?
    ensonmj
        23
    ensonmj  
       2023 年 8 月 26 日 via iPhone
    直接告诉他们你们 latency 很高,需要优化。自己去分析哪儿是瓶颈,他们就想到用了
    bli22ard
        24
    bli22ard  
       2023 年 8 月 31 日
    要有 tid ,老老实实显式传递。不单是 tid 的问题,还有 log 的问题。 最佳实战就是 ctx ,携带 log 和 tid
    paceewang1
        25
    paceewang1  
       2023 年 9 月 5 日
    之前也遇到过这个问题,用魔法取 gid 不可取,只能通过传递变量的方式进行,其实这里也有两种实现:
    1 、到底是传递 context ,很多框架用这种,但是实际的 Log 方法参数的时候要带上 ctx ,并且如果需要携带的 trace 多的话感觉效率不高
    2 、传递 log 变量呢,有一些框架用这种,第一眼看到也觉得很新奇,个人感觉用 log 变量传递的话能更高效的携带各种 trace ,并且 Log 方法参数和普通日志库都一致
    RedisMasterNode
        26
    RedisMasterNode  
    OP
       2023 年 9 月 5 日
    @paceewang1 我们之前在业务团队的时候倾向于用 ctx 传递,因为第三方库的方法签名不可控不可改,但是基本都保留了 ctx 。如果用 log 传递,或者说把 log 变量放在结构体内传递,它是对 ctx 的隐式使用,而且是 hardcode 的使用,无法分离出来给第三方方法用(或者说成本不如方法 1 )

    而这个帖子,其实不是想讨论如何改成传递 ctx ,而是说不发生任何业务修改的情况下如何拦截采集这种信息。因为想改动的人不是业务团队,也就是不是写这个代码的人,而是平台提供方,如何在极低成本(例如,只修改 cmd/main.go 方法里面的一两行)的目标下完成改造,(尽可能)让所有调用、日志都携带 trace 信息。

    另外我们之前选用方法 1 做业务层面的改造时,log.Info(string) 改成了 log.Info(context.Context, string),这个没啥好办法,都是全局的字符串替换修改调用方的,修改成了 context.TODO() 传入,然后等未来慢慢补全,实际落地下来感觉效果还可以的,而且有利于整体习惯养成。用 log 传递那是不是又不用设计 ctx 在方法签名里了?实际上这在 go 里是个不好的实践,相当于用 log 传递的方式,节约了 log.Info 调用的改造成本,但是继续保留了不传 ctx 的坏习惯,个人认为不可取。
    kingofzihua
        27
    kingofzihua  
       2023 年 9 月 7 日
    @RedisMasterNode #26 你看下 https://skywalking.apache.org/docs/skywalking-go/next/en/concepts-and-designs/key-principles/ 能否满足你的要求,看文档 skywalking go 使用起来貌似没有需要业务方做大量的改动,你你参考下他的实现
    RedisMasterNode
        28
    RedisMasterNode  
    OP
       2023 年 9 月 7 日
    @kingofzihua 很高兴在这里看到有人提及 skywalking go ,我也是其中的一位 contributor ,我觉得混合编译及代码生成是个很有创意的 idea ,因为项目比较新,短期内也没办法直接推广到公司用,未来看看其他使用方的反馈
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2783 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 194ms UTC 15:34 PVG 23:34 LAX 08:34 JFK 11:34
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86