什么?你让我自定义sw的traceId规则?

背景

公司最近引入skywalking(后面都简称:sw),由于之前项目的traceId是在controller用uuid生成的,所以老大想让sw的traceId规则也变成简单的uuid.

思路

  1. 先了解sw的traceId默认生成规则
  2. 去网上了解是否有更新默认traceId规则的方式
  3. 验证各种找到的方式

skywalking traceId 默认生成规则

默认规则:uuid + 线程号 + (时间戳 * 10000 +序列号)

其中uuid是项目启动的时候用来表示当前服务的一个标识。

查看源代码:(查看方式,下载skywalking-agent源码)Downloads | Apache SkyWalking

java 复制代码
public final class GlobalIdGenerator {
    private static final String PROCESS_ID = UUID.randomUUID().toString().replaceAll("-", "");
    private static final ThreadLocal<IDContext> THREAD_ID_SEQUENCE = ThreadLocal.withInitial(
        () -> new IDContext(System.currentTimeMillis(), (short) 0));

    private GlobalIdGenerator() {
    }

    /**
     * Generate a new id, combined by three parts.
     * <p>
     * The first one represents application instance id.
     * <p>
     * The second one represents thread id.
     * <p>
     * The third one also has two parts, 1) a timestamp, measured in milliseconds 2) a seq, in current thread, between
     * 0(included) and 9999(included)
     *
     * @return unique id to represent a trace or segment
     */
    public static String generate() {
        return StringUtil.join(
            '.',
            PROCESS_ID,
            String.valueOf(Thread.currentThread().getId()),
            String.valueOf(THREAD_ID_SEQUENCE.get().nextSeq())
        );
    }

    private static class IDContext {
        private long lastTimestamp;
        private short threadSeq;

        // Just for considering time-shift-back only.
        private long lastShiftTimestamp;
        private int lastShiftValue;

        private IDContext(long lastTimestamp, short threadSeq) {
            this.lastTimestamp = lastTimestamp;
            this.threadSeq = threadSeq;
        }

        private long nextSeq() {
            return timestamp() * 10000 + nextThreadSeq();
        }

        private long timestamp() {
            long currentTimeMillis = System.currentTimeMillis();

            if (currentTimeMillis < lastTimestamp) {
                // Just for considering time-shift-back by Ops or OS. @hanahmily 's suggestion.
                if (lastShiftTimestamp != currentTimeMillis) {
                    lastShiftValue++;
                    lastShiftTimestamp = currentTimeMillis;
                }
                return lastShiftValue;
            } else {
                lastTimestamp = currentTimeMillis;
                return lastTimestamp;
            }
        }

        private short nextThreadSeq() {
            if (threadSeq == 10000) {
                threadSeq = 0;
            }
            return threadSeq++;
        }
    }
}

网上查询到的方式

1. skywalking traceId如何自定义生成规则?_问答-阿里云开发者社区

调研结果:评论说是ai回答的,乱答的,没用。

2. 【SkyWalking】如何自定义TraceId文章介绍了在SkyWalking中,如何基于SW8协议自定义TraceI - 掘金

调研结果:这篇文章说的太简单了,太抽象了,而且基于sw8协议的更改,链路收集到的数据会有问题。

实测:链路的跨度会有问题,所以放弃。

问大佬了

实在没办法了,就放技术群里问了下,有个好心老哥帮我看了下。

主要思路是:在我们项目里自己弄一个和GlobalIdGenerator类的全路径一模一样的,然后根据父委托机制可以重写我们项目里自己的GlobalIdGenerator类。

这个有点奇怪,我直接在idea里运行时可以覆盖掉skywalking-agent里的GlobalIdGenerator类的,但是将项目打成jar包就覆盖不了了。

修改源码

直接将下载下来的skywalking-agent源码进行修改。

很简单只需要修改GlobalIdGenerator中的generate方法即可。

java 复制代码
public static String generate() {
    return UUID.randomUUID().toString().toUpperCase();
}

然后重新打包。

这种方式是可以的,但是老大说尽量不要改动源码的方式去实现。

再写一个探针

主要思路:在同事的提醒下,他说你可以自己再写一个探针,然后控制自己写的探针和skywalking探针的加载顺序,然后在自己写的那个探针里定义好一个GlobalIdGenerator类中的generate方法重写。

经过验证,这种方式是可以完成自定义traceId规则的,但是其实还是修改了源码,只不过是字节码层面的源码,老大最后说还是用sw的默认traceId。

贴一下自己实现探针的源码:

java 复制代码
public class TraceIdAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
        new AgentBuilder.Default()
        // 找到以GlobalIdGenerator结尾的类全限定名
                .type(ElementMatchers.nameEndsWith("GlobalIdGenerator"))
                .transform((builder, type, classLoader, module, protectionDomain) ->
                // 拦截到里面的 generate方法
                        builder.method(ElementMatchers.named("generate"))
                        // 替换成这个
                                .intercept(MethodDelegation.to(Interceptor.class)))
                .installOn(inst);
    }

    // 重写我们的目标方法
    public static class Interceptor {
        public static String generate() {
            return java.util.UUID.randomUUID().toString().replaceAll("-", "");
        }
    }
}

总结

简单的讲述了几种可以自定义的traceId的方法:

  1. 修改sw8跨进程头部协议,这个方式会让skywalking收集到错误的信息,如:链路的跨度信息。
  2. 根据父委托机制,在项目里重写一个GlobalIdGenerator类,自定义规则。(打成jar包不生效了,目前不知道原因)
  3. 重写源码,这是最简单直接的方式。
  4. 自定义一个探针,在字节码层面上对生成traceId的方法进行拦截。

其中3,4是肯定可以的,特别是方法3。

相关推荐
麻瓜生活睁不开眼8 小时前
Android16修改全局桌面视图边框四直角显示为弧边圆角
android·java·深度学习
dear_bi_MyOnly8 小时前
【MyBatis 操作数据库】
java·数据库·学习·mybatis·学习方法
2601_953720828 小时前
【计算机毕业设计】基于Spring Boot的心理健康咨询与评测系统的设计与实现
java·spring boot·后端
丈剑走天涯9 小时前
JDK 17 正式特性
java·开发语言
东方小月9 小时前
从零开发一个 Coding Agent(四):使用状态机校验大模型事件流
前端·人工智能·后端
圣光SG9 小时前
Java操作题练习(七)
java·开发语言·算法
都叫我大帅哥10 小时前
“让AI修就行”?我劝你收回这句话——来自一位Java博主的硬核反常识
后端
whyfail10 小时前
前端学 Spring Boot(8):接口为什么越用越慢?
前端·spring boot·后端
码字的特恩10 小时前
AI Agent Skill 工程化 09:让 Skill 自己变好——走向自进化流水线
人工智能·计算机·ai·程序员·大模型·互联网·编程