Skip to content

Commit dad5bd1

Browse files
authored
Merge branch 'master' into cbeauchesne/final_status
2 parents 1ed6dac + 4368dcf commit dad5bd1

50 files changed

Lines changed: 859 additions & 349 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/skills/migrate-groovy-to-java/SKILL.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
---
22
name: migrate-groovy-to-java
3-
description: migrate test groovy files to java
3+
description: >
4+
Converts Spock/Groovy test files in a Gradle module to equivalent JUnit 5 Java tests.
5+
Use when asked to "migrate groovy", "convert groovy to java", "g2j", or when a module
6+
has .groovy test files that need to be replaced with .java equivalents.
47
---
58

69
Migrate test Groovy files to Java using JUnit 5
@@ -18,6 +21,8 @@ When converting Groovy code to Java code, make sure that:
1821
- Ensure parameterized test names are human-readable (i.e. no hashcodes); instead add a description string as the first `Arguments.arguments(...)` value or index the test case
1922
- When converting tuples, create a light dedicated structure instead to keep the typing system
2023
- Instead of checking a state and throwing an exception, use JUnit asserts
24+
- Instead of using `assertTrue(a.equals(b))` or `assertFalse(a.equals(b))`, use `assertEquals(expected, actual)` and `assertNotEquals(unexpected, actual)`
25+
- Import frequently used types rather than using fully-qualified names inline, to improve readability
2126
- Do not wrap checked exceptions and throw a Runtime exception; prefer adding a throws clause at method declaration
2227
- Do not mark local variables `final`
2328
- Ensure variables are human-readable; avoid single-letter names and pre-define variables that are referenced multiple times
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
issuer: https://token.actions.githubusercontent.com
2+
3+
subject: repo:DataDog/dd-trace-java:ref:refs/heads/master
4+
5+
claim_pattern:
6+
event_name: (schedule|workflow_dispatch)
7+
ref: refs/heads/master
8+
ref_protected: "true"
9+
job_workflow_ref: DataDog/dd-trace-java/\.github/workflows/update-smoke-test-latest-versions\.yaml@refs/heads/master
10+
11+
permissions:
12+
contents: write
13+
pull_requests: write

.github/workflows/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,14 @@ _Action:_ Create a PR updating the Grade dependencies and their locking files.
171171

172172
_Recovery:_ Manually trigger the action again.
173173

174+
### update-smoke-test-latest-versions [🔗](update-smoke-test-latest-versions.yaml)
175+
176+
_Trigger:_ Every week or manually.
177+
178+
_Action:_ Create a PR updating the pinned "latest" tool versions (Gradle, Maven, Maven Surefire) used by CI Visibility smoke tests.
179+
180+
_Recovery:_ Manually trigger the action again.
181+
174182
### update-jmxfetch-submodule [🔗](update-jmxfetch-submodule.yaml)
175183

176184
_Trigger:_ Monthly or manually
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
name: Update smoke test latest versions
2+
on:
3+
schedule:
4+
- cron: "0 5 * * 0"
5+
workflow_dispatch:
6+
7+
jobs:
8+
update-smoke-test-latest-versions:
9+
runs-on: ubuntu-latest
10+
name: Update smoke test latest versions
11+
permissions:
12+
contents: read
13+
id-token: write # Required for OIDC token federation
14+
steps:
15+
- uses: DataDog/dd-octo-sts-action@acaa02eee7e3bb0839e4272dacb37b8f3b58ba80 # v1.0.3
16+
id: octo-sts
17+
with:
18+
scope: DataDog/dd-trace-java
19+
policy: self.update-smoke-test-latest-versions.create-pr
20+
21+
- name: Checkout repository
22+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
23+
24+
- name: Define branch name
25+
id: define-branch
26+
run: |
27+
DATE=$(date +'%Y%m%d')
28+
echo "branch=ci/update-smoke-test-latest-versions-${DATE}" >> "$GITHUB_OUTPUT"
29+
30+
- name: Fetch latest Gradle version
31+
id: gradle
32+
run: |
33+
VERSION=$(curl -sf https://services.gradle.org/versions/current | jq -r '.version')
34+
if [ -z "$VERSION" ] || [ "$VERSION" = "null" ]; then
35+
echo "::error::Failed to fetch latest Gradle version"
36+
exit 1
37+
fi
38+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
39+
echo "Latest Gradle version: $VERSION"
40+
41+
- name: Fetch latest stable Maven version
42+
id: maven
43+
run: |
44+
METADATA=$(curl -sf https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/maven-metadata.xml)
45+
# Get all versions, filter out alpha/beta/rc, take the latest
46+
VERSION=$(echo "$METADATA" \
47+
| xmllint --xpath '//versions/version/text()' - 2>/dev/null \
48+
| tr ' ' '\n' \
49+
| grep -v -E '(alpha|beta|rc)' \
50+
| sort -V \
51+
| tail -1)
52+
if [ -z "$VERSION" ]; then
53+
echo "::error::Failed to fetch latest stable Maven version"
54+
exit 1
55+
fi
56+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
57+
echo "Latest stable Maven version: $VERSION"
58+
59+
- name: Fetch latest stable Maven Surefire version
60+
id: surefire
61+
run: |
62+
METADATA=$(curl -sf https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-surefire-plugin/maven-metadata.xml)
63+
# Get all versions, filter out alpha/beta, take the latest
64+
VERSION=$(echo "$METADATA" \
65+
| xmllint --xpath '//versions/version/text()' - 2>/dev/null \
66+
| tr ' ' '\n' \
67+
| grep -v -E '(alpha|beta)' \
68+
| sort -V \
69+
| tail -1)
70+
if [ -z "$VERSION" ]; then
71+
echo "::error::Failed to fetch latest stable Maven Surefire version"
72+
exit 1
73+
fi
74+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
75+
echo "Latest stable Maven Surefire version: $VERSION"
76+
77+
- name: Update properties files
78+
env:
79+
GRADLE_VERSION: ${{ steps.gradle.outputs.version }}
80+
MAVEN_VERSION: ${{ steps.maven.outputs.version }}
81+
SUREFIRE_VERSION: ${{ steps.surefire.outputs.version }}
82+
run: |
83+
printf '%s\n' \
84+
"# Pinned \"latest\" versions for CI Visibility Gradle smoke tests." \
85+
"# Updated automatically by the update-smoke-test-latest-versions workflow." \
86+
"gradle.version=${GRADLE_VERSION}" \
87+
> dd-smoke-tests/gradle/src/test/resources/latest-tool-versions.properties
88+
89+
printf '%s\n' \
90+
"# Pinned \"latest\" versions for CI Visibility Maven smoke tests." \
91+
"# Updated automatically by the update-smoke-test-latest-versions workflow." \
92+
"maven.version=${MAVEN_VERSION}" \
93+
"maven-surefire.version=${SUREFIRE_VERSION}" \
94+
> dd-smoke-tests/maven/src/test/resources/latest-tool-versions.properties
95+
96+
- name: Check for changes
97+
id: check-changes
98+
run: |
99+
if [[ -z "$(git status -s)" ]]; then
100+
echo "No changes to commit."
101+
echo "has_changes=false" >> "$GITHUB_OUTPUT"
102+
else
103+
echo "has_changes=true" >> "$GITHUB_OUTPUT"
104+
fi
105+
106+
- name: Configure git
107+
if: steps.check-changes.outputs.has_changes == 'true'
108+
run: |
109+
git config user.name "github-actions[bot]"
110+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
111+
112+
- name: Create commit
113+
if: steps.check-changes.outputs.has_changes == 'true'
114+
id: create-commit
115+
run: |
116+
git add dd-smoke-tests/gradle/src/test/resources/latest-tool-versions.properties \
117+
dd-smoke-tests/maven/src/test/resources/latest-tool-versions.properties
118+
git commit -m "chore: Update smoke test latest tool versions"
119+
echo "commit=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
120+
121+
- name: Push changes
122+
if: steps.check-changes.outputs.has_changes == 'true'
123+
uses: DataDog/commit-headless@05d7b7ee023e2c7d01c47832d420c2503cd416f3 # action/v2.0.3
124+
with:
125+
token: "${{ steps.octo-sts.outputs.token }}"
126+
branch: "${{ steps.define-branch.outputs.branch }}"
127+
head-sha: "${{ github.sha }}"
128+
create-branch: true
129+
command: push
130+
commits: "${{ steps.create-commit.outputs.commit }}"
131+
132+
- name: Create pull request
133+
if: steps.check-changes.outputs.has_changes == 'true'
134+
env:
135+
GH_TOKEN: ${{ steps.octo-sts.outputs.token }}
136+
run: |
137+
gh pr create --title "Update smoke test latest tool versions" \
138+
--base master \
139+
--head ${{ steps.define-branch.outputs.branch }} \
140+
--label "tag: dependencies" \
141+
--label "tag: no release notes" \
142+
--body "$(cat <<'EOF'
143+
# What Does This Do
144+
145+
This PR updates the pinned "latest" tool versions used by CI Visibility smoke tests:
146+
- Gradle: ${{ steps.gradle.outputs.version }}
147+
- Maven: ${{ steps.maven.outputs.version }}
148+
- Maven Surefire: ${{ steps.surefire.outputs.version }}
149+
150+
# Motivation
151+
152+
Keep smoke tests running against the latest stable versions of build tools.
153+
154+
# Contributor Checklist
155+
156+
- [ ] Verify smoke tests pass with the new versions
157+
EOF
158+
)"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package datadog.trace.bootstrap;
2+
3+
import org.openjdk.jmh.annotations.Benchmark;
4+
import org.openjdk.jmh.annotations.Fork;
5+
import org.openjdk.jmh.annotations.Measurement;
6+
import org.openjdk.jmh.annotations.Threads;
7+
import org.openjdk.jmh.annotations.Warmup;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
10+
11+
/**
12+
* Benchmark showing impact of ExceptionLogger
13+
*
14+
* <p>NOTE: This benchmark exists to check the efficiency of retrieving the ExceptionLogger.
15+
* Previously, this caused significant allocation.
16+
*/
17+
@Fork(2)
18+
@Warmup(iterations = 2)
19+
@Measurement(iterations = 5)
20+
@Threads(8)
21+
public class ExceptionLoggerBenchmark {
22+
@Benchmark
23+
public Logger getExceptionLogger() {
24+
// This matches what happens in the bytecode weaving that defends against
25+
// exception leaking out of instrumentation.
26+
return LoggerFactory.getLogger(ExceptionLogger.class);
27+
}
28+
}

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/ExceptionLogger.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
*
99
* <p>See datadog.trace.agent.tooling.ExceptionHandlers
1010
*/
11-
public class ExceptionLogger {
11+
public final class ExceptionLogger {
1212
public static final Logger LOGGER = LoggerFactory.getLogger(ExceptionLogger.class);
13+
14+
private ExceptionLogger() {}
1315
}

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,14 @@ public Context startSpan(REQUEST_CARRIER carrier, Context parentContext) {
174174
extracted = startInferredProxySpan(parentContext, extracted);
175175
AgentSpan span =
176176
tracer().startSpan(instrumentationName, spanName(), extracted).setMeasured(true);
177+
// Register service-entry span with inferred proxy span (if present) so that premature
178+
// finish calls from child spans (e.g., Spring MVC handler) are deferred until the
179+
// service-entry span finishes (after the response status is known).
180+
registerServiceEntrySpanInInferredProxy(parentContext, span);
181+
// Reset service name inherited from inferred proxy parent: the inferred span uses the
182+
// gateway domain name as service name, but the service-entry span should identify
183+
// the application (configured DD_SERVICE), not the upstream gateway.
184+
resetServiceNameIfUnderInferredProxy(parentContext, span);
177185
// Apply RequestBlockingAction if any
178186
Flow<Void> flow = callIGCallbackRequestHeaders(span, carrier);
179187
if (flow.getAction() instanceof RequestBlockingAction) {
@@ -193,6 +201,20 @@ protected AgentSpanContext startInferredProxySpan(Context context, AgentSpanCont
193201
return span.start(extracted);
194202
}
195203

204+
private void registerServiceEntrySpanInInferredProxy(
205+
Context parentContext, AgentSpan serviceEntrySpan) {
206+
InferredProxySpan inferredProxy = InferredProxySpan.fromContext(parentContext);
207+
if (inferredProxy != null) {
208+
inferredProxy.registerServiceEntrySpan(serviceEntrySpan);
209+
}
210+
}
211+
212+
private void resetServiceNameIfUnderInferredProxy(Context parentContext, AgentSpan span) {
213+
if (InferredProxySpan.fromContext(parentContext) != null) {
214+
span.setServiceName(Config.get().getServiceName());
215+
}
216+
}
217+
196218
private final DataStreamsTransactionTracker.TransactionSourceReader
197219
DSM_TRANSACTION_SOURCE_READER =
198220
(source, headerName) -> {

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ConfigurationUpdater.java

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -206,33 +206,40 @@ private List<Class<?>> detectMethodParameters(
206206
}
207207
List<Class<?>> result = new ArrayList<>();
208208
for (Class<?> changedClass : changedClasses) {
209-
Method[] declaredMethods = changedClass.getDeclaredMethods();
210209
boolean addClass = true;
211-
// capping scanning of methods to 100 to avoid generated class with thousand of methods
212-
// assuming that in those first 100 methods there is at least one with at least one parameter
213-
for (int methodIdx = 0; methodIdx < declaredMethods.length && methodIdx < 100; methodIdx++) {
214-
Method method = declaredMethods[methodIdx];
215-
Parameter[] parameters = method.getParameters();
216-
if (parameters.length == 0) {
217-
continue;
218-
}
219-
if (parameters[0].isNamePresent()) {
220-
if (!SpringHelper.isSpringUsingOnlyMethodParameters(instrumentation)) {
221-
return changedClasses;
210+
try {
211+
Method[] declaredMethods = changedClass.getDeclaredMethods();
212+
// capping scanning of methods to 100 to avoid generated class with thousand of methods
213+
// assuming that in those first 100 methods there is at least one with at least one
214+
// parameter
215+
for (int methodIdx = 0;
216+
methodIdx < declaredMethods.length && methodIdx < 100;
217+
methodIdx++) {
218+
Method method = declaredMethods[methodIdx];
219+
Parameter[] parameters = method.getParameters();
220+
if (parameters.length == 0) {
221+
continue;
222+
}
223+
if (parameters[0].isNamePresent()) {
224+
if (!SpringHelper.isSpringUsingOnlyMethodParameters(instrumentation)) {
225+
return changedClasses;
226+
}
227+
LOGGER.debug(
228+
"Detecting method parameter: method={} param={}, Skipping retransforming this class",
229+
method.getName(),
230+
parameters[0].getName());
231+
// skip the class: compiled with -parameters
232+
reportError(
233+
changes,
234+
"Method Parameters detected, instrumentation not supported for "
235+
+ changedClass.getTypeName());
236+
addClass = false;
222237
}
223-
LOGGER.debug(
224-
"Detecting method parameter: method={} param={}, Skipping retransforming this class",
225-
method.getName(),
226-
parameters[0].getName());
227-
// skip the class: compiled with -parameters
228-
reportError(
229-
changes,
230-
"Method Parameters detected, instrumentation not supported for "
231-
+ changedClass.getTypeName());
232-
addClass = false;
238+
// we found at leat a method with one parameter if name is not present we can stop there
239+
break;
233240
}
234-
// we found at leat a method with one parameter if name is not present we can stop there
235-
break;
241+
} catch (Exception e) {
242+
LOGGER.debug("Exception scanning method parameters", e);
236243
}
237244
if (addClass) {
238245
result.add(changedClass);

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/SourceFileTrackingTransformer.java

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -98,23 +98,27 @@ public byte[] transform(
9898
}
9999

100100
private void registerSourceFile(String className, byte[] classfileBuffer) {
101-
String javaClassName = Strings.getClassName(className);
102-
if (classNameFilter.isExcluded(javaClassName)) {
103-
return;
104-
}
105-
String sourceFile = ClassFileHelper.extractSourceFile(classfileBuffer);
106-
if (sourceFile == null) {
107-
return;
108-
}
109-
if (!isExtensionAllowed(sourceFile)) {
110-
return;
111-
}
112-
String simpleClassName = stripPackagePath(className);
113-
String simpleSourceFile = removeExtension(sourceFile);
114-
if (simpleClassName.equals(simpleSourceFile)) {
115-
return;
101+
try {
102+
String javaClassName = Strings.getClassName(className);
103+
if (classNameFilter.isExcluded(javaClassName)) {
104+
return;
105+
}
106+
String sourceFile = ClassFileHelper.extractSourceFile(classfileBuffer);
107+
if (sourceFile == null) {
108+
return;
109+
}
110+
if (!isExtensionAllowed(sourceFile)) {
111+
return;
112+
}
113+
String simpleClassName = stripPackagePath(className);
114+
String simpleSourceFile = removeExtension(sourceFile);
115+
if (simpleClassName.equals(simpleSourceFile)) {
116+
return;
117+
}
118+
finder.register(sourceFile, className);
119+
} catch (Exception e) {
120+
LOGGER.debug("Error registering source file {}: {}", className, e);
116121
}
117-
finder.register(sourceFile, className);
118122
}
119123

120124
private boolean isExtensionAllowed(String sourceFile) {

0 commit comments

Comments
 (0)