什么?你让我自定义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。

相关推荐
网安墨雨几秒前
网络安全之命令
java·运维·web安全
考虑考虑1 分钟前
UNION和UNION ALL的用法与区别
数据库·后端·mysql
sd213151215 分钟前
springboot3 spring security+jwt实现接口权限验证实现
java·后端·spring
张国荣家的弟弟15 分钟前
【Yonghong 企业日常问题07 】 东方通TongWeb替代Tomcat的实战指南!
java·tomcat
局外人_Jia16 分钟前
Tomcat 新手入门指南
java·tomcat
Dreamboat-L16 分钟前
手写Tomcat
java·tomcat
出门撞大运17 分钟前
手写一个简易版的tomcat
java·tomcat
m0_7482480219 分钟前
Spring Boot 集成 MyBatis 全面讲解
spring boot·后端·mybatis
A阳俊yi19 分钟前
SpringMVC中有关请求参数的问题(映射路径,传递不同的参数)
java·前端·javascript
qq_4476630519 分钟前
《Spring日志整合与注入技术:从入门到精通》
java·开发语言·后端·spring