From 435e1d366db7c6a09ae1d0035d2981a2e1f86d9a Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Mon, 20 Feb 2023 10:23:38 +0100 Subject: [PATCH 1/4] Use composite action for updatecli workflow (#3023) * Use composite action * Add env --- .github/workflows/update-specs.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/update-specs.yml b/.github/workflows/update-specs.yml index 94759a2281..236927661d 100644 --- a/.github/workflows/update-specs.yml +++ b/.github/workflows/update-specs.yml @@ -6,23 +6,21 @@ on: - cron: '0 6 * * *' permissions: - pull-requests: write - contents: write + contents: read jobs: bump: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Setup Git - uses: elastic/apm-pipeline-library/.github/actions/setup-git@current - - name: Install Updatecli in the runner - uses: updatecli/updatecli-action@453502948b442d7b9a923de7b40cc7ce8628505c - - name: Run Updatecli + - uses: elastic/apm-pipeline-library/.github/actions/updatecli@current env: - GITHUB_TOKEN: ${{ github.token }} BRANCH_NAME: ${{ github.ref_name }} - run: updatecli apply --config ./.ci/update-specs.yml + with: + vaultUrl: ${{ secrets.VAULT_ADDR }} + vaultRoleId: ${{ secrets.VAULT_ROLE_ID }} + vaultSecretId: ${{ secrets.VAULT_SECRET_ID }} + pipeline: ./.ci/update-specs.yml - if: failure() uses: elastic/apm-pipeline-library/.github/actions/notify-build-status@current with: From 44ed8c2bec08da24283a645b5f76a81f6a035646 Mon Sep 17 00:00:00 2001 From: eyalkoren <41850454+eyalkoren@users.noreply.github.com> Date: Tue, 21 Feb 2023 14:40:19 +0200 Subject: [PATCH 2/4] Unnest exceptions before filtering (#3025) --- CHANGELOG.asciidoc | 1 + .../apm/agent/impl/ElasticApmTracer.java | 7 ++- .../apm/agent/impl/error/ErrorCapture.java | 8 +-- .../example/stacktrace/ErrorCaptureTest.java | 63 ++++++++++++++++--- 4 files changed, 61 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 9ee657fe7a..a53f312279 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -36,6 +36,7 @@ communication - {pull}2996[#2996] * Prevent potential connection leak on network failure - {pull}2869[#2869] * Fix for inferred spans where the parent id was also a child id - {pull}2686[#2686] * Fix context propagation for async 7.x and 8.x Elasticsearch clients - {pull}3015[#3015] +* Fix exceptions filtering based on <> when those are <> - {pull}3025[#3025] [[release-notes-1.x]] === Java Agent version 1.x diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java index 4ceafc421f..a7ec662330 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java @@ -333,9 +333,14 @@ public ErrorCapture captureException(@Nullable Throwable e, @Nullable AbstractSp @Nullable private ErrorCapture captureException(long epochMicros, @Nullable Throwable e, @Nullable AbstractSpan parent, @Nullable ClassLoader initiatingClassLoader) { - if (!isRunning()) { + if (!isRunning() || e == null) { return null; } + + while (e != null && WildcardMatcher.anyMatch(coreConfiguration.getUnnestExceptions(), e.getClass().getName()) != null) { + e = e.getCause(); + } + // note: if we add inheritance support for exception filtering, caching would be required for performance if (e != null && !WildcardMatcher.isAnyMatch(coreConfiguration.getIgnoreExceptions(), e.getClass().getName())) { ErrorCapture error = errorPool.createInstance(); diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/error/ErrorCapture.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/error/ErrorCapture.java index 2f1a503a9b..83659b2f8a 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/error/ErrorCapture.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/error/ErrorCapture.java @@ -18,7 +18,6 @@ */ package co.elastic.apm.agent.impl.error; -import co.elastic.apm.agent.configuration.CoreConfiguration; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.context.TransactionContext; import co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration; @@ -26,7 +25,6 @@ import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.TraceContext; import co.elastic.apm.agent.impl.transaction.Transaction; -import co.elastic.apm.agent.common.util.WildcardMatcher; import co.elastic.apm.agent.objectpool.Recyclable; import co.elastic.apm.agent.sdk.logging.Logger; import co.elastic.apm.agent.sdk.logging.LoggerFactory; @@ -153,11 +151,7 @@ public TraceContext getTraceContext() { } public void setException(Throwable e) { - if (WildcardMatcher.anyMatch(tracer.getConfig(CoreConfiguration.class).getUnnestExceptions(), e.getClass().getName()) != null) { - this.exception = e.getCause(); - } else { - this.exception = e; - } + exception = e; } public StringBuilder getCulprit() { diff --git a/apm-agent-core/src/test/java/org/example/stacktrace/ErrorCaptureTest.java b/apm-agent-core/src/test/java/org/example/stacktrace/ErrorCaptureTest.java index 4ff4d432be..77de041be1 100644 --- a/apm-agent-core/src/test/java/org/example/stacktrace/ErrorCaptureTest.java +++ b/apm-agent-core/src/test/java/org/example/stacktrace/ErrorCaptureTest.java @@ -19,7 +19,8 @@ package org.example.stacktrace; import co.elastic.apm.agent.MockTracer; -import co.elastic.apm.agent.configuration.SpyConfiguration; +import co.elastic.apm.agent.common.util.WildcardMatcher; +import co.elastic.apm.agent.configuration.CoreConfiguration; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.context.Request; import co.elastic.apm.agent.impl.error.ErrorCapture; @@ -27,7 +28,6 @@ import co.elastic.apm.agent.impl.transaction.Transaction; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.stagemonitor.configuration.ConfigurationRegistry; import java.util.List; @@ -36,14 +36,15 @@ class ErrorCaptureTest { - private StacktraceConfiguration stacktraceConfiguration; private ElasticApmTracer tracer; + private StacktraceConfiguration stacktraceConfiguration; + private CoreConfiguration coreConfiguration; @BeforeEach void setUp() { - final ConfigurationRegistry registry = SpyConfiguration.createSpyConfig(); - tracer = MockTracer.create(registry); - stacktraceConfiguration = registry.getConfig(StacktraceConfiguration.class); + tracer = MockTracer.createRealTracer(); + stacktraceConfiguration = tracer.getConfig(StacktraceConfiguration.class); + coreConfiguration = tracer.getConfig(CoreConfiguration.class); } @Test @@ -65,11 +66,45 @@ void testCulprit() { } @Test - void testUnnestNestedExceptions() { - final ErrorCapture errorCapture = new ErrorCapture(tracer); - final NestedException nestedException = new NestedException(new Exception()); - errorCapture.setException(nestedException); + void testUnnestNestedException() { + final NestedException nestedException = new NestedException(new CustomException()); + ErrorCapture errorCapture = tracer.captureException(nestedException, null, null); + assertThat(errorCapture).isNotNull(); assertThat(errorCapture.getException()).isNotInstanceOf(NestedException.class); + assertThat(errorCapture.getException()).isInstanceOf(CustomException.class); + } + + @Test + void testUnnestDoublyNestedException() { + final NestedException nestedException = new NestedException(new NestedException(new CustomException())); + ErrorCapture errorCapture = tracer.captureException(nestedException, null, null); + assertThat(errorCapture).isNotNull(); + assertThat(errorCapture.getException()).isNotInstanceOf(NestedException.class); + assertThat(errorCapture.getException()).isInstanceOf(CustomException.class); + } + + @Test + void testIgnoredNestedException() { + doReturn(List.of(WildcardMatcher.valueOf("*CustomException"))).when(coreConfiguration).getIgnoreExceptions(); + final NestedException nestedException = new NestedException(new CustomException()); + ErrorCapture errorCapture = tracer.captureException(nestedException, null, null); + assertThat(errorCapture).isNull(); + } + + @Test + void testNonConfiguredNestingException() { + final WrapperException wrapperException = new WrapperException(new CustomException()); + ErrorCapture errorCapture = tracer.captureException(wrapperException, null, null); + assertThat(errorCapture).isNotNull(); + assertThat(errorCapture.getException()).isInstanceOf(WrapperException.class); + } + + @Test + void testNonConfiguredWrappingConfigured() { + final NestedException nestedException = new NestedException(new WrapperException(new NestedException(new Exception()))); + ErrorCapture errorCapture = tracer.captureException(nestedException, null, null); + assertThat(errorCapture).isNotNull(); + assertThat(errorCapture.getException()).isInstanceOf(WrapperException.class); } private static class NestedException extends Exception { @@ -78,6 +113,14 @@ public NestedException(Throwable cause) { } } + private static class WrapperException extends Exception { + public WrapperException(Throwable cause) { + super(cause); + } + } + + private static class CustomException extends Exception {} + @Test void testTransactionContextTransfer() { final Transaction transaction = new Transaction(tracer); From 078da74c8109a3a58bcdba20f3d28f392da489ce Mon Sep 17 00:00:00 2001 From: jackshirazi Date: Wed, 22 Feb 2023 14:49:56 +0000 Subject: [PATCH 3/4] Add Activation method for telemetry on how the agent was started (#2926) * Add Activation method for telemetry on how the agent was started * fix NPE from test mode or not yet initialized agent * add integration tests * fix cyclic dependency * fix cyclic dependency 2nd try * ignore slim jars * parallelize the tests (they're mostly just waiting for the metadata) * cache the type * cache the type (forgot to add) * refactor * refactor to pass tests * fix doc typo * add env setup test * fix test expecting main but now run in a thread pool (as parallel testing enabled) * fix test to expect current thread name * fix another test parallelization thread name changes * jul logging test needs fixing for thread name checking * add changelog entry * add aws lambda explicit activation --- CHANGELOG.asciidoc | 1 + .../co/elastic/apm/attach/AgentAttacher.java | 3 + apm-agent-attach/pom.xml | 10 + .../apm/attach/ElasticApmAttacher.java | 3 + .../ExampleSelfAttachAppWithProvidedJar.java | 41 ++ .../agent/configuration/ActivationMethod.java | 39 ++ .../configuration/CoreConfiguration.java | 13 + .../apm/agent/impl/metadata/Agent.java | 62 ++ .../report/serialize/DslJsonSerializer.java | 1 + .../ActivationTestExampleApp.java | 27 + .../agent/configuration/ActivationTypeIT.java | 529 ++++++++++++++++++ .../logging/LoggingConfigurationTest.java | 3 +- .../test/resources/junit-platform.properties | 1 + .../apm/agent/jul/JulInstrumentationTest.java | 5 + .../loginstr/LoggingInstrumentationTest.java | 14 +- .../src/main/assembly/elastic-apm-handler | 1 + 16 files changed, 750 insertions(+), 3 deletions(-) create mode 100644 apm-agent-attach/src/test/java/co/elastic/apm/attach/ExampleSelfAttachAppWithProvidedJar.java create mode 100644 apm-agent-common/src/main/java/co/elastic/apm/agent/configuration/ActivationMethod.java create mode 100644 apm-agent-core/src/test/java/co/elastic/apm/agent/configuration/ActivationTestExampleApp.java create mode 100644 apm-agent-core/src/test/java/co/elastic/apm/agent/configuration/ActivationTypeIT.java create mode 100644 apm-agent-core/src/test/resources/junit-platform.properties diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index a53f312279..4370a1bbcf 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -29,6 +29,7 @@ endif::[] communication - {pull}2996[#2996] * Add the <> config option to suppress async profiler warning messages - {pull}3002[#3002] * Added support for OpenTelemetry metrics - {pull}2968[#2968] +* Added agent.activation_method telemetry - {pull}2926[#2926] [float] ===== Bug fixes diff --git a/apm-agent-attach-cli/src/main/java/co/elastic/apm/attach/AgentAttacher.java b/apm-agent-attach-cli/src/main/java/co/elastic/apm/attach/AgentAttacher.java index 30a7050445..baf74219ab 100644 --- a/apm-agent-attach-cli/src/main/java/co/elastic/apm/attach/AgentAttacher.java +++ b/apm-agent-attach-cli/src/main/java/co/elastic/apm/attach/AgentAttacher.java @@ -270,6 +270,9 @@ private void onJvmMatch(JvmInfo jvmInfo) throws Exception { private boolean attach(JvmInfo jvmInfo) throws Exception { final Map agentArgs = getAgentArgs(jvmInfo); + if (!agentArgs.containsKey("activation_method")) { + agentArgs.put("activation_method", "APM_AGENT_ATTACH_CLI"); + } logger.info("Attaching the Elastic APM agent to {} with arguments {}", jvmInfo, agentArgs); UserRegistry.User user = jvmInfo.getUser(userRegistry); diff --git a/apm-agent-attach/pom.xml b/apm-agent-attach/pom.xml index 6a88638879..118a30d485 100644 --- a/apm-agent-attach/pom.xml +++ b/apm-agent-attach/pom.xml @@ -156,6 +156,16 @@ + + maven-jar-plugin + + + + test-jar + + + + diff --git a/apm-agent-attach/src/main/java/co/elastic/apm/attach/ElasticApmAttacher.java b/apm-agent-attach/src/main/java/co/elastic/apm/attach/ElasticApmAttacher.java index cd9ad209b5..5d686c7323 100644 --- a/apm-agent-attach/src/main/java/co/elastic/apm/attach/ElasticApmAttacher.java +++ b/apm-agent-attach/src/main/java/co/elastic/apm/attach/ElasticApmAttacher.java @@ -149,6 +149,9 @@ public static void attach(String pid, Map configuration) { * @param agentJarFile the agent jar file */ public static void attach(String pid, Map configuration, File agentJarFile) { + if (!configuration.containsKey("activation_method")) { + configuration.put("activation_method", "PROGRAMMATIC_SELF_ATTACH"); + } File tempFile = createTempProperties(configuration, null); String agentArgs = tempFile == null ? null : TEMP_PROPERTIES_FILE_KEY + "=" + tempFile.getAbsolutePath(); diff --git a/apm-agent-attach/src/test/java/co/elastic/apm/attach/ExampleSelfAttachAppWithProvidedJar.java b/apm-agent-attach/src/test/java/co/elastic/apm/attach/ExampleSelfAttachAppWithProvidedJar.java new file mode 100644 index 0000000000..e20c50c617 --- /dev/null +++ b/apm-agent-attach/src/test/java/co/elastic/apm/attach/ExampleSelfAttachAppWithProvidedJar.java @@ -0,0 +1,41 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package co.elastic.apm.attach; + +import java.io.File; +import java.lang.management.ManagementFactory; +import java.util.HashMap; + +/** + * Note this is used for integration testing by the core project, + * so don't delete it without running tests there! (It's here + * to avoid a cyclic dependency in the poms) + */ +public class ExampleSelfAttachAppWithProvidedJar { + + public static void main(String[] args) throws InterruptedException { + // Just sleep for 5 minutes then exit + //long pid = ProcessHandle.current().pid(); //java 9+ + //Use the old hack - doesn't need to be guaranteed all platforms, it's just for testing + String pidHost = ManagementFactory.getRuntimeMXBean().getName(); + long pid = Integer.parseInt(pidHost.substring(0,pidHost.indexOf('@'))); + ElasticApmAttacher.attach(""+pid, new HashMap(), new File(System.getProperty("ElasticApmAgent.jarfile"))); + Thread.sleep(5*60*1000); + } +} diff --git a/apm-agent-common/src/main/java/co/elastic/apm/agent/configuration/ActivationMethod.java b/apm-agent-common/src/main/java/co/elastic/apm/agent/configuration/ActivationMethod.java new file mode 100644 index 0000000000..dc7e0ee5db --- /dev/null +++ b/apm-agent-common/src/main/java/co/elastic/apm/agent/configuration/ActivationMethod.java @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package co.elastic.apm.agent.configuration; + +public enum ActivationMethod { + // Set explicitly by the process starting the agent + K8S_ATTACH, // https://github.com/elastic/apm-mutating-webhook + AWS_LAMBDA_LAYER, // Only if installed by using layers (as other metadata already identifies lambda) + FLEET, // Fleet using 'java -jar apm-agent-attach-cli.jar ...' to attach (directly or through webhook) + APM_AGENT_ATTACH_CLI, // 'java -jar apm-agent-attach-cli.jar ...' used + PROGRAMMATIC_SELF_ATTACH, // ElasticApmAttacher.attach(); + AZURE_FUNCTIONS, //? + + //Inferred + JAVAAGENT_FLAG, // -javaagent:... command-line option used + ENV_ATTACH, // JAVA_TOOL_OPTIONS env var used to specify -javaagent option + + UNKNOWN; + + public String toReferenceString() { + return toString().replace('_', '-').toLowerCase(); + } +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java index 3a70bd1d3e..6fed254f18 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java @@ -809,6 +809,14 @@ public String toSafeString(List value) { .dynamic(true) .buildWithDefault(TraceContinuationStrategy.CONTINUE); + private final ConfigurationOption activationMethod = ConfigurationOption.enumOption(ActivationMethod.class) + .key("activation_method") + .configurationCategory(CORE_CATEGORY) + .tags("internal") + .description("telling the agent what activated it, used for telemetry and should not be set unless supported by ActivationMethod") + .dynamic(true) + .buildWithDefault(ActivationMethod.UNKNOWN); + public boolean isEnabled() { return enabled.get(); } @@ -1077,6 +1085,10 @@ public TraceContinuationStrategy getTraceContinuationStrategy() { return traceContinuationStrategy.get(); } + public ActivationMethod getActivationMethod() { + return activationMethod.get(); + } + public enum EventType { /** * Request bodies will never be reported @@ -1119,4 +1131,5 @@ public String toString() { return name().toLowerCase(); } } + } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/Agent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/Agent.java index ad92e4c573..a420cf4117 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/Agent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/Agent.java @@ -19,6 +19,14 @@ package co.elastic.apm.agent.impl.metadata; +import co.elastic.apm.agent.configuration.ActivationMethod; +import co.elastic.apm.agent.configuration.CoreConfiguration; +import co.elastic.apm.agent.impl.ElasticApmTracer; +import co.elastic.apm.agent.impl.GlobalTracer; +import co.elastic.apm.agent.util.PrivilegedActionUtils; + +import java.lang.management.ManagementFactory; +import java.util.List; import java.util.UUID; /** @@ -43,6 +51,8 @@ public class Agent { */ private final String ephemeralId; + private String activationMethod; + public Agent(String name, String version) { this(name, version, UUID.randomUUID().toString()); } @@ -75,4 +85,56 @@ public String getVersion() { public String getEphemeralId() { return ephemeralId; } + + public String getActivationMethod() { + if (activationMethod == null) { + ElasticApmTracer tracer = GlobalTracer.getTracerImpl(); + ActivationMethod activation = ActivationMethod.UNKNOWN; + if (tracer != null) { + activation = tracer.getConfig(CoreConfiguration.class).getActivationMethod(); + if (activation.equals(ActivationMethod.UNKNOWN)) { + //Need to infer it + String elasticJavaagentOnTheCommandline = getElasticJavaagentOnTheCommandline(); + String javaToolOptions = PrivilegedActionUtils.getEnv("JAVA_TOOL_OPTIONS"); + if (javaToolOptions != null && elasticJavaagentOnTheCommandline != null && javaToolOptions.contains(elasticJavaagentOnTheCommandline)) { + activation = ActivationMethod.ENV_ATTACH; + } else if(elasticJavaagentOnTheCommandline != null) { + activation = ActivationMethod.JAVAAGENT_FLAG; + } + } + } + activationMethod = activation.toReferenceString(); + } + return activationMethod; + } + + private static String getAgentJarFilename() { + String agentLocation = PrivilegedActionUtils.getProtectionDomain(GlobalTracer.class).getCodeSource().getLocation().getFile(); + if (agentLocation != null) { + String agentJarFile = agentLocation.replace('\\', '/'); + if (agentJarFile.contains("/")) { + return agentJarFile.substring(agentLocation.lastIndexOf('/') + 1, agentLocation.length()); + } else { + return agentJarFile; + } + } else { + return null; + } + } + + private static String getElasticJavaagentOnTheCommandline() { + String agentJarFile = getAgentJarFilename(); + if (agentJarFile != null) { + List javaArgs = ManagementFactory.getRuntimeMXBean().getInputArguments(); + if (javaArgs != null) { + //if there is more than one, this will return the first, which is the correct algorithm + for (String javaArg : javaArgs) { + if (javaArg.startsWith("-javaagent:") && javaArg.contains(agentJarFile)) { + return javaArg; + } + } + } + } + return null; + } } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonSerializer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonSerializer.java index 0c5bc6c5bc..b33ee6ef6f 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonSerializer.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonSerializer.java @@ -545,6 +545,7 @@ private static void serializeService(@Nullable String name, @Nullable String ver private static void serializeAgent(final Agent agent, final StringBuilder replaceBuilder, final JsonWriter jw) { writeFieldName("agent", jw); jw.writeByte(JsonWriter.OBJECT_START); + writeField("activation_method", agent.getActivationMethod(), replaceBuilder, jw); writeField("name", agent.getName(), replaceBuilder, jw); writeField("ephemeral_id", agent.getEphemeralId(), replaceBuilder, jw); writeLastField("version", agent.getVersion(), replaceBuilder, jw); diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/configuration/ActivationTestExampleApp.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/configuration/ActivationTestExampleApp.java new file mode 100644 index 0000000000..98fda5a42c --- /dev/null +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/configuration/ActivationTestExampleApp.java @@ -0,0 +1,27 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package co.elastic.apm.agent.configuration; + +public class ActivationTestExampleApp { + + public static void main(String[] args) throws InterruptedException { + // Just sleep for 5 minutes then exit + Thread.sleep(5*60*1000); + } +} diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/configuration/ActivationTypeIT.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/configuration/ActivationTypeIT.java new file mode 100644 index 0000000000..58cd3b2fd8 --- /dev/null +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/configuration/ActivationTypeIT.java @@ -0,0 +1,529 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package co.elastic.apm.agent.configuration; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.lang.ref.Cleaner; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +@Execution(ExecutionMode.CONCURRENT) +public class ActivationTypeIT { + // Activation is about how an external config or process activates the agent, + // so tests here spawn a full JVM with an agent to test the activation method + private static final int TIMEOUT_IN_SECONDS = 200; + + private static String ElasticAgentAttachJarFileLocation; + private static String ElasticAgentAttachTestJarFileLocation; + private static String ElasticAgentAttachCliJarFileLocation; + private static String ElasticAgentJarFileLocation; + private static final Cleaner MockCleaner = Cleaner.create(); + + @BeforeAll + public static void setUp() throws IOException { + ElasticAgentJarFileLocation = getJarPath("elastic-apm-agent", false); + assertThat(ElasticAgentJarFileLocation).isNotNull(); + ElasticAgentAttachJarFileLocation = getJarPath("apm-agent-attach", false); + assertThat(ElasticAgentAttachJarFileLocation).isNotNull(); + ElasticAgentAttachTestJarFileLocation = getJarPath("apm-agent-attach", true); + assertThat(ElasticAgentAttachTestJarFileLocation).isNotNull(); + ElasticAgentAttachCliJarFileLocation = getJarPath("apm-agent-attach-cli", false); + assertThat(ElasticAgentAttachCliJarFileLocation).isNotNull(); + } + + public MockServer startServer() throws IOException { + final MockServer server = new MockServer(); + server.start(); + assertThat(server.waitUntilStarted(500)).isTrue(); + assertThat(server.port()).isGreaterThan(0); + MockCleaner.register(server, () -> server.stop()); + return server; + } + + private static String getJarPath(String project, boolean findTestJar) { + File rootDir = new File(".."); + File projectDir = new File(rootDir, project); + assertThat(projectDir.exists()).isTrue(); + assertThat(projectDir.isDirectory()).isTrue(); + File targetDir = new File(projectDir, "target"); + assertThat(targetDir.exists()).isTrue(); + assertThat(targetDir.isDirectory()).isTrue(); + String jarName = null; + for (String file : targetDir.list()) { + if (file.matches("^"+project+".*"+".jar$") + && !file.contains("-sources") && !file.contains("-slim")) { + if (!findTestJar && file.contains("-tests")) { + continue; + } + if (findTestJar && !file.contains("-tests")) { + continue; + } + File jarNameFile = new File(targetDir, file); + assertThat(jarNameFile.exists()).isTrue(); + assertThat(jarNameFile.isDirectory()).isFalse(); + assertThat(jarNameFile.canRead()).isTrue(); + jarName = jarNameFile.getPath(); + } + } + return jarName; + } + + @Test + public void testSelfAttach() throws Exception { + try (MockServer server = startServer()) { + JvmAgentProcess proc = new JvmAgentProcess(server, "SimpleSelfAttach", + "co.elastic.apm.attach.ExampleSelfAttachAppWithProvidedJar", + "programmatic-self-attach"); + proc.prependToClasspath(ElasticAgentAttachJarFileLocation); + proc.prependToClasspath(ElasticAgentAttachTestJarFileLocation); + proc.addOption("-DElasticApmAgent.jarfile=" + ElasticAgentJarFileLocation); + proc.executeCommand(); + } + } + + @Test + public void testCLIAttach() throws Exception { + try (MockServer server = startServer()) { + JvmAgentProcess proc = new JvmAgentProcess(server, "JavaAgentCLI", + "co.elastic.apm.agent.configuration.ActivationTestExampleApp", + "javaagent-flag"); + proc.addOption("-javaagent:" + ElasticAgentJarFileLocation); + proc.executeCommand(); + } + } + + @Test + public void testEnvAttach() throws Exception { + try (MockServer server = startServer()) { + JvmAgentProcess proc = new JvmAgentProcess(server, "JavaAgentCLIViaToolEnv", + "co.elastic.apm.agent.configuration.ActivationTestExampleApp", + "env-attach"); + proc.addEnv("JAVA_TOOL_OPTIONS", "-javaagent:" + ElasticAgentJarFileLocation); + proc.executeCommand(); + } + } + + @Test + public void testRemoteAttach() throws Exception { + try (MockServer server = startServer()) { + JvmAgentProcess proc = new JvmAgentProcess(server, "SimpleRemoteAttached", + "co.elastic.apm.agent.configuration.ActivationTestExampleApp", + "apm-agent-attach-cli"); + proc.attachRemotely(true); + proc.executeCommand(); + } + } + + @Test + public void testFleetAttach() throws Exception { + try (MockServer server = startServer()) { + JvmAgentProcess proc = new JvmAgentProcess(server, "FleetRemoteAttached", + "co.elastic.apm.agent.configuration.ActivationTestExampleApp", + "fleet"); + proc.attachRemotely(true); + proc.executeCommand(); + } + } + + @Test + public void fakeTestLambdaAttach() throws Exception { + try (MockServer server = startServer()) { + JvmAgentProcess proc = new JvmAgentProcess(server, "FakeLambdaWithEnv", + "co.elastic.apm.agent.configuration.ActivationTestExampleApp", + "aws-lambda-layer"); + proc.addEnv("JAVA_TOOL_OPTIONS", "-javaagent:" + ElasticAgentJarFileLocation); + proc.addEnv("ELASTIC_APM_ACTIVATION_METHOD", "AWS_LAMBDA_LAYER"); + proc.executeCommand(); + } + } + + static class ExternalProcess { + volatile Process child; + boolean debug = true; + + private static void pauseSeconds(int seconds) { + try {Thread.sleep(seconds*1_000L);} catch (InterruptedException e) {} + } + + public void executeCommandInNewThread(ProcessBuilder pb, ActivationHandler handler, String activationMethod, String serviceName) throws IOException, InterruptedException { + ExternalProcess spawnedProcess = new ExternalProcess(); + new Thread(() -> { + try { + spawnedProcess.executeCommandSynchronously(pb); + } catch (Exception e) { + e.printStackTrace(); + } + }).start(); + pauseSeconds(1); + if (serviceName != null) { + ProcessBuilder pbAttach; + if ("fleet".equals(activationMethod)) { + pbAttach = new ProcessBuilder("java", + "-jar", ElasticAgentAttachCliJarFileLocation, + "--include-vmarg", serviceName, + "-C", "activation_method=FLEET"); + } else { + pbAttach = new ProcessBuilder("java", + "-jar", ElasticAgentAttachCliJarFileLocation, + "--include-vmarg", serviceName); + } + executeCommandSynchronously(pbAttach); + } + waitForActivationMethod(handler, TIMEOUT_IN_SECONDS*1000); + assertThat(handler.found()).isTrue(); + terminate(); + spawnedProcess.terminate(); + } + + private static void waitForActivationMethod(ActivationHandler handler, long timeoutInMillis) { + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < timeoutInMillis) { + if (handler.found()) { + return; + } + try {Thread.sleep(5);} catch (InterruptedException e) {} + } + } + + + private void terminate() { + if (child != null) { + if (child.isAlive()) { + child.destroy(); + pauseSeconds(1); + if (child.isAlive()) { + child.destroyForcibly(); + } + } + } + } + + public void executeCommandSynchronously(ProcessBuilder pb) throws IOException { + if (debug) { + System.out.println("Executing command: "+ Arrays.toString(pb.command().toArray())); + } + pb.redirectErrorStream(true); + Process childProcess = pb.start(); + child = childProcess; + + StringBuilder commandOutput = new StringBuilder(); + + boolean isAlive = true; + byte[] buffer = new byte[64 * 1000]; + try (InputStream in = childProcess.getInputStream()) { + //stop trying if the time elapsed exceeds the timeout + while (isAlive) { + while (in.available() > 0) { + int lengthRead = in.read(buffer, 0, buffer.length); + commandOutput.append(new String(buffer, 0, lengthRead)); + if (debug) { + System.out.print(commandOutput); + } + commandOutput.setLength(0); + } + pauseSeconds(1); + //if it's not alive but there is still readable input, then continue reading + isAlive = childProcess.isAlive() || in.available() > 0; + } + } + + //Cleanup as well as I can + boolean exited = false; + try {exited = childProcess.waitFor(3, TimeUnit.SECONDS);}catch (InterruptedException e) {} + if (!exited) { + childProcess.destroy(); + pauseSeconds(1); + if (childProcess.isAlive()) { + childProcess.destroyForcibly(); + } + } + if (debug) { + System.out.print(commandOutput); + } + } + + } + + static class JvmAgentProcess extends ExternalProcess { + static final String Classpath = System.getProperty("java.class.path"); + static final String[] TestAgentParams = {"api_request_size=100b", "report_sync=true", "log_level=DEBUG", "instrument=false"}; + + MockServer apmServer; + List command = new ArrayList<>(); + String serviceName; + String targetClass; + String activationMethod; + Map env; + boolean attachRemotely; + List targetParams = new ArrayList<>(); + + public void attachRemotely(boolean attachRemotely1) { + this.attachRemotely = attachRemotely1; + } + + public JvmAgentProcess(MockServer server, String serviceName1, String targetClass1, String activationMethod1) { + apmServer = server; + serviceName = serviceName1; + targetClass = targetClass1; + activationMethod = activationMethod1; + init(); + } + + public void addEnv(String key, String value) { + if(env == null) { + env = new HashMap<>(); + } + env.put(key, value); + } + + public void prependToClasspath(String location) { + command.set(3, location+System.getProperty("path.separator")+command.get(3)); + } + + public void executeCommand() throws IOException, InterruptedException { + executeCommandInNewThread(buildProcess(), apmServer.getHandler(), + activationMethod, attachRemotely? serviceName : null); + } + + public void init() { + command.clear(); + addOption("java"); + addOption("-Xmx32m"); + addOption("-classpath"); + addOption(Classpath); + addAgentOption("server_url=http://localhost:"+apmServer.port()); + for (String keyEqualsValue : TestAgentParams) { + addAgentOption(keyEqualsValue); + } + addAgentOption("service_name="+serviceName); + apmServer.getHandler().setActivationToWaitFor(serviceName, activationMethod); + } + + public void addAgentOption(String keyEqualsValue) { + command.add("-Delastic.apm."+keyEqualsValue); + } + + public void addOption(String option) { + command.add(option); + } + + public void addTargetParam(String param) { + targetParams.add(param); + } + + private ProcessBuilder buildProcess() { + command.add(targetClass); + for (String param :targetParams) { + command.add(param); + } + ProcessBuilder pb = new ProcessBuilder(command); + if (env != null) { + for (Map.Entry entry : env.entrySet()) { + pb.environment().put(entry.getKey(), entry.getValue()); + } + } + return pb; + } + + } + + static class ActivationHandler { + + private volatile String serviceNameToWaitFor; + private volatile String activationMethodToWaitFor; + private volatile boolean found; + private final ObjectMapper objectMapper = new ObjectMapper(); + + public boolean found() { + return found; + } + + public void setActivationToWaitFor(String serviceNameToWaitFor1, String activationMethodToWaitFor1) { + this.serviceNameToWaitFor = serviceNameToWaitFor1; + this.activationMethodToWaitFor = activationMethodToWaitFor1; + found = false; + } + + public void handle(String line) { + try { + report(line); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + private void report(String line) throws JsonProcessingException { + System.out.println("MockServer line read: "+line); + JsonNode messageRootNode = objectMapper.readTree(line); + JsonNode metadataNode = messageRootNode.get("metadata"); + if (metadataNode != null) { + JsonNode serviceNode = metadataNode.get("service"); + if (serviceNode != null) { + String name = serviceNode.get("name").asText(); + JsonNode agentNode = serviceNode.get("agent"); + if (agentNode != null) { + JsonNode activationNode = agentNode.get("activation_method"); + if(activationNode != null) { + String activationMethod = activationNode.asText(); + if (name.equals(serviceNameToWaitFor) && activationMethod.equals(activationMethodToWaitFor)) { + found = true; + } + } + } + } + } + } + + } + + class MockServer implements AutoCloseable { + + private static final String HTTP_HEADER ="HTTP/1.0 200 OK\nContent-Type: text/html; charset=utf-8\nServer: MockApmServer\n\n"; + + private volatile ServerSocket server; + private volatile boolean keepGoing = true; + private final ActivationHandler handler = new ActivationHandler(); + + + public MockServer() { + } + + public ActivationHandler getHandler() { + return handler; + } + + public void stop() { + keepGoing = false; + try { + if (this.server != null) { + this.server.close(); + } + } catch (IOException e) { + System.out.println("MockApmServer: Unsuccessfully called stop(), stack trace follows, error is:"+e.getLocalizedMessage()); + e.printStackTrace(System.out); + } + } + + public int port() { + if (this.server != null) { + return this.server.getLocalPort(); + } else { + return -1; + } + } + + public boolean waitUntilStarted(long timeoutInMillis) { + long start = System.currentTimeMillis(); + while((System.currentTimeMillis() - start < timeoutInMillis) && server == null) { + try {Thread.sleep(1);} catch (InterruptedException e) {} + } + return server != null; + } + + public void start() throws IOException { + if (this.server != null) { + throw new IOException("MockApmServer: Ooops, you can't start this instance more than once"); + } + new Thread(() -> { + try { + _start(); + } catch (Exception e) { + e.printStackTrace(); + } + }).start(); + } + + private synchronized void _start() throws IOException { + if (this.server != null) { + throw new IOException("MockApmServer: Ooops, you can't start this instance more than once"); + } + this.server = new ServerSocket(0); + System.out.println("MockApmServer: Successfully called start(), now listening for requests on port "+this.server.getLocalPort()); + while(keepGoing) { + try(Socket client = this.server.accept()) { + while(!client.isClosed() && !client.isInputShutdown() && !client.isOutputShutdown()) { + try (BufferedReader clientInput = new BufferedReader(new InputStreamReader(client.getInputStream()))) { + String line = clientInput.readLine(); + if(line == null) { + //hmmm, try again + try {Thread.sleep(10);} catch (InterruptedException e) {} + line = clientInput.readLine(); + if (line == null) { + clientInput.close(); + break; + } + } + if (line.startsWith("GET /exit")) { + keepGoing = false; + } + while ( (line = clientInput.readLine()) != null) { + if (line.strip().startsWith("{")) { + try { + handler.handle(line.strip()); + } catch (Throwable e) { + //ignore, the report() is responsible to have log it + } + } + } + PrintWriter outputToClient = new PrintWriter(client.getOutputStream()); + outputToClient.println(HTTP_HEADER); + outputToClient.println("{}"); + outputToClient.flush(); + outputToClient.close(); + } catch (IOException e) { + if (!e.getMessage().equals("Connection reset")) { + e.printStackTrace(); + } + } + } + } catch (SocketException e) { + //ignore, we exit regardless and stop() at the end of the method + } + } + stop(); + } + + @Override + public void close() throws Exception { + stop(); + } + } +} diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/logging/LoggingConfigurationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/logging/LoggingConfigurationTest.java index 0811579f42..42fb36b746 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/logging/LoggingConfigurationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/logging/LoggingConfigurationTest.java @@ -157,6 +157,7 @@ void loggingLevelChangeTest() throws IOException { @Test void testFileLogging() throws IOException { + String currentThreadName = Thread.currentThread().getName(); agentLogger.info("agent"); configOptionLogger.info("config"); pluginLogger.info("plugin"); @@ -164,7 +165,7 @@ void testFileLogging() throws IOException { assertThat(logJsonLines).hasSize(3); for (JsonNode logJsonLine : logJsonLines) { assertThat(logJsonLine.get("@timestamp")).isNotNull(); - assertThat(logJsonLine.get("process.thread.name").textValue()).isEqualTo("main"); + assertThat(logJsonLine.get("process.thread.name").textValue()).isEqualTo(currentThreadName); assertThat(logJsonLine.get("log.level").textValue()).isEqualTo("INFO"); assertThat(logJsonLine.get("log.logger")).isNotNull(); assertThat(logJsonLine.get("message")).isNotNull(); diff --git a/apm-agent-core/src/test/resources/junit-platform.properties b/apm-agent-core/src/test/resources/junit-platform.properties new file mode 100644 index 0000000000..1d27b78fbb --- /dev/null +++ b/apm-agent-core/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.execution.parallel.enabled=true diff --git a/apm-agent-plugins/apm-logging-plugin/apm-jul-plugin/src/test/java/co/elastic/apm/agent/jul/JulInstrumentationTest.java b/apm-agent-plugins/apm-logging-plugin/apm-jul-plugin/src/test/java/co/elastic/apm/agent/jul/JulInstrumentationTest.java index 554b356cbd..407ed8a218 100644 --- a/apm-agent-plugins/apm-logging-plugin/apm-jul-plugin/src/test/java/co/elastic/apm/agent/jul/JulInstrumentationTest.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-jul-plugin/src/test/java/co/elastic/apm/agent/jul/JulInstrumentationTest.java @@ -40,6 +40,11 @@ protected LoggerFacade createLoggerFacade() { return new JulLoggerFacade(); } + @Override + protected boolean logsThreadName() { + return false; + } + @Override protected void waitForFileRolling() { await().untilAsserted(() -> assertThat(new File(getLogReformattingFilePath()).length()).isEqualTo(0)); diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/LoggingInstrumentationTest.java b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/LoggingInstrumentationTest.java index fb9d21da40..b588182fb5 100644 --- a/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/LoggingInstrumentationTest.java +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/src/test/java/co/elastic/apm/agent/loginstr/LoggingInstrumentationTest.java @@ -125,6 +125,10 @@ public void closeLogger() { protected abstract LoggerFacade createLoggerFacade(); + protected boolean logsThreadName() { + return true; + } + @Test public void testSimpleLogReformatting() throws Exception { setEcsReformattingConfig(LogEcsReformatting.SHADE); @@ -315,8 +319,9 @@ public void testDynamicConfiguration() throws Exception { } private void verifyEcsLogLine(JsonNode ecsLogLineTree) { + String currentThreadName = Thread.currentThread().getName(); assertThat(ecsLogLineTree.get("@timestamp")).isNotNull(); - assertThat(ecsLogLineTree.get("process.thread.name").textValue()).isEqualTo("main"); + assertThat(ecsLogLineTree.get("process.thread.name").textValue()).isEqualTo(currentThreadName); JsonNode logLevel = ecsLogLineTree.get("log.level"); assertThat(logLevel).isNotNull(); boolean isErrorLine = logLevel.textValue().equalsIgnoreCase("error"); @@ -394,7 +399,12 @@ private void verifyEcsFormat(String[] splitRawLogLine, JsonNode ecsLogLineTree) Date rawTimestamp = timestampFormat.parse(splitRawLogLine[0]); Date ecsTimestamp = utcTimestampFormat.parse(ecsLogLineTree.get("@timestamp").textValue()); assertThat(rawTimestamp).isEqualTo(ecsTimestamp); - assertThat(splitRawLogLine[1]).isEqualTo(ecsLogLineTree.get("process.thread.name").textValue()); + if (logsThreadName()) { + // JUL simple formatter doesn't have the capability to log the thread name + // we've faked it with a 'main' in the format, but that no longer works + // with parallelized unit tests (where the thread is a pool thread) + assertThat(splitRawLogLine[1]).isEqualTo(ecsLogLineTree.get("process.thread.name").textValue()); + } JsonNode logLevel = ecsLogLineTree.get("log.level"); assertThat(splitRawLogLine[2]).isEqualTo(logLevel.textValue()); boolean isErrorLine = logLevel.textValue().equalsIgnoreCase("error"); diff --git a/elastic-apm-agent/src/main/assembly/elastic-apm-handler b/elastic-apm-agent/src/main/assembly/elastic-apm-handler index 1cd10824d5..0493366747 100644 --- a/elastic-apm-agent/src/main/assembly/elastic-apm-handler +++ b/elastic-apm-agent/src/main/assembly/elastic-apm-handler @@ -6,6 +6,7 @@ export ELASTIC_APM_AWS_LAMBDA_HANDLER="${_HANDLER}" export ELASTIC_APM_METRICS_INTERVAL="0s" export ELASTIC_APM_CENTRAL_CONFIG="false" export ELASTIC_APM_CLOUD_PROVIDER="none" +export ELASTIC_APM_ACTIVATION_METHOD="AWS_LAMBDA_LAYER" CMD="$(echo "$@" | sed 's/-Xshare:on/-Xshare:auto/g')" exec $CMD From 557f1c7173db15321cefd310b4c0fec7b685f4dc Mon Sep 17 00:00:00 2001 From: jackshirazi Date: Wed, 22 Feb 2023 17:32:35 +0000 Subject: [PATCH 4/4] make the Agent.activationMethod field immutable (#3032) --- .../apm/agent/impl/metadata/Agent.java | 48 +++++++++++-------- .../agent/impl/metadata/ServiceFactory.java | 2 +- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/Agent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/Agent.java index a420cf4117..81fca62800 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/Agent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/Agent.java @@ -51,16 +51,22 @@ public class Agent { */ private final String ephemeralId; - private String activationMethod; + /** + * The way the agent was activated, e.g."javaagent-flag" + * per the activation method spec + * (Required) + */ + private final String activationMethod; public Agent(String name, String version) { - this(name, version, UUID.randomUUID().toString()); + this(name, version, UUID.randomUUID().toString(), null); } - public Agent(String name, String version, String ephemeralId) { + public Agent(String name, String version, String ephemeralId, CoreConfiguration coreConfiguration) { this.name = name; this.version = version; this.ephemeralId = ephemeralId; + this.activationMethod = getActivationMethod(coreConfiguration); } /** @@ -86,26 +92,30 @@ public String getEphemeralId() { return ephemeralId; } + /** + * Activation method of the Elastic APM agent, e.g."javaagent-flag" + * (Required) + */ public String getActivationMethod() { - if (activationMethod == null) { - ElasticApmTracer tracer = GlobalTracer.getTracerImpl(); - ActivationMethod activation = ActivationMethod.UNKNOWN; - if (tracer != null) { - activation = tracer.getConfig(CoreConfiguration.class).getActivationMethod(); - if (activation.equals(ActivationMethod.UNKNOWN)) { - //Need to infer it - String elasticJavaagentOnTheCommandline = getElasticJavaagentOnTheCommandline(); - String javaToolOptions = PrivilegedActionUtils.getEnv("JAVA_TOOL_OPTIONS"); - if (javaToolOptions != null && elasticJavaagentOnTheCommandline != null && javaToolOptions.contains(elasticJavaagentOnTheCommandline)) { - activation = ActivationMethod.ENV_ATTACH; - } else if(elasticJavaagentOnTheCommandline != null) { - activation = ActivationMethod.JAVAAGENT_FLAG; - } + return activationMethod; + } + + private String getActivationMethod(CoreConfiguration coreConfiguration) { + ActivationMethod activation = ActivationMethod.UNKNOWN; + if (coreConfiguration != null) { + activation = coreConfiguration.getActivationMethod(); + if (activation.equals(ActivationMethod.UNKNOWN)) { + //Need to infer it + String elasticJavaagentOnTheCommandline = getElasticJavaagentOnTheCommandline(); + String javaToolOptions = PrivilegedActionUtils.getEnv("JAVA_TOOL_OPTIONS"); + if (javaToolOptions != null && elasticJavaagentOnTheCommandline != null && javaToolOptions.contains(elasticJavaagentOnTheCommandline)) { + activation = ActivationMethod.ENV_ATTACH; + } else if(elasticJavaagentOnTheCommandline != null) { + activation = ActivationMethod.JAVAAGENT_FLAG; } } - activationMethod = activation.toReferenceString(); } - return activationMethod; + return activation.toReferenceString(); } private static String getAgentJarFilename() { diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/ServiceFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/ServiceFactory.java index 97d68d9329..ee9a853135 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/ServiceFactory.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/ServiceFactory.java @@ -30,7 +30,7 @@ public Service createService(CoreConfiguration coreConfiguration, String ephemer .withName(coreConfiguration.getServiceName()) .withVersion(coreConfiguration.getServiceVersion()) .withEnvironment(coreConfiguration.getEnvironment()) - .withAgent(new Agent("java", VersionUtils.getAgentVersion(), ephemeralId)) + .withAgent(new Agent("java", VersionUtils.getAgentVersion(), ephemeralId, coreConfiguration)) .withRuntime(new RuntimeInfo("Java", System.getProperty("java.version"))) .withLanguage(new Language("Java", System.getProperty("java.version"))) .withNode(new Node(coreConfiguration.getServiceNodeName()));