ddtrace-api 使用指南¶
Attention
当前案例使用 ddtrace 版本0.114.0
(最新版本)进行测试
添加 maven pom 依赖¶
<dependency>
<groupId>com.datadoghq</groupId>
<artifactId>dd-trace-api</artifactId>
<version>0.114.0</version>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-api</artifactId>
<version>0.33.0</version>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-mock</artifactId>
<version>0.33.0</version>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-util</artifactId>
<version>0.33.0</version>
</dependency>
函数级别埋点¶
除了dd.trace.methods
方式可以对方法进行主动埋点外,ddtrace 提供了 api 方式能够对业务进行更灵活的埋点。
在对应需要埋点的方法添加注解 @Trace
然后在 gateway
方法调用这个
注意:入侵式埋点不代表应用启动的时候不需要 agent ,如果没有agent, @Trace
也将失效。@Trace
注释具有默认操作名称 trace.annotation
,而跟踪的方法默认具有资源。
可以修改对应的名称
@Trace(resourceName = "apiTrace",operationName = "apiTrace")
public String apiTrace(){
return "apiTrace";
}
使用 Baggage 让业务关键 tag 在后端链路进行传递¶
ddtrace 提供了Baggage
方式,准确的说,应该是 ddtrace 使用 opentracing 提供的Baggage
功能,让指定的 tag 在链路上进行传递。比如用户名、岗位等信息,方便分析用户行为。
span.setBaggageItem("username","liurui");
编写 TraceBaggageFilter¶
通过 TraceBaggageFilter 方式拦截请求,并将 request header 相关参数通过 baggage 方式进行传递。
package com.zy.observable.ddtrace;
import io.opentracing.Span;
import io.opentracing.util.GlobalTracer;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Enumeration;
/**
* Baggage 可以让 tag 在链路之间进行传递,通过获取当前请求header,将指定前缀的header设置为Baggage
* @author liurui
* @date 2022/7/19 14:59
*/
@Component
public class TraceBaggageFilter implements Filter {
/**
* 指定前缀的 header 进行链路传递
*/
private static final String PREFIX = "dd-";
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
final Span span = GlobalTracer.get().activeSpan();
if (span != null) {
HttpServletRequest request = (HttpServletRequest)servletRequest;
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
final String header = headerNames.nextElement();
String value = request.getHeader(header);
if (StringUtils.startsWith(header,PREFIX) && StringUtils.isNotBlank(value)){
//Baggage 可以在链路之间进行传递,而普通的tag不行
span.setBaggageItem(header.replace(PREFIX,""),value);
}
}
}
filterChain.doFilter(servletRequest,servletResponse);
}
}
datakit 配置¶
这里需要配合 datakit 的 ddtrace 采集器配置一起使用,需要通过customer_tags
方式新增自定义 tag ,否则这部分数据只存在meta
里面。
发起一个请求¶
发起一个 gateway 请求,携带两个 header :dd-username、dd-job,系统会识别dd
开头的 header 参数并在当前所有的链路 span 进行传递。
在观测云上的效果¶
自定义traceId¶
使用 extract + TextMapAdapter 实现了自定义 traceId
自定义 span¶
通常,应用会对业务逻辑进行异常处理,相关的链路可能因为无法标记为 error 从而导致无法正常统计到应用的 error trace,一般为 try-catch
或者全局异常捕获。这时候就需要对链路进行标记处理,可以通过自定义 span 的方式来标记这些 span 为 error span,只需要在 catch 处进行标记即可.
通过以下方式获取当前 span 信息
final Span span = GlobalTracer.get().activeSpan();
标记 span 为 error
span.setTag(Tags.ERROR, true);
如果当前方法是放在catch里面,则可以将 track 信息也可以输出到 span 里面。
可以将 error span 处理逻辑作为一个公共的函数共全局使用,代码如下所示:
private void buildErrorTrace(Exception ex) {
final Span span = GlobalTracer.get().activeSpan();
if (span != null) {
span.setTag(Tags.ERROR, true);
span.log(Collections.singletonMap(Fields.ERROR_OBJECT, ex));
span.setTag(DDTags.ERROR_MSG, ex.getMessage());
span.setTag(DDTags.ERROR_TYPE, ex.getClass().getName());
final StringWriter errorString = new StringWriter();
ex.printStackTrace(new PrintWriter(errorString));
span.setTag(DDTags.ERROR_STACK, errorString.toString());
}
}
调用方代码
@GetMapping("/gateway")
@ResponseBody
public String gateway(String tag) {
......
try {
if (client) {
httpTemplate.getForEntity("http://" + extraHost + ":8081/client", String.class).getBody();
}
} catch (Exception e) {
buildErrorTrace(e);
}
return httpTemplate.getForEntity(apiUrl + "/billing?tag=" + tag, String.class).getBody();
}