Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 6 additions & 8 deletions dd-java-agent/instrumentation/spark/sparkjava-2.3/build.gradle
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@

// building against 2.3 and testing against 2.4 because JettyHandler is available since 2.4 only
muzzle {
pass {
group = "com.sparkjava"
group = 'com.sparkjava'
module = 'spark-core'
versions = "[2.3,)"
versions = "[2.3,2.4)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AI has narrowed this down too much - it should be more open

assertInverse = true
}
}

apply from: "$rootDir/gradle/java.gradle"
apply from: "${rootDir}/gradle/java.gradle"

addTestSuiteForDir('latestDepTest', 'test')

dependencies {
compileOnly group: 'com.sparkjava', name: 'spark-core', version: '2.3'

testImplementation project(':dd-java-agent:instrumentation:jetty:jetty-server:jetty-server-9.0')
testImplementation group: 'com.sparkjava', name: 'spark-core', version: '2.3'
testImplementation project(':dd-java-agent:instrumentation-testing')

testImplementation group: 'com.sparkjava', name: 'spark-core', version: '2.4'

latestDepTestImplementation group: 'com.sparkjava', name: 'spark-core', version: '+'
latestDepTestImplementation group: 'com.sparkjava', name: 'spark-core', version: '2.3'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd expect to see 2.+ for the latest test version here

}
172 changes: 0 additions & 172 deletions dd-java-agent/instrumentation/spark/sparkjava-2.3/gradle.lockfile

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package datadog.trace.instrumentation.sparkjava;

import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
import java.util.Collection;
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public abstract class ExtractAdapter {

public static final class Request
implements AgentPropagation.ContextVisitor<HttpServletRequest> {
public static final Request GETTER = new Request();

@Override
public void forEachKey(
HttpServletRequest carrier, AgentPropagation.KeyClassifier classifier) {
Enumeration<String> headerNames = carrier.getHeaderNames();
if (headerNames == null) {
return;
}
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
if (!classifier.accept(headerName, carrier.getHeader(headerName))) {
return;
}
}
}
}

public static final class Response
implements AgentPropagation.ContextVisitor<HttpServletResponse> {
public static final Response GETTER = new Response();

@Override
public void forEachKey(
HttpServletResponse carrier, AgentPropagation.KeyClassifier classifier) {
Collection<String> headerNames = carrier.getHeaderNames();
if (headerNames == null) {
return;
}
for (String headerName : headerNames) {
if (!classifier.accept(headerName, carrier.getHeader(headerName))) {
return;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package datadog.trace.instrumentation.sparkjava;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static datadog.trace.instrumentation.sparkjava.SparkJavaDecorator.DECORATE;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import datadog.context.Context;
import datadog.context.ContextScope;
import datadog.trace.agent.tooling.Instrumenter;
import javax.servlet.http.HttpServletRequest;
import net.bytebuddy.asm.Advice;

public class JettyHandlerInstrumentation
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {

@Override
public String instrumentedType() {
return "spark.webserver.JettyHandler";

@mcculls mcculls Jul 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see it added span-creating advice for a specific (Spark) handler. This level of detail might not be required depending what spans the framework instrumentation is creating.

Specifically here it's creating a request span from the handler's request context - but the framework instrumentation should already be doing that.

}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
named("doHandle")
.and(takesArgument(0, String.class))
.and(
takesArgument(
1, named("org.eclipse.jetty.server.Request")))
.and(takesArgument(2, named("javax.servlet.http.HttpServletRequest")))
.and(takesArgument(3, named("javax.servlet.http.HttpServletResponse"))),
JettyHandlerInstrumentation.class.getName() + "$JettyHandlerAdvice");
}

public static class JettyHandlerAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static ContextScope onEnter(
@Advice.Argument(2) final HttpServletRequest request) {
final Context parentContext = DECORATE.extract(request);
return parentContext.attach();
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Enter final ContextScope scope) {
if (scope != null) {
scope.close();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package datadog.trace.instrumentation.sparkjava;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static datadog.trace.bootstrap.instrumentation.api.Java8BytecodeBridge.spanFromContext;
import static datadog.trace.bootstrap.instrumentation.decorator.HttpServerDecorator.DD_CONTEXT_ATTRIBUTE;
import static datadog.trace.instrumentation.sparkjava.SparkJavaDecorator.DECORATE;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import datadog.context.Context;
import datadog.context.ContextScope;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.bytebuddy.asm.Advice;

public class MatcherFilterInstrumentation
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {

@Override
public String instrumentedType() {
return "spark.webserver.MatcherFilter";
}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
named("doFilter")
.and(takesArgument(0, named("javax.servlet.ServletRequest")))
.and(takesArgument(1, named("javax.servlet.ServletResponse")))
.and(isPublic()),
MatcherFilterInstrumentation.class.getName() + "$MatcherFilterAdvice");
}

public static class MatcherFilterAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static ContextScope onEnter(
@Advice.Argument(0) final javax.servlet.ServletRequest servletRequest) {
if (!(servletRequest instanceof HttpServletRequest)) {
return null;
}

final HttpServletRequest request = (HttpServletRequest) servletRequest;

final Object contextAttr = request.getAttribute(DD_CONTEXT_ATTRIBUTE);
if (contextAttr instanceof Context) {
// Already instrumented by another HTTP server integration (e.g. Jetty)
return null;
}

final Context parentContext = DECORATE.extract(request);
final Context context = DECORATE.startSpan(request, parentContext);
final AgentSpan span = spanFromContext(context);
DECORATE.afterStart(span);
DECORATE.onRequest(span, request, request, parentContext);

final ContextScope scope = context.attach();
request.setAttribute(DD_CONTEXT_ATTRIBUTE, context);

return scope;
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(
@Advice.Argument(0) final javax.servlet.ServletRequest servletRequest,
@Advice.Argument(1) final javax.servlet.ServletResponse servletResponse,
@Advice.Enter final ContextScope scope,
@Advice.Thrown final Throwable throwable) {
if (scope == null) {
return;
}

final Context context = scope.context();
final AgentSpan span = spanFromContext(context);

if (servletResponse instanceof HttpServletResponse) {
DECORATE.onResponse(span, (HttpServletResponse) servletResponse);
}
if (throwable != null) {
DECORATE.onError(span, throwable);
}
DECORATE.beforeFinish(context);
scope.close();
span.finish();

if (servletRequest instanceof HttpServletRequest) {
((HttpServletRequest) servletRequest).removeAttribute(DD_CONTEXT_ATTRIBUTE);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package datadog.trace.instrumentation.sparkjava;

import datadog.trace.bootstrap.instrumentation.api.URIRawDataAdapter;
import javax.servlet.http.HttpServletRequest;

final class RequestURIDataAdapter extends URIRawDataAdapter {

private final HttpServletRequest request;

RequestURIDataAdapter(HttpServletRequest request) {
this.request = request;
}

@Override
public String scheme() {
return request.getScheme();
}

@Override
public String host() {
return request.getServerName();
}

@Override
public int port() {
return request.getServerPort();
}

@Override
protected String innerRawPath() {
return request.getRequestURI();
}

@Override
public String fragment() {
return null;
}

@Override
protected String innerRawQuery() {
return request.getQueryString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package datadog.trace.instrumentation.sparkjava;

import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass;
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static datadog.trace.bootstrap.instrumentation.api.Java8BytecodeBridge.currentSpan;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

public class RouteHandlerInstrumentation
implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice {

@Override
public String hierarchyMarkerType() {
return "spark.RouteImpl";
}

@Override
public ElementMatcher<TypeDescription> hierarchyMatcher() {
return extendsClass(named("spark.RouteImpl"));
}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
named("handle")
.and(isMethod())
.and(takesArgument(0, named("spark.Request")))
.and(takesArgument(1, named("spark.Response"))),
RouteHandlerInstrumentation.class.getName() + "$RouteHandlerAdvice");
}

public static class RouteHandlerAdvice {

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Thrown final Throwable throwable) {
if (throwable != null) {
final AgentSpan span = currentSpan();
if (span != null) {
span.addThrowable(throwable);
}
}
}
}
}

This file was deleted.

Loading
Loading