Skip to content

Commit ba13352

Browse files
authored
Merge branch 'master' into feature/agent-java-neutral-naming
2 parents a969329 + 47d76e0 commit ba13352

File tree

60 files changed

+1038
-195
lines changed

Some content is hidden

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

60 files changed

+1038
-195
lines changed

.ci/schedule-weekly.groovy

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ pipeline {
1818
cron('H H(1-4) * * 1')
1919
}
2020
stages {
21-
stage('Run JDK compatibility tests') {
21+
stage('Agent weekly exhaustive test') {
2222
steps {
2323
build(job: 'apm-agent-java/apm-agent-java-mbp/main',
2424
parameters: [
25-
booleanParam(name: 'compatibility_ci', value: true)
25+
booleanParam(name: 'jdk_compatibility_ci', value: true),
26+
booleanParam(name: 'end_to_end_tests_ci', value: true),
27+
booleanParam(name: 'agent_integration_tests_ci', value: true),
2628
],
2729
propagate: false,
2830
wait: false

.github/dependabot.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ updates:
3232
- dependency-name: "com.datastax.cassandra:cassandra-driver-core"
3333
- dependency-name: "com.datastax.oss:java-driver-core"
3434
- dependency-name: "io.micrometer:*"
35+
- dependency-name: "jakarta.*:*"
3536
ignore:
3637
- dependency-name: "net.bytebuddy:byte-buddy-agent"
3738
# We deliberately want to keep this older version of Byte Buddy for our runtime attach test
3839
versions: ["<=1.9.16"]
40+
- dependency-name: "org.slf4j:slf4j-api"
41+
# A static arbitrary version used within our external plugin test
42+
versions: ["<=1.7.25"]

.github/workflows/triggerCI.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: "Trigger Tests"
2+
on:
3+
pull_request_target:
4+
types: [ready_for_review]
5+
6+
jobs:
7+
triggerCI:
8+
runs-on: ubuntu-latest
9+
if: ${{ !github.event.pull_request.draft }}
10+
steps:
11+
- name: Check team membership for user
12+
uses: elastic/[email protected]
13+
id: checkUserMember
14+
with:
15+
username: ${{ github.actor }}
16+
team: 'apm'
17+
usernamesToExclude: |
18+
apmmachine
19+
dependabot
20+
dependabot[bot]
21+
GITHUB_TOKEN: ${{ secrets.APM_TECH_USER_TOKEN }}
22+
- name: Add comment to trigger tests
23+
if: steps.checkUserMember.outputs.isTeamMember == 'true' && steps.checkUserMember.outputs.isExcluded != 'true'
24+
uses: wow-actions/auto-comment@v1
25+
with:
26+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27+
pullRequestReadyForReview: /test
28+

CHANGELOG.asciidoc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,13 @@ endif::[]
3434
===== Bug fixes
3535
* Fix runtime attach with some docker images - {pull}2385[#2385]
3636
* Restore dynamic capability to `log_level` config for plugin loggers - {pull}2384[#2384]
37-
* Fix slf4j-related `LinkageError` - {pull}2390[#2390]
37+
* Fix slf4j-related `LinkageError` - {pull}2390[#2390] and {pull}2376[#2376]
3838
* Fix possible deadlock occurring when Byte Buddy reads System properties by warming up bytecode instrumentation code
3939
paths. The BCI warmup is on by default and may be disabled through the internal `warmup_byte_buddy` config option - {pull}2368[#2368]
40+
* Fixed few dubbo plugin issues - {pull}2149[#2149]
41+
** Dubbo transaction will should be created at the provider side
42+
** APM headers conversion issue within dubbo transaction
43+
* Fix External plugins automatic setting of span outcome - {pull}2376[#2376]
4044
4145
[[release-notes-1.x]]
4246
=== Java Agent version 1.x

Jenkinsfile

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pipeline {
1212
DOCKERHUB_SECRET = 'secret/apm-team/ci/elastic-observability-dockerhub'
1313
ELASTIC_DOCKER_SECRET = 'secret/apm-team/ci/docker-registry/prod'
1414
CODECOV_SECRET = 'secret/apm-team/ci/apm-agent-java-codecov'
15-
GITHUB_CHECK_ITS_NAME = 'Integration Tests'
15+
GITHUB_CHECK_ITS_NAME = 'End-To-End Integration Tests'
1616
ITS_PIPELINE = 'apm-integration-tests-selector-mbp/main'
1717
MAVEN_CONFIG = '-Dmaven.repo.local=.m2'
1818
OPBEANS_REPO = 'opbeans-java'
@@ -30,15 +30,35 @@ pipeline {
3030
quietPeriod(10)
3131
}
3232
triggers {
33-
issueCommentTrigger("(${obltGitHubComments()}|^run (compatibility|benchmark|integration) tests)")
33+
issueCommentTrigger("(${obltGitHubComments()}|^run (jdk compatibility|benchmark|integration|end-to-end) tests)")
3434
}
3535
parameters {
3636
string(name: 'MAVEN_CONFIG', defaultValue: '-V -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -Dhttps.protocols=TLSv1.2 -Dmaven.wagon.http.retryHandler.count=3 -Dmaven.wagon.httpconnectionManager.ttlSeconds=25', description: 'Additional maven options.')
37+
38+
// Note about GH checks and optional steps
39+
//
40+
// All the steps executed by default are included in GH checks
41+
// All the mandatory steps not included by default need to be added in GH branch protection rules.
42+
43+
// enabled by default, required for merge through default GH check
3744
booleanParam(name: 'test_ci', defaultValue: true, description: 'Enable Unit tests')
38-
booleanParam(name: 'agent_integration_tests_ci', defaultValue: true, description: 'Enable Agent Integration tests')
39-
booleanParam(name: 'endtoend_tests_ci', defaultValue: true, description: 'Enable APM End-to-End Integration tests')
40-
booleanParam(name: 'bench_ci', defaultValue: true, description: 'Enable benchmarks')
41-
booleanParam(name: 'compatibility_ci', defaultValue: false, description: 'Enable JDK compatibility tests')
45+
46+
// disabled by default, but required for merge, there are two GH checks:
47+
// - Non-Application Server integration tests
48+
// - Application Server integration tests
49+
// opt-in with 'ci:agent-integration'
50+
booleanParam(name: 'agent_integration_tests_ci', defaultValue: false, description: 'Enable Agent Integration tests')
51+
52+
// disabled by default, but required for merge, GH check name is ${GITHUB_CHECK_ITS_NAME}
53+
// opt-in with 'ci:end-to-end' tag on PR
54+
booleanParam(name: 'end_to_end_tests_ci', defaultValue: false, description: 'Enable APM End-to-End tests')
55+
56+
// disabled by default, not required for merge
57+
booleanParam(name: 'bench_ci', defaultValue: false, description: 'Enable benchmarks')
58+
59+
// disabled by default, not required for merge
60+
// opt-in with 'ci:jdk-compatibility' tag on PR
61+
booleanParam(name: 'jdk_compatibility_ci', defaultValue: false, description: 'Enable JDK compatibility tests')
4262
}
4363
stages {
4464
stage('Initializing'){
@@ -163,7 +183,12 @@ pipeline {
163183
}
164184
when {
165185
beforeAgent true
166-
expression { return params.agent_integration_tests_ci }
186+
anyOf {
187+
expression { return params.agent_integration_tests_ci }
188+
expression { return env.GITHUB_COMMENT?.contains('integration tests') }
189+
expression { matchesPrLabel(label: 'ci:agent-integration') }
190+
expression { return env.CHANGE_ID != null && !pullRequest.draft }
191+
}
167192
}
168193
steps {
169194
withGithubNotify(context: 'Non-Application Server integration tests', tab: 'tests') {
@@ -192,7 +217,12 @@ pipeline {
192217
}
193218
when {
194219
beforeAgent true
195-
expression { return params.agent_integration_tests_ci }
220+
anyOf {
221+
expression { return params.agent_integration_tests_ci }
222+
expression { return env.GITHUB_COMMENT?.contains('integration tests') }
223+
expression { matchesPrLabel(label: 'ci:agent-integration') }
224+
expression { return env.CHANGE_ID != null && !pullRequest.draft }
225+
}
196226
}
197227
steps {
198228
withGithubNotify(context: 'Application Server integration tests', tab: 'tests') {
@@ -229,9 +259,6 @@ pipeline {
229259
allOf {
230260
anyOf {
231261
branch 'main'
232-
branch "\\d+\\.\\d+"
233-
branch "v\\d?"
234-
tag pattern: 'v\\d+\\.\\d+\\.\\d+', comparator: 'REGEXP'
235262
expression { return env.GITHUB_COMMENT?.contains('benchmark tests') }
236263
}
237264
expression { return params.bench_ci }
@@ -295,9 +322,10 @@ pipeline {
295322
allOf {
296323
expression { return env.ONLY_DOCS == "false" }
297324
anyOf {
298-
changeRequest()
299-
expression { return params.endtoend_tests_ci }
300-
expression { return env.GITHUB_COMMENT?.contains('integration tests') }
325+
expression { return params.end_to_end_tests_ci }
326+
expression { return env.GITHUB_COMMENT?.contains('end-to-end tests') }
327+
expression { matchesPrLabel(label: 'ci:end-to-end') }
328+
expression { return env.CHANGE_ID != null && !pullRequest.draft }
301329
}
302330
}
303331
}
@@ -318,8 +346,9 @@ pipeline {
318346
allOf {
319347
expression { return env.ONLY_DOCS == "false" }
320348
anyOf {
321-
expression { return params.compatibility_ci }
322-
expression { return env.GITHUB_COMMENT?.contains('compatibility tests') }
349+
expression { return params.jdk_compatibility_ci }
350+
expression { return env.GITHUB_COMMENT?.contains('jdk compatibility tests') }
351+
expression { matchesPrLabel(label: 'ci:jdk-compatibility') }
323352
}
324353
}
325354
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package co.elastic.apm.agent.common.util;
20+
21+
import java.util.Arrays;
22+
import java.util.HashSet;
23+
import java.util.Set;
24+
25+
public class AgentInfo {
26+
27+
private static final Set<String> dependencyPackages = new HashSet<>(Arrays.asList(
28+
"org.slf4j",
29+
"org.apache.logging",
30+
"net.bytebuddy",
31+
"org.objectweb.asm",
32+
"org.jctools" ,
33+
"org.stagemonitor",
34+
"org.HdrHistogram",
35+
"co.elastic.logging",
36+
"com.blogspot.mydailyjava.weaklockfree",
37+
"com.lmax.disruptor",
38+
"com.dslplatform.json",
39+
"com.googlecode.concurrentlinkedhashmap"
40+
));
41+
42+
private static final Set<String> agentRootPackages = new HashSet<>(Arrays.asList(
43+
"co.elastic.apm",
44+
"bootstrap.co.elastic.apm",
45+
"bootstrap.java.lang"
46+
));
47+
48+
/**
49+
* Returns a list of packages of dependencies used by the agent.
50+
* Should be updated manually whenever a new dependency is added to the agent code.
51+
* See {@code co.elastic.apm.agent.premain.AgentPackagingIT#validateDependencyPackages()}.
52+
* @return a list of root packages of dependencies used by the agent.
53+
*/
54+
public static Set<String> getAgentDependencyPackages() {
55+
return dependencyPackages;
56+
}
57+
58+
/**
59+
* Returns a list of root packages of agent classes.
60+
* Should be updated manually whenever a new root is added to the agent code.
61+
* See {@code co.elastic.apm.agent.premain.AgentPackagingIT#validateDependencyPackages()}.
62+
* @return a list of agent root packages.
63+
*/
64+
public static Set<String> getAgentRootPackages() {
65+
return agentRootPackages;
66+
}
67+
}

apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,12 +198,12 @@ public boolean accept(File dir, String name) {
198198
}
199199
List<ClassLoader> result = new ArrayList<>(pluginJars.length);
200200
for (File pluginJar : pluginJars) {
201+
logger.info("Loading plugin {}", pluginJar.getName());
201202
try {
202203
result.add(new ExternalPluginClassLoader(pluginJar, ElasticApmAgent.class.getClassLoader()));
203-
} catch (IOException e) {
204-
logger.error(e.getMessage(), e);
204+
} catch (Exception e) {
205+
logger.error("Error loading external plugin", e);
205206
}
206-
logger.info("Loading plugin {}", pluginJar.getName());
207207
}
208208
return result;
209209
}

apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ public class IndyBootstrap {
207207
* Caches the names of classes that are defined within a package and it's subpackages
208208
*/
209209
private static final ConcurrentMap<String, List<String>> classesByPackage = new ConcurrentHashMap<>();
210+
210211
@Nullable
211212
static Method indyBootstrapMethod;
212213

@@ -362,10 +363,19 @@ public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup,
362363
MethodHandle instrumentedMethod = args.length >= 5 ? (MethodHandle) args[4] : null;
363364

364365
ClassLoader instrumentationClassLoader = ElasticApmAgent.getInstrumentationClassLoader(adviceClassName);
366+
ClassLoader targetClassLoader = lookup.lookupClass().getClassLoader();
365367
ClassFileLocator classFileLocator;
366368
List<String> pluginClasses = new ArrayList<>();
367369
if (instrumentationClassLoader instanceof ExternalPluginClassLoader) {
368-
pluginClasses.addAll(((ExternalPluginClassLoader) instrumentationClassLoader).getClassNames());
370+
List<String> externalPluginClasses = ((ExternalPluginClassLoader) instrumentationClassLoader).getClassNames();
371+
for (String externalPluginClass : externalPluginClasses) {
372+
if (// API classes have no dependencies and don't need to be loaded by an IndyPluginCL
373+
!(externalPluginClass.startsWith("co.elastic.apm.api")) &&
374+
!(externalPluginClass.startsWith("co.elastic.apm.opentracing"))
375+
) {
376+
pluginClasses.add(externalPluginClass);
377+
}
378+
}
369379
File agentJarFile = ElasticApmAgent.getAgentJarFile();
370380
if (agentJarFile == null) {
371381
throw new IllegalStateException("External plugin cannot be applied - can't find agent jar");
@@ -380,7 +390,7 @@ public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup,
380390
}
381391
pluginClasses.add(LOOKUP_EXPOSER_CLASS_NAME);
382392
ClassLoader pluginClassLoader = IndyPluginClassLoaderFactory.getOrCreatePluginClassLoader(
383-
lookup.lookupClass().getClassLoader(),
393+
targetClassLoader,
384394
pluginClasses,
385395
// we provide the instrumentation class loader as the agent class loader, but it could actually be an
386396
// ExternalPluginClassLoader, of which parent is the agent class loader, so this works as well.

apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/CustomElementMatchers.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import co.elastic.apm.agent.matcher.WildcardMatcher;
2323
import co.elastic.apm.agent.sdk.weakconcurrent.WeakConcurrent;
2424
import co.elastic.apm.agent.sdk.weakconcurrent.WeakMap;
25+
import co.elastic.apm.agent.util.ClassLoaderUtils;
2526
import co.elastic.apm.agent.util.Version;
2627
import net.bytebuddy.description.NamedElement;
2728
import net.bytebuddy.description.annotation.AnnotationSource;
@@ -54,7 +55,7 @@ public class CustomElementMatchers {
5455
private static final ElementMatcher.Junction.AbstractBase<ClassLoader> AGENT_CLASS_LOADER_MATCHER = new ElementMatcher.Junction.AbstractBase<ClassLoader>() {
5556
@Override
5657
public boolean matches(@Nullable ClassLoader classLoader) {
57-
return classLoader != null && classLoader.getClass().getName().startsWith("co.elastic.apm");
58+
return ClassLoaderUtils.isAgentClassLoader(classLoader);
5859
}
5960
};
6061

apm-agent-core/src/main/java/co/elastic/apm/agent/bci/classloading/ExternalPluginClassLoader.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
*/
1919
package co.elastic.apm.agent.bci.classloading;
2020

21+
import co.elastic.apm.agent.common.util.AgentInfo;
2122
import co.elastic.apm.agent.configuration.CoreConfiguration;
2223
import co.elastic.apm.agent.sdk.ElasticApmInstrumentation;
24+
import co.elastic.apm.agent.sdk.logging.LoggerFactory;
2325

2426
import java.io.File;
2527
import java.io.IOException;
@@ -44,7 +46,8 @@ public ExternalPluginClassLoader(File pluginJar, ClassLoader agentClassLoader) t
4446
super(new URL[]{pluginJar.toURI().toURL()}, agentClassLoader);
4547
classNames = Collections.unmodifiableList(scanForClasses(pluginJar));
4648
if (classNames.contains(ElasticApmInstrumentation.class.getName())) {
47-
throw new IllegalStateException("The plugin %s contains the plugin SDK. Please make sure the scope for the dependency apm-agent-plugin-sdk is set to provided.");
49+
throw new IllegalStateException(String.format("The plugin %s contains the plugin SDK. Please make sure the " +
50+
"scope for the dependency apm-agent-plugin-sdk is set to provided.", pluginJar.getName()));
4851
}
4952
}
5053

@@ -55,7 +58,27 @@ private List<String> scanForClasses(File pluginJar) throws IOException {
5558
while (entries.hasMoreElements()) {
5659
JarEntry jarEntry = entries.nextElement();
5760
if (jarEntry.getName().endsWith(".class")) {
58-
tempClassNames.add(jarEntry.getName().replace('/', '.').substring(0, jarEntry.getName().length() - 6));
61+
String fqcn = jarEntry.getName().replace('/', '.').substring(0, jarEntry.getName().length() - 6);
62+
if (fqcn.startsWith("org.slf4j") || fqcn.startsWith("org.apache.logging")) {
63+
throw new IllegalStateException(String.format("Package \"%s\" is used within plugin %s. This is not allowed " +
64+
"because it is already used by the agent. For logging purposes, use the agent SDK logging facade - %s",
65+
fqcn.substring(0, fqcn.lastIndexOf('.')),
66+
pluginJar.getName(),
67+
LoggerFactory.class.getName()
68+
));
69+
}
70+
for (String agentDependencyPackage : AgentInfo.getAgentDependencyPackages()) {
71+
if (fqcn.startsWith(agentDependencyPackage)) {
72+
throw new IllegalStateException(String.format("Package \"%s\" is used within plugin %s. This is not allowed " +
73+
"because the same dependency is used by the agent. Please either replace the corresponding dependency or " +
74+
"make sure its scope is set to provided. See the full list of such packages in %s",
75+
fqcn.substring(0, fqcn.lastIndexOf('.')),
76+
pluginJar.getName(),
77+
AgentInfo.class.getName()
78+
));
79+
}
80+
}
81+
tempClassNames.add(fqcn);
5982
}
6083
}
6184
}

0 commit comments

Comments
 (0)