Skip to content

ddtrace-api Usage Guide


Warning

The current example is tested with the corresponding version of ddtrace

Prerequisites

java -javaagent:dd-java-agent-v1.34.0-guance.jar \
-Ddd.service.name=ddtrace-server \
-Ddd.agent.port=9529 \
-jar springboot-ddtrace-server.jar

Installation and Deployment

Add Maven POM Dependencies

    <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>

Obtain the Tracer

Obtain the Tracer object via GlobalTracer

Tracer tracer = GlobalTracer.get();

Through the Tracer, you can retrieve the current span information

Span span = tracer.activeSpan();

    // Obtain the tracer object
    Tracer tracer = GlobalTracer.get();
    // Obtain the current span object
    Span span = tracer.activeSpan();
    if (span!=null) {
        // Get traceId
        String traceId = span.context().toTraceId();
        // Get spanId
        String spanId = span.context().toSpanId();
    }

Method-Level Instrumentation

In addition to the dd.trace.methods approach for automatic method instrumentation, ddtrace provides an API for more flexible business instrumentation.

  1. Add the @Trace annotation to the method that needs instrumentation
    @Trace
    public String apiTrace(){
        return "apiTrace";
    }
  1. Then call it in the gateway method
...
testService.apiTrace();
...
  1. Restart, then access the gateway

image.png

Note: Invasive instrumentation does not mean the application can start without the agent. Without the agent, the @Trace annotation will be ineffective. The @Trace annotation has a default operation name trace.annotation, and the traced method has a default resource.

You can customize the names:

    @Trace(resourceName = "apiTrace",operationName = "apiTrace")
    public String apiTrace(){
        return "apiTrace";
    }

After modification, the effect is as follows:

image.png

Using Baggage to Propagate Business-Critical Tags Across the Backend Trace

ddtrace provides the Baggage mechanism—more precisely, ddtrace uses OpenTracing's Baggage feature to propagate specific tags across the trace. For example, user name, job title, etc., to facilitate user behavior analysis.

span.setBaggageItem("username","liurui");

1 Write a TraceBaggageFilter

Use the TraceBaggageFilter to intercept requests and propagate relevant request header parameters via 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 allows tags to be propagated between traces. By obtaining the current request headers,
 * headers with a specified prefix are set as Baggage.
 * @author liurui
 * @date 2022/7/19 14:59
 */
@Component
public class TraceBaggageFilter implements Filter {

    /**
     * Headers with this prefix will be propagated across the trace
     */
    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 can be propagated between traces, while ordinary tags cannot
                    span.setBaggageItem(header.replace(PREFIX,""),value);
                }
            }

        }
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

2 DataKit Configuration

This needs to be used together with the DataKit ddtrace collector configuration. Add custom tags via the customer_tags field; otherwise, this data will only exist in meta.

customer_tags = ["username", "job"]

3 Send a Request

Send a request to the gateway with two headers: dd-username and dd-job. The system will recognize headers starting with dd and propagate them across all spans in the current trace.

Result

Customization

Custom traceId

Refer to the best practice document: <Using extract + TextMapAdapter to implement custom traceId>

Custom Span

Often, applications handle exceptions in business logic, and the related trace may not be marked as error, causing the application's error traces not to be counted correctly. This commonly occurs with try-catch blocks or global exception handlers.

In such cases, you need to mark the trace accordingly. You can mark the current span as an error span by customizing the span—just mark it in the catch block.

  1. Obtain the current span information as follows:
final Span span = GlobalTracer.get().activeSpan();
  1. Mark the span as error:
span.setTag(Tags.ERROR, true);

If the current method is inside a catch block, you can also output the stack trace information to the span.

You can encapsulate the error span handling logic into a common function for global use, as shown below:

    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());
        }

    }

Caller code:

@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();
    }

Reference Documents

<Demo Source Code>

ddtrace Startup Options

Feedback

Is this page helpful?