ddtrace-api 사용 가이드¶
Warning
현재 예제는 ddtrace의 해당 버전을 사용하여 테스트되었습니다.
전제 조건¶
-
Shell 준비
java -javaagent:dd-java-agent-v1.34.0-guance.jar \
-Ddd.service.name=ddtrace-server \
-Ddd.agent.port=9529 \
-jar springboot-ddtrace-server.jar
설치 및 배포¶
Maven pom 의존성 추가¶
<dependency>
<groupId>com.datadoghq</groupId>
<artifactId>dd-trace-api</artifactId>
<version>1.34.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>
Tracer 획득¶
GlobalTracer를 통해 Tracer 객체를 획득합니다.
Tracer tracer = GlobalTracer.get();
Tracer를 통해 현재 span 정보를 얻을 수 있습니다.
Span span = tracer.activeSpan();
// tracer 객체 획득
Tracer tracer = GlobalTracer.get();
// 현재 span 객체 획득
Span span = tracer.activeSpan();
if (span!=null) {
// traceId 획득
String traceId = span.context().toTraceId();
// spanId 획득
String spanId = span.context().toSpanId();
}
함수 수준 계측¶
dd.trace.methods 방식 외에도 ddtrace는 API를 통해 비즈니스 로직에 더 유연하게 계측할 수 있는 방법을 제공합니다.
- 계측이 필요한 메서드에
@Trace어노테이션 추가
gateway메서드에서 호출
- 재시작 후 gateway에 접근
참고: 침투적 계측이라고 해서 애플리케이션 시작 시 agent가 필요하지 않은 것은 아닙니다. agent가 없으면
@Trace도 무효화됩니다.@Trace어노테이션은 기본 작업 이름이trace.annotation이며, 추적되는 메서드는 기본적으로 리소스를 가집니다.
이름을 변경할 수 있습니다.
@Trace(resourceName = "apiTrace",operationName = "apiTrace")
public String apiTrace(){
return "apiTrace";
}
변경 후 결과는 다음과 같습니다.
Baggage를 사용하여 비즈니스 핵심 태그를 백엔드 링크에서 전달¶
ddtrace는 Baggage 방식을 제공합니다. 정확히 말하면 ddtrace는 OpenTracing에서 제공하는 Baggage 기능을 사용하여 지정된 태그를 링크에서 전달합니다. 예를 들어 사용자 이름, 직무 등의 정보를 전달하여 사용자 행동 분석을 용이하게 합니다.
1 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를 사용하면 태그를 링크 간에 전달할 수 있습니다. 현재 요청 헤더를 가져와 지정된 접두사의 헤더를 Baggage로 설정합니다.
* @author liurui
* @date 2022/7/19 14:59
*/
@Component
public class TraceBaggageFilter implements Filter {
/**
* 지정된 접두사의 헤더를 링크 전달 대상으로 함
*/
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는 링크 간에 전달 가능하지만 일반 태그는 불가능
span.setBaggageItem(header.replace(PREFIX,""),value);
}
}
}
filterChain.doFilter(servletRequest,servletResponse);
}
}
2 DataKit 설정¶
DataKit의 ddtrace 수집기 설정과 함께 사용해야 합니다. customer_tags 방식으로 사용자 정의 태그를 추가해야 하며, 그렇지 않으면 이 데이터는 meta에만 존재합니다.
3 요청 실행¶
gateway 요청을 실행하고, 두 개의 헤더(dd-username 및 dd-job)를 전달합니다. 시스템은 dd로 시작하는 헤더 매개변수를 인식하여 모든 링크 span에 전달합니다.
결과 확인¶
사용자 정의¶
사용자 정의 traceId¶
최적 사례 문서 참조: <extract + TextMapAdapter를 사용하여 사용자 정의 traceId 구현>
사용자 정의 span¶
일반적으로 애플리케이션은 비즈니스 로직에 대한 예외 처리를 수행합니다. 관련 링크가 error로 표시되지 않으면 애플리케이션의 error trace를 정상적으로 통계할 수 없습니다. 일반적으로 try-catch 또는 전역 예외 캐치가 해당됩니다.
이러한 경우 링크를 표시해야 하며, 사용자 정의 span을 통해 이러한 span을 error span으로 표시할 수 있습니다. catch 블록에서 표시하면 됩니다.
- 다음 방법으로 현재 span 정보를 획득합니다.
- span을 error로 표시합니다.
현재 메서드가 catch 블록 내에 있는 경우, 추적 정보를 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();
}
참고 문서¶
<데모 소스 코드>



