-
Notifications
You must be signed in to change notification settings - Fork 1k
feat: Add OpenTelemetry instrumentation for ActiveJ HTTP server #13335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
57 commits
Select commit
Hold shift + click to select a range
b52c49d
Introduces OpenTelemetry instrumentation for the ActiveJ framework, e…
kcsurapaneni 1cc6c5f
Resolved PR comments
kcsurapaneni b4e8e29
instrumentation name is updated with version
kcsurapaneni ce368c1
instrumentation name is updated with version
kcsurapaneni b287f78
instrumentation name is updated with version
kcsurapaneni e1a29dc
checkstyle config is reverted and added @SuppressWarnings(Abbreviatio…
kcsurapaneni 583e063
renames class files to avoid AbbreviationAsWordInName checkstyle rule
kcsurapaneni b7aff9d
Test cases are added
kcsurapaneni b241b45
build script dependencies is updated
kcsurapaneni 1662b23
throwable is now read-only parameter
kcsurapaneni 4fd797f
base version is set to Java 17
kcsurapaneni 7bf1776
PR comments addressed
kcsurapaneni a07317e
PR comments addressed
kcsurapaneni d5bb920
spotlessCheck applied
kcsurapaneni 429ad65
PR comments addressed
kcsurapaneni ac95594
explicitly added test dependencies
kcsurapaneni 5a4d2dd
Resolving common/check-latest-dep-test-overrides issue
kcsurapaneni c142d7f
fix double instrumentation (#13337)
zeitlinger c098084
Add comment to workflow file (#13343)
trask c0b67a3
Better qualify Java HttpClient instrumentation package name (#13296)
trask 4c149bd
Update apidiff baseline to released version 2.13.1 (#13348)
otelbot[bot] fea36de
Merge change log updates from release/v2.13.x (#13347)
otelbot[bot] 2c31d8f
fix(deps): update gradle develocity packages to v3.19.2 (patch) (#13349)
renovate[bot] 583801e
Fix automated PR body text (#13350)
trask f6ef942
fix(deps): update dependency io.opentelemetry.semconv:opentelemetry-s…
renovate[bot] bb75dab
fix(deps): update testcontainers-java monorepo to v1.20.5 (patch) (#1…
renovate[bot] aba23a3
fix(deps): update dependency com.google.apis:google-api-services-shee…
renovate[bot] 5a92938
Fix flaky test (#13358)
laurit 1420a18
Fix testLatestDeps (#13364)
trask 20d00a0
Fix TODO (#13363)
trask f1cfe0e
integration test case has been added which extends AbstractHttpServer…
kcsurapaneni 0748348
fix(deps): update dependency org.springframework.boot:spring-boot-sta…
renovate[bot] 6ec9373
fix(deps): update dependency org.testcontainers:testcontainers to v1.…
renovate[bot] 37032d2
Merge branch 'main' into activej
laurit 65e6d57
Test cases are updated to use JUnit 5
kcsurapaneni ad7692a
fix(deps): update dependency org.awaitility:awaitility to v4.3.0 (#13…
renovate[bot] 6f01a1c
Promise response method exit handling logic updated
kcsurapaneni dc94afa
addressed PR comments
kcsurapaneni 2332b12
fix(deps): update opentelemetry-java-contrib monorepo to v1.44.0-alph…
renovate[bot] fe1151c
[Spring Scheduling] Support Virtual Threads (#13370)
jakobjoachim 18b3cde
chore(deps): update weekly update (#13381)
renovate[bot] dfb73d1
fix(deps): update dependency checkstyle to v10.21.3 (#13380)
renovate[bot] f167d99
fix(deps): update junit5 monorepo to v5.12.0 (minor) (#13372)
renovate[bot] bf3fe1f
fix(deps): update dependency com.google.auth:google-auth-library-oaut…
renovate[bot] e802708
fix semconv naming for 'jvm.buffer.memory.used' metric (#13374)
SylvainJuge f6ee0dc
fix(deps): update dependency com.google.auth:google-auth-library-oaut…
renovate[bot] 036ae0b
chore(deps): update dependency gradle to v8.13 (#13394)
renovate[bot] 148a91a
Ensure tilde$1 onExit is run in correct order (#13360)
masonedmison 2f40b7f
Add instrumentation of AWS Bedrock to use gen_ai conventions (#13355)
anuraaga 483ec98
fix(deps): update dependency ch.qos.logback:logback-classic to v1.5.1…
renovate[bot] ef5ceec
Merge remote-tracking branch 'origin' into activej
kcsurapaneni 418a259
@laurit PR comments are addressed
kcsurapaneni 81827bc
@laurit PR comments are addressed
kcsurapaneni 87345f0
polish
laurit 9023ad8
reformat table
laurit 99a4c21
only consider 6.0+ classes
kcsurapaneni 7269a0e
supports minimum 6.0 version
kcsurapaneni File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # OpenTelemetry Java Agent for ActiveJ Framework | ||
|
|
||
| This repository provides an OpenTelemetry Java agent specifically designed to instrument applications built on the [ActiveJ framework](https://activej.io/). The agent enables distributed tracing, context propagation, and telemetry data collection for ActiveJ-based HTTP servers, making it easier to monitor and debug applications in distributed systems. | ||
|
|
||
| ## Table of Contents | ||
|
|
||
| 1. [Overview](#overview) | ||
| 2. [Features](#features) | ||
| 3. [Prerequisites](#prerequisites) | ||
| 4. [Installation & Usage](#installation) | ||
|
|
||
| --- | ||
|
|
||
| ## Overview | ||
|
|
||
| The OpenTelemetry Java agent for ActiveJ integrates with the OpenTelemetry API to provide automatic instrumentation for ActiveJ HTTP servers. It captures trace context from incoming HTTP requests, propagates it through responses, and enriches telemetry data with HTTP-specific attributes such as request methods, headers, status codes, and URL components. | ||
|
|
||
| This agent is particularly useful for applications that rely on ActiveJ's high-performance, event-driven architecture and need observability in distributed systems. | ||
|
|
||
| --- | ||
|
|
||
| ## Features | ||
|
|
||
| - **Distributed Tracing**: Automatically propagates trace context across service boundaries using the `traceparent` header. | ||
| - **HTTP Attribute Extraction**: Captures detailed HTTP attributes (e.g., method, path, query, headers) for enriched telemetry data. | ||
| - **Error Handling**: Handles exceptions and maps them to appropriate HTTP status codes for better error visibility. | ||
| - **Compatibility**: Works seamlessly with OpenTelemetry's Java instrumentation framework and exporters (e.g., Jaeger, Zipkin, HyperDX). | ||
|
|
||
| --- | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| Before using this agent, ensure you have the following: | ||
|
|
||
| - Java 8 or higher | ||
| - ActiveJ framework | ||
| - An OpenTelemetry collector or backend (e.g., Jaeger, Zipkin, HyperDX) for visualizing traces | ||
|
|
||
| --- | ||
|
|
||
| ## Installation | ||
|
|
||
| ### Using the Java Agent JAR | ||
|
|
||
| 1. Download the latest release of the OpenTelemetry Java agent JAR file. | ||
| 2. Add the agent to your application's JVM arguments: | ||
|
|
||
| ```bash | ||
| java -javaagent:/path/to/opentelemetry-java-agent.jar -jar your-application.jar | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| plugins { | ||
| id("otel.javaagent-instrumentation") | ||
| } | ||
|
|
||
| muzzle { | ||
| pass { | ||
| group.set("io.activej:activej-http") | ||
| module.set("activej-http") | ||
|
|
||
| versions.set("[1.0,)") | ||
| } | ||
| } | ||
|
|
||
| dependencies { | ||
| library("io.activej:activej-http:6.0-beta2") | ||
| } |
164 changes: 164 additions & 0 deletions
164
...try/javaagent/instrumentation/activejhttp/ActiveJHttpServerConnectionInstrumentation.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.javaagent.instrumentation.activejhttp; | ||
|
|
||
| import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; | ||
| import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; | ||
| import static io.opentelemetry.javaagent.instrumentation.activejhttp.ActiveJHttpServerConnectionSingletons.instrumenter; | ||
| import static net.bytebuddy.matcher.ElementMatchers.isInterface; | ||
| import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
| import static net.bytebuddy.matcher.ElementMatchers.named; | ||
| import static net.bytebuddy.matcher.ElementMatchers.not; | ||
| import static net.bytebuddy.matcher.ElementMatchers.takesArgument; | ||
| import static net.bytebuddy.matcher.ElementMatchers.takesArguments; | ||
|
|
||
| import io.activej.http.AsyncServlet; | ||
| import io.activej.http.HttpHeaders; | ||
| import io.activej.http.HttpRequest; | ||
| import io.activej.http.HttpResponse; | ||
| import io.activej.promise.Promise; | ||
| import io.opentelemetry.api.trace.Span; | ||
| import io.opentelemetry.context.Context; | ||
| import io.opentelemetry.context.Scope; | ||
| import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
| import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; | ||
| import net.bytebuddy.asm.Advice; | ||
| import net.bytebuddy.description.type.TypeDescription; | ||
| import net.bytebuddy.matcher.ElementMatcher; | ||
|
|
||
| /** | ||
| * This class provides instrumentation for ActiveJ HTTP server connections by applying advice to the | ||
| * {@code serve} method of classes that extend {@code io.activej.http.AsyncServlet}. The | ||
| * instrumentation is designed to integrate with OpenTelemetry for distributed tracing, capturing | ||
| * and propagating trace context through HTTP requests and responses. | ||
| * | ||
| * @author Krishna Chaitanya Surapaneni | ||
kcsurapaneni marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| */ | ||
| public class ActiveJHttpServerConnectionInstrumentation implements TypeInstrumentation { | ||
|
|
||
| /** | ||
| * Matches classes that extend {@code io.activej.http.AsyncServlet} but are not interfaces. | ||
| * | ||
| * @return An {@code ElementMatcher} that identifies target classes for instrumentation. | ||
| */ | ||
| @Override | ||
| public ElementMatcher<TypeDescription> typeMatcher() { | ||
| return hasSuperType(named("io.activej.http.AsyncServlet")).and(not(isInterface())); | ||
| } | ||
|
|
||
| /** | ||
| * Applies advice to the {@code serve} method of the matched classes. The advice captures trace | ||
| * context at the start of the method and propagates it through the response. | ||
kcsurapaneni marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| * | ||
| * @param transformer The {@code TypeTransformer} used to apply the advice. | ||
| */ | ||
| @Override | ||
| public void transform(TypeTransformer transformer) { | ||
| transformer.applyAdviceToMethod( | ||
| isMethod() | ||
| .and(named("serve")) | ||
| .and(takesArguments(1).and(takesArgument(0, named("io.activej.http.HttpRequest")))), | ||
| this.getClass().getName() + "$ServeAdvice"); | ||
| } | ||
|
|
||
| /** | ||
| * Inner class containing the advice logic for the {@code serve} method. This class defines two | ||
| * methods: | ||
| * | ||
| * <ul> | ||
| * <li>{@code methodEnter}: Captures the trace context at the start of the method. | ||
| * <li>{@code methodExit}: Propagates the trace context to the response and ends the span. | ||
| * </ul> | ||
| */ | ||
| @SuppressWarnings("unused") | ||
| public static class ServeAdvice { | ||
|
|
||
| /** | ||
| * Advice executed at the start of the {@code serve} method. Captures the current trace context | ||
| * and starts a new span if tracing is enabled for the request. | ||
| * | ||
| * @param asyncServlet The {@code AsyncServlet} instance handling the request. | ||
| * @param request The incoming HTTP request. | ||
| * @param context Local variable to store the OpenTelemetry context. | ||
| * @param scope Local variable to store the OpenTelemetry scope. | ||
| * @param httpRequest Local variable to store the HTTP request. | ||
| */ | ||
| @Advice.OnMethodEnter(suppress = Throwable.class) | ||
| public static void methodEnter( | ||
| @Advice.This AsyncServlet asyncServlet, | ||
| @Advice.Argument(0) HttpRequest request, | ||
| @Advice.Local("otelContext") Context context, | ||
| @Advice.Local("otelScope") Scope scope, | ||
| @Advice.Local("httpRequest") HttpRequest httpRequest) { | ||
| Context parentContext = currentContext(); | ||
| httpRequest = request; | ||
| if (!instrumenter().shouldStart(parentContext, request)) { | ||
| return; | ||
| } | ||
| context = instrumenter().start(parentContext, request); | ||
| scope = context.makeCurrent(); | ||
| } | ||
|
|
||
| /** | ||
| * Advice executed at the end of the {@code serve} method. Propagates the trace context to the | ||
| * response, handles exceptions, and ends the span. | ||
| * | ||
| * @param asyncServlet The {@code AsyncServlet} instance handling the request. | ||
| * @param responsePromise The promise representing the HTTP response. | ||
| * @param throwable Any exception thrown during the execution of the method. | ||
| * @param context Local variable storing the OpenTelemetry context. | ||
| * @param scope Local variable storing the OpenTelemetry scope. | ||
| * @param httpRequest Local variable storing the HTTP request. | ||
| */ | ||
| @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
| public static void methodExit( | ||
| @Advice.This AsyncServlet asyncServlet, | ||
| @Advice.Return(readOnly = false) Promise<HttpResponse> responsePromise, | ||
| @Advice.Thrown(readOnly = false) Throwable throwable, | ||
| @Advice.Local("otelContext") Context context, | ||
| @Advice.Local("otelScope") Scope scope, | ||
| @Advice.Local("httpRequest") HttpRequest httpRequest) { | ||
| if (context == null || scope == null || httpRequest == null) { | ||
| return; | ||
| } | ||
| String traceId = Span.fromContext(context).getSpanContext().getTraceId(); | ||
| String spanId = Span.fromContext(context).getSpanContext().getSpanId(); | ||
| String traceFlags = | ||
| Span.fromContext(context).getSpanContext().getTraceFlags().asHex().substring(0, 2); | ||
| String traceparent = String.format("00-%s-%s-%s", traceId, spanId, traceFlags); | ||
|
|
||
| scope.close(); | ||
| String traceparentHeader = "traceparent"; | ||
| if (responsePromise != null) { | ||
| HttpResponse httpResponse = responsePromise.getResult(); | ||
| Throwable error = throwable; | ||
| if (responsePromise.isException()) { | ||
| error = responsePromise.getException(); | ||
| } | ||
| httpResponse = ActiveJHttpServerHelper.createResponse(error, traceparent, httpResponse); | ||
| instrumenter().end(context, httpRequest, httpResponse, error); | ||
| responsePromise = Promise.of(httpResponse); | ||
| } else if (throwable != null) { | ||
| HttpResponse httpResponse = | ||
| HttpResponse.builder() | ||
| .withCode(500) | ||
| .withPlainText(throwable.getMessage()) | ||
| .withHeader(HttpHeaders.of(traceparentHeader), traceparent) | ||
| .build(); | ||
| instrumenter().end(context, httpRequest, httpResponse, throwable); | ||
| responsePromise = Promise.of(httpResponse); | ||
| throwable = null; | ||
| } else { | ||
| HttpResponse httpResponse = | ||
| HttpResponse.notFound404() | ||
| .withHeader(HttpHeaders.of(traceparentHeader), traceparent) | ||
| .build(); | ||
| instrumenter().end(context, httpRequest, httpResponse, throwable); | ||
| responsePromise = Promise.of(httpResponse); | ||
| } | ||
| } | ||
| } | ||
| } | ||
66 changes: 66 additions & 0 deletions
66
...vaagent/instrumentation/activejhttp/ActiveJHttpServerConnectionInstrumentationModule.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.javaagent.instrumentation.activejhttp; | ||
|
|
||
| import static java.util.Collections.singletonList; | ||
|
|
||
| import com.google.auto.service.AutoService; | ||
| import io.opentelemetry.javaagent.extension.instrumentation.HelperResourceBuilder; | ||
| import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; | ||
| import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
| import java.util.List; | ||
|
|
||
| /** | ||
| * This class is an instrumentation module for ActiveJ HTTP server connections. It integrates with | ||
| * OpenTelemetry to provide distributed tracing capabilities for applications using the ActiveJ HTTP | ||
| * server. | ||
| * | ||
| * <p>The module is annotated with {@code @AutoService(InstrumentationModule.class)}, which | ||
| * automatically registers it as a service provider for the OpenTelemetry instrumentation framework. | ||
| * This allows the module to be discovered and loaded dynamically during runtime. | ||
| * | ||
| * @author Krishna Chaitanya Surapaneni | ||
| */ | ||
| @AutoService(InstrumentationModule.class) | ||
| public class ActiveJHttpServerConnectionInstrumentationModule extends InstrumentationModule { | ||
|
|
||
| /** | ||
| * Constructs the instrumentation module with the specified instrumentation names. These names are | ||
| * used to identify the instrumentation in the OpenTelemetry framework. | ||
| * | ||
| * <p>In this case, the module is identified by the names "activej-http" and | ||
| * "activej-http-server". | ||
| */ | ||
| public ActiveJHttpServerConnectionInstrumentationModule() { | ||
| super("activej-http", "activej-http-server"); | ||
| } | ||
|
|
||
| /** | ||
| * Returns a list of type instrumentation's provided by this module. Each type instrumentation | ||
| * applies advice to specific methods or classes to capture trace context and propagate it through | ||
| * HTTP requests and responses. | ||
| * | ||
| * @return A list containing the {@code ActiveJHttpServerConnectionInstrumentation} instance. | ||
| */ | ||
| @Override | ||
| public List<TypeInstrumentation> typeInstrumentations() { | ||
| return singletonList(new ActiveJHttpServerConnectionInstrumentation()); | ||
| } | ||
|
|
||
| /** | ||
| * Registers helper resources required for the instrumentation. Helper resources are typically | ||
| * utility classes or configurations that support the instrumentation logic. | ||
| * | ||
| * <p>In this case, the {@code ActiveJHttpServerHelper} class is registered as a helper resource. | ||
| * This class provides utilities for creating HTTP responses with trace context. | ||
| * | ||
| * @param helperResourceBuilder The builder used to register helper resources. | ||
| */ | ||
| @Override | ||
| public void registerHelperResources(HelperResourceBuilder helperResourceBuilder) { | ||
| helperResourceBuilder.register(ActiveJHttpServerHelper.class.getName()); | ||
| } | ||
| } |
42 changes: 42 additions & 0 deletions
42
...elemetry/javaagent/instrumentation/activejhttp/ActiveJHttpServerConnectionSingletons.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.javaagent.instrumentation.activejhttp; | ||
|
|
||
| import io.activej.http.HttpRequest; | ||
| import io.activej.http.HttpResponse; | ||
| import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; | ||
| import io.opentelemetry.javaagent.bootstrap.internal.JavaagentHttpServerInstrumenters; | ||
|
|
||
| /** | ||
| * This class provides singleton instances and utilities for ActiveJ HTTP server instrumentation. It | ||
| * is designed to centralize the creation and access of OpenTelemetry-related components, such as | ||
| * the {@code Instrumenter} used for tracing HTTP requests and responses. | ||
| * | ||
| * @author Krishna Chaitanya Surapaneni | ||
| */ | ||
| public class ActiveJHttpServerConnectionSingletons { | ||
|
|
||
| /** | ||
| * The name of the instrumentation, used to identify this module in the OpenTelemetry framework. | ||
| */ | ||
| private static final String INSTRUMENTATION_NAME = "io.opentelemetry.activej-http"; | ||
|
|
||
| private static final Instrumenter<HttpRequest, HttpResponse> INSTRUMENTER; | ||
|
|
||
| static { | ||
| INSTRUMENTER = | ||
| JavaagentHttpServerInstrumenters.create( | ||
| INSTRUMENTATION_NAME, | ||
| new ActiveJHttpServerHttpAttributesGetter(), | ||
| ActiveJHttpServerHeaders.INSTANCE); | ||
| } | ||
|
|
||
| public static Instrumenter<HttpRequest, HttpResponse> instrumenter() { | ||
| return INSTRUMENTER; | ||
| } | ||
|
|
||
| private ActiveJHttpServerConnectionSingletons() {} | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.