Skip to content
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

feat: Add OpenTelemetry instrumentation for ActiveJ HTTP server #13335

Merged
merged 57 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
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 Feb 18, 2025
1cc6c5f
Resolved PR comments
kcsurapaneni Feb 18, 2025
b4e8e29
instrumentation name is updated with version
kcsurapaneni Feb 18, 2025
ce368c1
instrumentation name is updated with version
kcsurapaneni Feb 18, 2025
b287f78
instrumentation name is updated with version
kcsurapaneni Feb 18, 2025
e1a29dc
checkstyle config is reverted and added @SuppressWarnings(Abbreviatio…
kcsurapaneni Feb 18, 2025
583e063
renames class files to avoid AbbreviationAsWordInName checkstyle rule
kcsurapaneni Feb 18, 2025
b7aff9d
Test cases are added
kcsurapaneni Feb 19, 2025
b241b45
build script dependencies is updated
kcsurapaneni Feb 19, 2025
1662b23
throwable is now read-only parameter
kcsurapaneni Feb 19, 2025
4fd797f
base version is set to Java 17
kcsurapaneni Feb 19, 2025
7bf1776
PR comments addressed
kcsurapaneni Feb 20, 2025
a07317e
PR comments addressed
kcsurapaneni Feb 20, 2025
d5bb920
spotlessCheck applied
kcsurapaneni Feb 20, 2025
429ad65
PR comments addressed
kcsurapaneni Feb 20, 2025
ac95594
explicitly added test dependencies
kcsurapaneni Feb 20, 2025
5a4d2dd
Resolving common/check-latest-dep-test-overrides issue
kcsurapaneni Feb 20, 2025
c142d7f
fix double instrumentation (#13337)
zeitlinger Feb 18, 2025
c098084
Add comment to workflow file (#13343)
trask Feb 18, 2025
c0b67a3
Better qualify Java HttpClient instrumentation package name (#13296)
trask Feb 18, 2025
4c149bd
Update apidiff baseline to released version 2.13.1 (#13348)
otelbot[bot] Feb 18, 2025
fea36de
Merge change log updates from release/v2.13.x (#13347)
otelbot[bot] Feb 18, 2025
2c31d8f
fix(deps): update gradle develocity packages to v3.19.2 (patch) (#13349)
renovate[bot] Feb 18, 2025
583801e
Fix automated PR body text (#13350)
trask Feb 18, 2025
f6ef942
fix(deps): update dependency io.opentelemetry.semconv:opentelemetry-s…
renovate[bot] Feb 19, 2025
bb75dab
fix(deps): update testcontainers-java monorepo to v1.20.5 (patch) (#1…
renovate[bot] Feb 19, 2025
aba23a3
fix(deps): update dependency com.google.apis:google-api-services-shee…
renovate[bot] Feb 20, 2025
5a92938
Fix flaky test (#13358)
laurit Feb 20, 2025
1420a18
Fix testLatestDeps (#13364)
trask Feb 20, 2025
20d00a0
Fix TODO (#13363)
trask Feb 20, 2025
f1cfe0e
integration test case has been added which extends AbstractHttpServer…
kcsurapaneni Feb 21, 2025
0748348
fix(deps): update dependency org.springframework.boot:spring-boot-sta…
renovate[bot] Feb 21, 2025
6ec9373
fix(deps): update dependency org.testcontainers:testcontainers to v1.…
renovate[bot] Feb 21, 2025
37032d2
Merge branch 'main' into activej
laurit Feb 21, 2025
65e6d57
Test cases are updated to use JUnit 5
kcsurapaneni Feb 21, 2025
ad7692a
fix(deps): update dependency org.awaitility:awaitility to v4.3.0 (#13…
renovate[bot] Feb 21, 2025
6f01a1c
Promise response method exit handling logic updated
kcsurapaneni Feb 21, 2025
dc94afa
addressed PR comments
kcsurapaneni Feb 26, 2025
2332b12
fix(deps): update opentelemetry-java-contrib monorepo to v1.44.0-alph…
renovate[bot] Feb 22, 2025
fe1151c
[Spring Scheduling] Support Virtual Threads (#13370)
jakobjoachim Feb 22, 2025
18b3cde
chore(deps): update weekly update (#13381)
renovate[bot] Feb 24, 2025
dfb73d1
fix(deps): update dependency checkstyle to v10.21.3 (#13380)
renovate[bot] Feb 24, 2025
f167d99
fix(deps): update junit5 monorepo to v5.12.0 (minor) (#13372)
renovate[bot] Feb 24, 2025
bf3fe1f
fix(deps): update dependency com.google.auth:google-auth-library-oaut…
renovate[bot] Feb 25, 2025
e802708
fix semconv naming for 'jvm.buffer.memory.used' metric (#13374)
SylvainJuge Feb 25, 2025
f6ee0dc
fix(deps): update dependency com.google.auth:google-auth-library-oaut…
renovate[bot] Feb 25, 2025
036ae0b
chore(deps): update dependency gradle to v8.13 (#13394)
renovate[bot] Feb 25, 2025
148a91a
Ensure tilde$1 onExit is run in correct order (#13360)
masonedmison Feb 26, 2025
2f40b7f
Add instrumentation of AWS Bedrock to use gen_ai conventions (#13355)
anuraaga Feb 26, 2025
483ec98
fix(deps): update dependency ch.qos.logback:logback-classic to v1.5.1…
renovate[bot] Feb 26, 2025
ef5ceec
Merge remote-tracking branch 'origin' into activej
kcsurapaneni Feb 26, 2025
418a259
@laurit PR comments are addressed
kcsurapaneni Feb 26, 2025
81827bc
@laurit PR comments are addressed
kcsurapaneni Feb 26, 2025
87345f0
polish
laurit Feb 26, 2025
9023ad8
reformat table
laurit Feb 26, 2025
99a4c21
only consider 6.0+ classes
kcsurapaneni Feb 26, 2025
7269a0e
supports minimum 6.0 version
kcsurapaneni Feb 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .fossa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ targets:
- type: gradle
path: ./
target: ':testing:agent-for-testing'
- type: gradle
path: ./
target: ':instrumentation:activej-http:javaagent'
- type: gradle
path: ./
target: ':instrumentation:alibaba-druid-1.0:javaagent'
Expand Down
2 changes: 1 addition & 1 deletion buildscripts/checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@
-->
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="0"/>
<property name="allowedAbbreviationLength" value="2"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF,
Expand Down
50 changes: 50 additions & 0 deletions instrumentation/activej-http/javaagent/README.md
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
```
16 changes: 16 additions & 0 deletions instrumentation/activej-http/javaagent/build.gradle.kts
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")
}
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
*/
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.
*
* @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);
}
}
}
}
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());
}
}
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() {}
}
Loading
Loading