2

Docs | Coroot

 1 year ago
source link: https://coroot.com/docs/coroot-community-edition/tracing/opentelemetry-java
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

OpenTelemetry for Java

Instrumenting a Java application with OpenTelemetry can provide valuable insights into the application's performance and behavior. OpenTelemetry is an open-source observability framework that enables the collection and exporting of telemetry data. This document covers the steps required to instrument a Java application with OpenTelemetry.

Automatic instrumentation

Let's start with a simple Spring Boot web application.

@SpringBootApplication
@RestController
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

	@GetMapping("/hello/{name}")
	public String hello(@PathVariable(value = "name") String name) {
		return String.format("Hello, %s!", name);
	}
}

OpenTelemetry instrumentation generates detailed spans that describe the handling of inbound HTTP requests. These spans will provide insight into the entire lifecycle of each request, from the moment it arrives at the server to the moment it is sent back to the client.

OpenTelemetry provides a Java agent that automatically detects and instruments the most popular application servers, clients, and frameworks. This means we don't even need to change the code of our app to instrument it.

# download the latest version of the agent
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar

Then, run the application with the instrumentation:

export \
 OTEL_SERVICE_NAME="spring-demo" \
 OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://coroot-opentelemetry-collector.coroot:4318/v1/traces" \
 OTEL_EXPORTER_OTLP_TRACES_PROTOCOL="http/protobuf" \
 OTEL_METRICS_EXPORTER="none" \
 && java -javaagent:./opentelemetry-javaagent.jar -jar build/libs/demo-0.0.1-SNAPSHOT.jar

As a result, our app reports traces to the configured OpenTelemetry collector:

Each server span contains all the details related to a given request:

Exceptions

Now, let's explore what happens when our app throws an exception.

@SpringBootApplication
@RestController
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

	@GetMapping("/hello/{name}")
	public String hello(@PathVariable(value = "name") String name) {
		throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failure injection: HTTP-500");
	}
}

The resulting trace indicates two spans with errors.

Typically, the error is captured within the details of the deepest nested failing span. In this particular case, this is the DemoApplication.hello span.

As you can see, we have easily identified the reason why this particular request resulted in an error.

Database calls

Now, let's add a database call to our app:

@SpringBootApplication
@RestController
public class DemoApplication {
	@Autowired
	private UserRepository userRepository;

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

	@GetMapping("/hello/{id}")
	public String hello(@PathVariable(value = "id") Integer id) {
		Optional<User> user = userRepository.findById(id);
		if (user.isEmpty()) {
			throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found");
		}

		return String.format("Hello, %s!", user.get().getName());
	}
}

Once again, there is no need to add any additional instrumentation as the OTel Java agent automatically captures every database call.

Span attributes:

HTTP calls and context propagation

Next, instead of retrieving a user from the database, let's make an HTTP call to a service and retrieve a JSON response containing the desired information.

@SpringBootApplication
@RestController
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

	@GetMapping("/hello/{id}")
	public String hello(@PathVariable(value = "id") Integer id) {
		RestTemplate tpl = new RestTemplate();
        tpl.getMessageConverters().add(new MappingJackson2HttpMessageConverter());

        User user = tpl.getForObject(String.format("http://127.0.0.1:8082/user/%d", id), User.class);

		return String.format("Hello %s!", user.getName());
	}
}

Client span attributes:

As you can see, the resulting trace includes a span reported by the user service. Both services are instrumented with OpenTelemetry. But how is the context of the current trace propagated between them? To gain a better understanding of context propagation, let's examine the request sent to the user service:

GET /user/1 HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
User-agent: Java/17.0.1
Accept: application/json
Traceparent: 00-d12946898a11d917a2fb6bd1ab054e0e-d55429431be788ff-01

OpenTelemetry adds the Traceparent HTTP header on the client side, and dependency services read this header to propagate the trace context. It has the following format:

Version-TraceID-ParentSpanID-TraceFlags

In our case, d12946898a11d917a2fb6bd1ab054e0e is the TraceID, and d55429431be788ff is the ParentSpanId.

Adding custom attributes and events to spans

To customize OpenTelemetry spans, you will need to add the OpenTelemetry dependency to your project:

Gradle
Maven
dependencies {
    implementation 'io.opentelemetry:opentelemetry-sdk:1.26.0'
}

Then, you can retrieve the current span and set custom attributes or add events:

@SpringBootApplication
@RestController
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

	@GetMapping("/hello/{id}")
	public String hello(@PathVariable(value = "id") Integer id) {
        Span span = Span.current();

		// set an attribute
		span.setAttribute("user.id", id);

		//add an event
		span.addEvent("the user profile has been loaded from the database");

        ...
		return String.format("Hello %s!", user.getName());
	}
}

The resulting span:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK