字节码基于JavaAgent的全链路监控五- ThreadLocal链路追踪
Posted 九师兄
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了字节码基于JavaAgent的全链路监控五- ThreadLocal链路追踪相关的知识,希望对你有一定的参考价值。
1.概述
转载:基于JavaAgent的全链路监控五《ThreadLocal链路追踪》
Google开源的Dapper链路追踪组件,并在2010年发表了论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,这篇文章是业内实现链路追踪的标杆和理论基础,具有非常大的参考价值。目前,链路追踪组件有Google的Dapper,Twitter 的Zipkin,以及阿里的Eagleeye (鹰眼)等,它们都是非常优秀的链路追踪开源组件。本文主要讲述如何在Spring Cloud Sleuth中集成Zipkin。在Spring Cloud Sleuth中集成Zipkin非常的简单,只需要引入相应的依赖和做相关的配置即可。
链路追踪Dapper 当业务程序代码在线上运行时,实例A、实例B、实例C,他们直接可能从上到下依次调用,为了能很好的监控程序的调用链路,我们需要对调用链路进行追踪监控。实例的外部可能是通过RPC、HTTP、SOCKET、WEBSERVICE等方式进行调用,内部是方法逻辑依次执行。外部例如http可以通过在头部写入追踪ID进行监控,内部使用threadlocal进行保存上下文关系。{ThreadLocal变量特殊的地方在于:对变量值的任何操作实际都是对这个变量在线程中的一份copy进行操作,不会影响另外一个线程中同一个ThreadLocal变量的值。}
2.环境准备
IntelliJ IDEA Community Edition 2019
jdk1.8.0_45 64位
3. #配置信息(路径相关修改为自己的)
配置位置:Run/Debug Configurations -> VM options
配置内容:-javaagent:-javaagent:/Users/lcc/IdeaProjects/lcc_work/test-byte-buddy/byte-buddy-v1x-javaagent-demo3/target/byte-buddy-v1x-javaagent-demo3-1.0-SNAPSHOT.jar=test
4.maven设置如下
<properties>
<asm.version>9.0</asm.version>
<slf4j-api.version>1.7.28</slf4j-api.version>
<byte-buddy.version>1.12.11</byte-buddy.version>
<!-- <byte-buddy.version>1.8.20</byte-buddy.version>-->
<fastjson.version>1.2.76</fastjson.version>
<lombok.version>1.18.16</lombok.version>
</properties>
<dependencies>
<!-- 日志 相关 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>$slf4j-api.version</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>$slf4j-api.version</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>$fastjson.version</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>$lombok.version</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>$byte-buddy.version</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>$byte-buddy.version</version>
</dependency>
<dependency>
<groupId>com.bytebuddy</groupId>
<artifactId>byte-buddy-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<addClasspath>true</addClasspath>
<!-- <Premain-Class>com.javaagent.bytebuddy.MyPreMainAgent</Premain-Class>-->
<!-- <Premain-Class>com.javaagent.bytebuddy.demo.MyAgent</Premain-Class>-->
<Premain-Class>com.javaagent.bytebuddy.demo4.TraceAgent</Premain-Class>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
5.主要类
TrackManager 设置如下
package com.javaagent.bytebuddy.demo4;
import java.util.Stack;
public class TrackManager
private static final ThreadLocal<Stack<String>> track = new ThreadLocal<Stack<String>>();
private static String createSpan()
Stack<String> stack = track.get();
if (stack == null)
stack = new Stack<>();
track.set(stack);
String linkId;
if (stack.isEmpty())
linkId = TrackContext.getLinkId();
if (linkId == null)
linkId = "nvl";
TrackContext.setLinkId(linkId);
else
linkId = stack.peek();
TrackContext.setLinkId(linkId);
return linkId;
public static String createEntrySpan()
String span = createSpan();
Stack<String> stack = track.get();
stack.push(span);
return span;
public static String getExitSpan()
Stack<String> stack = track.get();
if (stack == null || stack.isEmpty())
TrackContext.clear();
return null;
return stack.pop();
public static String getCurrentSpan()
Stack<String> stack = track.get();
if (stack == null || stack.isEmpty())
return null;
return stack.peek();
TrackContext设置如下
package com.javaagent.bytebuddy.demo4;
/**
* @author lcc
*/
public class TrackContext
private static final ThreadLocal<String> trackLocal = new ThreadLocal<String>();
public static void clear()
trackLocal.remove();
public static String getLinkId()
return trackLocal.get();
public static void setLinkId(String linkId)
trackLocal.set(linkId);
TraceAdvice设置如下
package com.javaagent.bytebuddy.demo4;
import net.bytebuddy.asm.Advice;
import java.util.UUID;
public class TraceAdvice
@Advice.OnMethodEnter()
public static void enter(@Advice.Origin("#t") String className, @Advice.Origin("#m") String methodName)
String linkId = TrackManager.getCurrentSpan();
if (null == linkId)
linkId = UUID.randomUUID().toString();
TrackContext.setLinkId(linkId);
String entrySpan = TrackManager.createEntrySpan();
System.out.println("链路追踪:" + entrySpan + " " + className + "." + methodName);
@Advice.OnMethodExit()
public static void exit(@Advice.Origin("#t") String className, @Advice.Origin("#m") String methodName)
TrackManager.getExitSpan();
TraceAgent设置如下
package com.javaagent.bytebuddy.demo4;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.NamedElement;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import java.lang.instrument.Instrumentation;
public class TraceAgent
//JVM 首先尝试在代理类上调用以下方法
public static void premain(String agentArgs, Instrumentation inst)
System.out.println("基于javaagent链路追踪");
AgentBuilder agentBuilder = new AgentBuilder.Default();
AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) ->
builder = builder.visit(
Advice.to(TraceAdvice.class)
.on(ElementMatchers.isMethod()
/**
* 测试发现,这里需要所有,不能做下面的改造 否则没有相关的效果
*/
.and(ElementMatchers.any())
// 这里相比原来的做了改造 我们只看我们这个包下的
// .and(ElementMatchers.nameStartsWith("com.javaagent.bytebuddy.demo4_1"))
.and(ElementMatchers.not(ElementMatchers.nameStartsWith("main")))));
return builder;
;
/**
* 这里要写我们的目标类,不能包括自身 TraceAgent 所在包,否则会报错如下
* "Thread-0" java.lang.StackOverflowError
* at com.javaagent.bytebuddy.demo4.TrackManager.getCurrentSpan(TrackManager.java:47)
*
*/
ElementMatcher.Junction<NamedElement> namedElementJunction = ElementMatchers.nameStartsWith("com.javaagent.bytebuddy.demo4_1");
AgentBuilder.Identified.Extendable transform = agentBuilder.type(namedElementJunction).transform(transformer);
// 1.12.11 版本使用这个
agentBuilder = transform.asTerminalTransformation();
// 1.8.20 版本使用这个
// agentBuilder = transform.asDecorator();
//监听
AgentBuilder.Listener listener = new AgentBuilder.Listener()
@Override
public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b)
@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType)
System.out.println("onTransformation:" + typeDescription);
@Override
public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b)
@Override
public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable)
@Override
public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b)
;
agentBuilder.with(listener).installOn(inst);
//如果代理类没有实现上面的方法,那么 JVM 将尝试调用该方法
public static void premain(String agentArgs)
6.测试类
这里注意测试类的包名称和主要类的名称不一致,不在一个包下,否则会报错
package com.javaagent.bytebuddy.demo4_1;
public class TraceAgentTest
public static void main(String[] args)
//线程一
new Thread(() -> new TraceAgentTest().http_lt1()).start();
//线程二
new Thread(() ->
new TraceAgentTest().http_lt1();
).start();
public void http_lt1()
System.out.println("测试结果:hi1");
http_lt2();
public void http_lt2()
System.out.println("测试结果:hi2");
http_lt3();
public void http_lt3()
System.out.println("测试结果:hi3");
7.运行结果
基于javaagent链路追踪
onTransformation:class com.javaagent.bytebuddy.demo4_1.TraceAgentTest
链路追踪:d9192c02-0fc7-437c-a47f-c150361dca19 com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt1
测试结果:hi1
链路追踪:7fde1ae3-95a0-4a34-a0d6-06fcbe0e596e com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt1
测试结果:hi1
链路追踪:d9192c02-0fc7-437c-a47f-c150361dca19 com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt2
测试结果:hi2
链路追踪:d9192c02-0fc7-437c-a47f-c150361dca19 com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt3
测试结果:hi3
链路追踪:7fde1ae3-95a0-4a34-a0d6-06fcbe0e596e com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt2
测试结果:hi2
链路追踪:7fde1ae3-95a0-4a34-a0d6-06fcbe0e596e com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt3
测试结果:hi3
以上是关于字节码基于JavaAgent的全链路监控五- ThreadLocal链路追踪的主要内容,如果未能解决你的问题,请参考以下文章
字节码基于JavaAgent的全链路监控六 基于jvmti定位java异常信 息
字节码基于JavaAgent的全链路监控四-JVM内存与GC信息
字节码JavaAgent的全链路监控篇二,通过字节码增加监控执行 耗时