Welcome to my GitHub
https://github.com/zq2599/blog_demos
Content: Classification and summary of all original articles and supporting source code, involving Java, Docker, Kubernetes, DevOPS, etc.;
Overview of this article
- In the "Introduction to Jaeger Development (Java Edition)" , we have coded the creation and reporting of spans, as shown in the red box below. Although the amount of code is not large, writing these codes in business codes is too intrusive , Many programmers don’t like:
- Today, let’s try AOP+custom annotations to solve the above problems, as shown in the figure below, <font color="blue">mock</font> is a common method, and the annotations in the red box are added <font color="red">@MySpan </font>, the span will be created and reported to Jaeger, there is no change in the code of the mock method:
- Through the comparison of the above two pictures, it can be seen that the annotations are very concise. Next, we will actually combat the above methods. The specific steps are as follows:
- Create a new web project, which has the controller layer, call the service Biz of the service layer, and the service Biz calls another service ChildBiz
- Create two annotations <font color="blue">MySpan</font> and <font color="blue">MyChildSpan</font>
- Create the AOP class SpanAspect, responsible for processing all methods modified by annotations <font color="blue">MySpan</font> and <font color="blue">MyChildSpan</font>;
- Use the annotations <font color="blue">MySpan</font> and <font color="blue">MyChildSpan</font> on common services Biz and ChildBiz
- Start the service and verify the function;
Source download
- The complete source code of this actual combat can be downloaded on GitHub, the address and link information are shown in the following table ( https://github.com/zq2599/blog_demos):
name | Link | Remark |
---|---|---|
Project homepage | https://github.com/zq2599/blog_demos | The project's homepage on GitHub |
git warehouse address (https) | https://github.com/zq2599/blog_demos.git | The warehouse address of the source code of the project, https protocol |
git warehouse address (ssh) | git@github.com:zq2599/blog_demos.git | The warehouse address of the source code of the project, ssh protocol |
- There are multiple folders in this git project. The source code of this article is under the <font color="blue">spring-cloud-tutorials</font> folder, as shown in the red box in the following figure:
- There are multiple sub-projects under the <font color="blue">spring-cloud-tutorials</font> folder. The code for this article is <font color="red">jaeger-annonation-demo</font>, as shown below The red box shows:
coding
- Create a subproject of spring-cloud-tutorials <font color="blue">jaeger-annonation-demo</font>, and its pom.xml is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-tutorials</artifactId>
<groupId>com.bolingcavalry</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jaeger-annonation-demo</artifactId>
<dependencies>
<dependency>
<groupId>com.bolingcavalry</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
</plugins>
</build>
</project>
- Configuration file application.yml:
spring:
application:
name: jaeger-annonation-demo
opentracing:
jaeger:
enabled: true
udp-sender:
host: 127.0.0.1
port: 6831
server:
port: 18080
- Startup class:
package com.bolingcavalry.annonation;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AnnonationDemoApplication {
public static void main(String[] args) {
SpringApplication.run(AnnonationDemoApplication.class, args);
}
}
- The first interface Biz:
package com.bolingcavalry.annonation.service;
public interface ChildBiz {
void mockChild();
}
- Its realization:
package com.bolingcavalry.annonation.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class ChildBizImpl implements ChildBiz {
@Override
public void mockChild() {
log.info("mockChild");
}
}
- The second interface:
package com.bolingcavalry.annonation.service;
public interface Biz {
void mock();
}
- Its implementation, it can be seen that the mockChild method of childBiz is called in its mock method:
package com.bolingcavalry.annonation.service.impl;
import com.bolingcavalry.annonation.service.Biz;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class BizImpl implements Biz {
final ChildBiz childBiz;
public BizImpl(ChildBiz childBiz) {
this.childBiz = childBiz;
}
@Override
public void mock() {
log.info("mock");
childBiz.mockChild();
}
}
- Now a simple web service has been developed. There is a web layer and a service layer. It’s enough for us to experiment.
Definition annotation
- Define two annotations, one is used to create a span, the other is used to create a sub-span:
- The annotation <font color="blue">MySpan</font> is used to create a new span in the scene:
package com.bolingcavalry.annonation.aop;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface MySpan {
String spanName() default "";
}
- The second is MyChildSpan, which is used in scenarios where you want to create a child span under the current span:
package com.bolingcavalry.annonation.aop;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface MyChildSpan {
String spanName() default "";
}
- After the annotations are defined, AOP can be developed to handle the logic of the annotations
AOP and annotation development
- Next is the core of today: the AOP class that handles annotations. The Chinese annotations have been written in detail, so I won’t repeat them too much. The only thing to pay attention to is the annotations<font color="red">@Around("@annotation( mySpan)")</font>, which specifies that this method will handle all methods modified by <font color="blue">mySpan</font>:
package com.bolingcavalry.annonation.aop;
import io.opentracing.Span;
import io.opentracing.Tracer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class SpanAspect {
private Tracer tracer;
public SpanAspect(Tracer tracer) {
this.tracer = tracer;
}
/**
* 取得真实方法的相关信息
* @param proceedingJoinPoint
* @return
*/
private static String getOperationDesc(ProceedingJoinPoint proceedingJoinPoint) {
Signature signature = proceedingJoinPoint.getSignature();
// 提取类名
return StringUtils.substringAfterLast(signature.getDeclaringTypeName(), ".")
+ ":"
+ signature.getName();
}
@Around("@annotation(mySpan)")
public Object traceSpan(ProceedingJoinPoint proceedingJoinPoint, MySpan mySpan) throws Throwable {
// 类名:方法名
String operationDesc = getOperationDesc(proceedingJoinPoint);
// 看方法的注解中有没有设置name
String name = mySpan.spanName();
// 如果没有设置name,就给span一个默认name
if (StringUtils.isEmpty(name)) {
name = "span-aspect-" + operationDesc;
}
// 创建一个span,在创建的时候就添加一个tag
Span span = tracer.buildSpan(name).start();
// span日志
span.log("span log of " + operationDesc);
// 增加一个tag
span.setTag("operation-desc", operationDesc);
// span结束
span.finish();
return proceedingJoinPoint.proceed();
}
@Around("@annotation(myChildSpan)")
public Object traceChildSpan(ProceedingJoinPoint proceedingJoinPoint, MyChildSpan myChildSpan) throws Throwable {
// 类名:方法名
String operationDesc = getOperationDesc(proceedingJoinPoint);
// 看方法的注解中有没有设置name
String name = myChildSpan.spanName();
// 如果没有设置name,就给span一个默认name
if (StringUtils.isEmpty(name)) {
name = "child-span-aspect-" + operationDesc;
}
// 从上下文中取得已存在的span
Span parentSpan = tracer.activeSpan();
if (null==parentSpan) {
log.error("can not get span from context");
} else {
Span span = tracer.buildSpan(name).asChildOf(parentSpan).start();
// span日志
span.log("child span log of " + operationDesc);
// 增加一个tag
span.setTag("child-operation-desc", operationDesc);
// span结束
span.finish();
}
return proceedingJoinPoint.proceed();
}
}
- The annotations and AOP have been written, let’s use the annotations and see how it works
Use annotations
- Use MySpan to modify the BizImpl.mock method, and pay attention to setting its <font color="blue">spanName</font> property as the name of the span:
- Use MyChildSpan to modify the method ChildBizImpl.mockChild method to create a child span. Note that the annotation does not set any attributes. The AOP class will set the default name for this span:
- The above is all the code, let's run it and try it;
verify
- Start the Jaeger background service with docker:
docker run -d \
--name jaeger \
-e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
-p 5775:5775/udp \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 14268:14268 \
-p 14269:14269 \
-p 9411:9411 jaegertracing/all-in-one:1.26
- Start the application jaeger-annonation-demo
- Browser visits <font color="blue"> http://localhost:18080/hello </font> to trigger the execution of web interface classes and service classes
- Open the jaeger web page, the address is <font color="blue"> http://localhost:16686/search </font>, as shown in the figure below, it can be seen that the trace has been reported:
- Click on the record in the red box in the figure above, you can see the details of this trace, as shown in the figure below, the data is in line with expectations:
- At this point, the annotation-based Jaeger report is complete. This article only provides an idea and reference code. In practical applications, you can make more powerful annotation reports, such as adding richer annotation attributes and class-based The annotations, parameter-based annotations, etc., or even abandon the annotations, and directly use pure AOP for Jaeger reporting for specific packages, methods, etc.;
You are not lonely, Xinchen original is with you all the way
- Java series
- Spring Series
- Docker series
- kubernetes series
- Database + Middleware Series
- DevOps series
Welcome to pay attention to the public account: programmer Xin Chen
Search "Programmer Xin Chen" on WeChat, I am Xin Chen, and I look forward to traveling the Java world with you...
https://github.com/zq2599/blog_demos
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。