Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
740cccd
build: Bump jafar to 0.26.2, add jafar-parser and jmc flightrecorder-…
jbachorik Sep 29, 2026
9fc83b5
feat(profiling): Reference-count RecordingData lifecycle
jbachorik Sep 29, 2026
ea36aeb
feat(profiling): Add JFR to OTLP/P converter with CLI, benchmarks and…
jbachorik Sep 29, 2026
3e9e61f
refactor: Extract OTLP sender infrastructure to communication/otlp-ex…
jbachorik Sep 29, 2026
46d0278
feat(profiling): Add OTLP profiles configuration
jbachorik Sep 29, 2026
d011ba1
feat(profiling): Report OTLP profile conversion and export telemetry
jbachorik Sep 29, 2026
0e36400
feat(profiling): Integrate OtlpProfileUploader with reference-counted…
jbachorik Sep 29, 2026
02b7b95
build: Include otlp-exporter in the agent shadow jar, raise size budg…
jbachorik Sep 29, 2026
39caf72
test: Bump TempLocationManagerTest concurrent cleanup deadlines to 60s
jbachorik Sep 29, 2026
c2332f4
chore: Add CODEOWNERS for otlp-exporter module and profcheck Dockerfile
jbachorik Sep 29, 2026
207f9f5
Merge branch 'master' into jb/rnd_otlp_profile
jbachorik Sep 29, 2026
14879a7
Spotless!!!
jbachorik Sep 29, 2026
1d7ca6e
Address OTLP profiles review comments
jbachorik Oct 6, 2026
55a10c5
Share canonical OTLP resource attributes
jbachorik Oct 6, 2026
eef09c2
Fix OTLP Sample field numbers and pin validation inputs
jbachorik Oct 6, 2026
757d6fd
Fix OTLP profiles review findings
jbachorik Oct 6, 2026
d8f6cad
Expose internal-api from otlp-exporter, tighten test
jbachorik Oct 6, 2026
4183e2e
Use epoch ticks in OTLP benchmarks, update profcheck doc
jbachorik Oct 6, 2026
f20b8f0
Warn that OTLP profiles export is not production-ready
jbachorik Oct 7, 2026
5372348
Merge jb/otlp-resource-attrs
jbachorik Oct 7, 2026
a5d5ae8
Merge jb/otlp-proto-pin
jbachorik Oct 7, 2026
69de26b
Convert jdk.CPUTimeSample to OTLP CPU samples
jbachorik Oct 7, 2026
7274b35
Fix OTLP profiles protocol and timeout defaults
jbachorik Oct 7, 2026
e7b11bd
Use registry version B for DD_PROFILING_OTLP_MODE
jbachorik Oct 7, 2026
0ad43fb
Use registry version B for OTLP profiles compression
jbachorik Oct 7, 2026
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
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

# Shared communication
/communication/*.* @DataDog/apm-java
/communication/otlp-exporter/ @DataDog/apm-sdk-capabilities-java
/communication/src/*/*/datadog/communication/*.* @DataDog/apm-java
/communication/src/*/*/datadog/communication/ddagent/ @DataDog/apm-java
/communication/src/*/*/datadog/communication/http/ @DataDog/apm-java
Expand Down Expand Up @@ -341,6 +342,7 @@

# @DataDog/profiling-java
/dd-java-agent/ddprof-lib/ @DataDog/profiling-java
/docker/Dockerfile.profcheck @DataDog/profiling-java
/dd-java-agent/agent-profiling/ @DataDog/profiling-java
/dd-java-agent/agent-crashtracking/ @DataDog/profiling-java
/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jfr/ @DataDog/profiling-java
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,4 @@ mise*.local.toml

# Exclude kotlin build files
.kotlin
.claude/state/
27 changes: 27 additions & 0 deletions communication/otlp-exporter/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
plugins {
`java-library`
id("dd-trace-java.module.internal-library")
}

description = "otlp-exporter"

// Network/IO classes are hard to unit-test without a real OTLP endpoint; they are exercised
// via integration tests and the profiling-otel smoke tests instead.
extra["excludedClassesCoverage"] = listOf(
"datadog.communication.otlp.OtlpGrpcSender",
"datadog.communication.otlp.OtlpHttpSender",
"datadog.communication.otlp.OtlpResponse",
"datadog.communication.otlp.OtlpSenderSupport"
)

dependencies {
api(project(":dd-trace-api"))
api(project(":communication"))
api(project(":internal-api"))
implementation(project(":utils:logging-utils"))
implementation(libs.slf4j)

testImplementation(libs.bundles.junit5)
testImplementation(libs.bundles.mockito)
testImplementation(libs.okhttp3.mockwebserver)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package datadog.communication.otlp;

import static datadog.communication.ddagent.TracerVersion.TRACER_VERSION;

import datadog.trace.api.Config;
import java.util.function.BiConsumer;

/** Canonical OTLP resource attributes shared by the trace and profile exports. */
public final class OtlpCanonicalResourceAttributes {
private OtlpCanonicalResourceAttributes() {}

/**
* Visits {@code service.name}, {@code deployment.environment.name} and {@code service.version}
* (when not empty), {@code host.name} (when reported and not empty) and the {@code
* telemetry.sdk.*} attributes, in that order.
*/
public static void visit(Config config, BiConsumer<String, String> visitor) {
visitor.accept("service.name", config.getServiceName());
String env = config.getEnv();
if (!env.isEmpty()) {
visitor.accept("deployment.environment.name", env);
}
String version = config.getVersion();
if (!version.isEmpty()) {
visitor.accept("service.version", version);
}
if (config.isReportHostName()) {
String hostName = config.getHostName();
if (hostName != null && !hostName.isEmpty()) {
visitor.accept("host.name", hostName);
}
}
visitor.accept("telemetry.sdk.name", "datadog");
visitor.accept("telemetry.sdk.version", TRACER_VERSION);
visitor.accept("telemetry.sdk.language", "java");
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.core.otlp.common;
package datadog.communication.otlp;

import java.io.IOException;
import javax.annotation.Nonnull;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package datadog.trace.core.otlp.common;
package datadog.communication.otlp;

import static datadog.communication.http.OkHttpUtils.buildHttp2Client;
import static datadog.communication.http.OkHttpUtils.isPlainHttp;

import datadog.communication.http.HttpRetryPolicy;
import datadog.logging.RatelimitedLogger;
import datadog.trace.api.config.OtlpConfig.Compression;
import datadog.trace.common.writer.RemoteApi;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okhttp3.HttpUrl;
Expand Down Expand Up @@ -53,7 +52,7 @@ public OtlpGrpcSender(
}

@Override
public RemoteApi.Response send(OtlpPayload payload) {
public OtlpResponse send(OtlpPayload payload) {
return OtlpSenderSupport.send(client, retryPolicy, makeRequest(payload), RATELIMITED_LOGGER);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.core.otlp.common;
package datadog.communication.otlp;

import java.io.IOException;
import javax.annotation.Nonnull;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package datadog.trace.core.otlp.common;
package datadog.communication.otlp;

import static datadog.communication.http.OkHttpUtils.buildHttpClient;
import static datadog.communication.http.OkHttpUtils.isPlainHttp;

import datadog.communication.http.HttpRetryPolicy;
import datadog.logging.RatelimitedLogger;
import datadog.trace.api.config.OtlpConfig.Compression;
import datadog.trace.common.writer.RemoteApi;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okhttp3.HttpUrl;
Expand Down Expand Up @@ -57,7 +56,7 @@ public HttpUrl url() {
}

@Override
public RemoteApi.Response send(OtlpPayload payload) {
public OtlpResponse send(OtlpPayload payload) {
return OtlpSenderSupport.send(client, retryPolicy, makeRequest(payload), RATELIMITED_LOGGER);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.core.otlp.common;
package datadog.communication.otlp;

import java.nio.ByteBuffer;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package datadog.communication.otlp;

import java.util.Optional;
import java.util.OptionalInt;

/**
* Encapsulates the result of an OTLP export attempt.
*
* <p>If communication fails or times out, the response will NOT be successful and will lack a
* status code, but will have an exception.
*
* <p>If communication succeeds, the response will have a status code and will be marked as success
* or failure in accordance with the code.
*
* <p>NOTE: A successful communication may still contain an exception if there was a problem parsing
* the response.
*/
public final class OtlpResponse {
/** Factory method for a successful request with a trivial response body */
public static OtlpResponse success(final int status) {
return new OtlpResponse(true, status, null, null);
}

/** Factory method for a successful request with a response body */
public static OtlpResponse success(final int status, String response) {
return new OtlpResponse(true, status, null, response);
}

/** Factory method for a successful request with a malformed response body */
public static OtlpResponse successWithParseError(final int status, final Throwable exception) {
return new OtlpResponse(true, status, exception, null);
}

/** Factory method for a request that received an error status in response */
public static OtlpResponse failed(final int status) {
return new OtlpResponse(false, status, null, null);
}

/** Factory method for a request that received an error status and a response body */
public static OtlpResponse failed(final int status, String response) {
return new OtlpResponse(false, status, null, response);
}

/** Factory method for a failed communication attempt */
public static OtlpResponse failed(final Throwable exception) {
return new OtlpResponse(false, null, exception, null);
}

private final boolean success;
private final Integer status;
private final Throwable exception;
private final String response;

private OtlpResponse(
final boolean success, final Integer status, final Throwable exception, String response) {
this.success = success;
this.status = status;
this.exception = exception;
this.response = response;
}

public boolean success() {
return success;
}

public OptionalInt status() {
return status == null ? OptionalInt.empty() : OptionalInt.of(status);
}

public Optional<Throwable> exception() {
return Optional.ofNullable(exception);
}

/** The raw response body, or empty for failed communication attempts (no body received) */
public Optional<String> response() {
return Optional.ofNullable(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package datadog.communication.otlp;

/** Sends chunks of OTLP data. */
public interface OtlpSender {
OtlpResponse send(OtlpPayload payload);

void shutdown();
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package datadog.trace.core.otlp.common;
package datadog.communication.otlp;

import static datadog.communication.http.OkHttpUtils.sendWithRetries;
import static datadog.trace.common.writer.RemoteApi.Response.failed;
import static datadog.trace.common.writer.RemoteApi.Response.success;
import static datadog.communication.otlp.OtlpResponse.failed;
import static datadog.communication.otlp.OtlpResponse.success;

import datadog.communication.http.HttpRetryPolicy;
import datadog.logging.RatelimitedLogger;
import datadog.trace.common.writer.RemoteApi;
import java.io.IOException;
import okhttp3.OkHttpClient;

Expand All @@ -15,7 +14,7 @@ final class OtlpSenderSupport {
private OtlpSenderSupport() {}

/** Executes the given request with retries, logging failures via the rate-limited logger. */
static RemoteApi.Response send(
static OtlpResponse send(
OkHttpClient client,
HttpRetryPolicy.Factory retryPolicy,
okhttp3.Request request,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package datadog.communication.otlp;

import static datadog.communication.ddagent.TracerVersion.TRACER_VERSION;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import datadog.trace.api.Config;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;

class OtlpCanonicalResourceAttributesTest {

@Test
void visitsAllAttributesInOrder() {
Config config = config("env", "1.0", true, "host");

assertEquals(
asList(
"service.name=svc",
"deployment.environment.name=env",
"service.version=1.0",
"host.name=host",
"telemetry.sdk.name=datadog",
"telemetry.sdk.version=" + TRACER_VERSION,
"telemetry.sdk.language=java"),
visit(config));
}

@Test
void skipsEmptyAndUnreportedAttributes() {
Config config = config("", "", false, "host");

assertEquals(
asList(
"service.name=svc",
"telemetry.sdk.name=datadog",
"telemetry.sdk.version=" + TRACER_VERSION,
"telemetry.sdk.language=java"),
visit(config));
}

@Test
void skipsEmptyHostName() {
Config config = config("env", "1.0", true, "");

assertEquals(
asList(
"service.name=svc",
"deployment.environment.name=env",
"service.version=1.0",
"telemetry.sdk.name=datadog",
"telemetry.sdk.version=" + TRACER_VERSION,
"telemetry.sdk.language=java"),
visit(config));
}

private static Config config(String env, String version, boolean reportHost, String host) {
Config config = mock(Config.class);
when(config.getServiceName()).thenReturn("svc");
when(config.getEnv()).thenReturn(env);
when(config.getVersion()).thenReturn(version);
when(config.isReportHostName()).thenReturn(reportHost);
when(config.getHostName()).thenReturn(host);
return config;
}

private static List<String> visit(Config config) {
List<String> visited = new ArrayList<>();
OtlpCanonicalResourceAttributes.visit(config, (k, v) -> visited.add(k + "=" + v));
return visited;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.core.otlp.common;
package datadog.communication.otlp;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.core.otlp.common;
package datadog.communication.otlp;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.core.otlp.common;
package datadog.communication.otlp;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
Expand All @@ -12,7 +12,6 @@
import datadog.communication.http.HttpRetryPolicy;
import datadog.communication.http.OkHttpUtils;
import datadog.logging.RatelimitedLogger;
import datadog.trace.common.writer.RemoteApi;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
Expand All @@ -39,8 +38,7 @@ void successfulResponseIsReturnedWithoutLogging() throws IOException {
.when(() -> OkHttpUtils.sendWithRetries(client, retryPolicy, request))
.thenReturn(response);

RemoteApi.Response result =
OtlpSenderSupport.send(client, retryPolicy, request, ratelimitedLogger);
OtlpResponse result = OtlpSenderSupport.send(client, retryPolicy, request, ratelimitedLogger);

assertTrue(result.success());
assertEquals(200, result.status().getAsInt());
Expand All @@ -56,8 +54,7 @@ void unsuccessfulResponseIsReturnedAndLogged() throws IOException {
.when(() -> OkHttpUtils.sendWithRetries(client, retryPolicy, request))
.thenReturn(response);

RemoteApi.Response result =
OtlpSenderSupport.send(client, retryPolicy, request, ratelimitedLogger);
OtlpResponse result = OtlpSenderSupport.send(client, retryPolicy, request, ratelimitedLogger);

assertFalse(result.success());
assertEquals(500, result.status().getAsInt());
Expand All @@ -73,8 +70,7 @@ void ioExceptionIsReturnedAsFailureAndLogged() throws IOException {
.when(() -> OkHttpUtils.sendWithRetries(client, retryPolicy, request))
.thenThrow(exception);

RemoteApi.Response result =
OtlpSenderSupport.send(client, retryPolicy, request, ratelimitedLogger);
OtlpResponse result = OtlpSenderSupport.send(client, retryPolicy, request, ratelimitedLogger);

assertFalse(result.success());
assertTrue(result.exception().isPresent());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mock-maker-inline
Loading
Loading