Skip to content

Commit 1c0f1c6

Browse files
authored
Improve runtime attach configuration (#2283)
* code cleanup * implement new config tests * refactor & test properties config * implement & fix test cases * update changelog * simplify things a bit * minor cleanup * make animal sniffer happy * config sources: use separate factory class * make animal sniffer happy again * add missing file * try to simplify config location * rename test file * fix wording * don't try to attach twice in test app * remove useless failing test + small refactor * fix test * use more concrete type for config to avoid confusion * exclude duplicated cached-lookup-keys * improve + test for duplicated classes * use another way to exclude cached keys dep.
1 parent 721d892 commit 1c0f1c6

File tree

20 files changed

+739
-256
lines changed

20 files changed

+739
-256
lines changed

CHANGELOG.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ endif::[]
2828
* Fixing missing Micrometer metrics in Spring boot due to premature initialization - {pull}2255[#2255]
2929
* Fixing hostname trimming of FQDN too aggressive - {pull}2286[#2286]
3030
* Fixing agent `unknown` version - {pull}2289[#2289]
31-
31+
* Improve runtime attach configuration reliability - {pull}2283[#2283]
3232
3333
[[release-notes-1.x]]
3434
=== Java Agent version 1.x

apm-agent-attach-cli/src/test/java/co/elastic/apm/attach/ElasticApmAttacherTest.java

Lines changed: 0 additions & 98 deletions
This file was deleted.

apm-agent-attach/src/main/java/co/elastic/apm/attach/ElasticApmAttacher.java

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
import co.elastic.apm.agent.common.util.ResourceExtractionUtil;
2222
import net.bytebuddy.agent.ByteBuddyAgent;
2323

24+
import javax.annotation.Nullable;
2425
import java.io.File;
25-
import java.io.FileInputStream;
2626
import java.io.FileOutputStream;
2727
import java.io.IOException;
2828
import java.io.InputStream;
@@ -55,7 +55,7 @@ public class ElasticApmAttacher {
5555
* @throws IllegalStateException if there was a problem while attaching the agent to this VM
5656
*/
5757
public static void attach() {
58-
attach(loadProperties("elasticapm.properties"));
58+
attach(loadPropertiesFromClasspath("elasticapm.properties"));
5959
}
6060

6161
/**
@@ -69,10 +69,10 @@ public static void attach() {
6969
* @since 1.11.0
7070
*/
7171
public static void attach(String propertiesLocation) {
72-
attach(loadProperties(propertiesLocation));
72+
attach(loadPropertiesFromClasspath(propertiesLocation));
7373
}
7474

75-
private static Map<String, String> loadProperties(String propertiesLocation) {
75+
private static Map<String, String> loadPropertiesFromClasspath(String propertiesLocation) {
7676
Map<String, String> propertyMap = new HashMap<>();
7777
final Properties props = new Properties();
7878
try (InputStream resourceStream = ElasticApmAttacher.class.getClassLoader().getResourceAsStream(propertiesLocation)) {
@@ -105,24 +105,21 @@ public static void attach(Map<String, String> configuration) {
105105
attach(ByteBuddyAgent.ProcessProvider.ForCurrentVm.INSTANCE.resolve(), configuration);
106106
}
107107

108-
static File createTempProperties(Map<String, String> configuration) {
108+
/**
109+
* Store configuration to a temporary file
110+
*
111+
* @param configuration agent configuration
112+
* @param folder temporary folder, use {@literal null} to use default
113+
* @return created file if any, {@literal null} if none was created
114+
*/
115+
static File createTempProperties(Map<String, String> configuration, @Nullable File folder) {
109116
File tempFile = null;
110117
if (!configuration.isEmpty()) {
111118
Properties properties = new Properties();
112119
properties.putAll(configuration);
113120

114-
// when an external configuration file is used, we have to load it last to give it higher priority
115-
String externalConfig = configuration.get("config_file");
116-
if (null != externalConfig) {
117-
try (FileInputStream stream = new FileInputStream(externalConfig)) {
118-
properties.load(stream);
119-
} catch (IOException e) {
120-
e.printStackTrace();
121-
}
122-
}
123-
124121
try {
125-
tempFile = File.createTempFile("elstcapm", ".tmp");
122+
tempFile = File.createTempFile("elstcapm", ".tmp", folder);
126123
try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
127124
properties.store(outputStream, null);
128125
}
@@ -148,10 +145,10 @@ public static void attach(String pid, Map<String, String> configuration) {
148145
*
149146
* @param pid the PID of the JVM the agent should be attached on
150147
* @param configuration the agent configuration
151-
* @param agentJarFile
148+
* @param agentJarFile the agent jar file
152149
*/
153150
public static void attach(String pid, Map<String, String> configuration, File agentJarFile) {
154-
File tempFile = createTempProperties(configuration);
151+
File tempFile = createTempProperties(configuration, null);
155152
String agentArgs = tempFile == null ? null : TEMP_PROPERTIES_FILE_KEY + "=" + tempFile.getAbsolutePath();
156153

157154
ByteBuddyAgent.attach(agentJarFile, pid, agentArgs, ElasticAttachmentProvider.get());
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.attach;
20+
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.io.TempDir;
23+
24+
import java.io.File;
25+
import java.io.FileReader;
26+
import java.io.IOException;
27+
import java.util.Map;
28+
import java.util.Properties;
29+
30+
import static org.assertj.core.api.Assertions.assertThat;
31+
32+
class ElasticApmAttacherTest {
33+
34+
@Test
35+
void testCreateTempProperties(@TempDir File tmp) throws Exception {
36+
File tempProperties = ElasticApmAttacher.createTempProperties(Map.of("foo", "bär"), tmp);
37+
assertThat(tempProperties).isNotNull();
38+
39+
assertThat(tempProperties.getParentFile())
40+
.describedAs("property files should be created at root of tmp folder")
41+
.isEqualTo(tmp);
42+
43+
Properties properties = readProperties(tempProperties);
44+
assertThat(properties)
45+
.hasSize(1)
46+
.containsEntry("foo", "bär");
47+
}
48+
49+
@Test
50+
void testCreateEmptyConfigDoesNotCreateFile(@TempDir File tmp) {
51+
File tempProperties = ElasticApmAttacher.createTempProperties(Map.of(), tmp);
52+
assertThat(tempProperties).isNull();
53+
assertThat(tmp.listFiles())
54+
.describedAs("no file should be created in temp folder")
55+
.isEmpty();
56+
}
57+
58+
private Properties readProperties(File propertyFile) throws IOException {
59+
Properties properties = new Properties();
60+
try (FileReader fileReader = new FileReader(propertyFile)) {
61+
properties.load(fileReader);
62+
}
63+
return properties;
64+
}
65+
66+
67+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public static void initialize(@Nullable final String agentArguments, final Instr
134134
ElasticApmAgent.agentJarFile = agentJarFile;
135135

136136
// silently early abort when agent is disabled to minimize the number of loaded classes
137-
List<ConfigurationSource> configSources = ElasticApmTracerBuilder.getConfigSources(agentArguments);
137+
List<ConfigurationSource> configSources = ElasticApmTracerBuilder.getConfigSources(agentArguments, premain);
138138
for (ConfigurationSource configSource : configSources) {
139139
String enabled = configSource.getValue(CoreConfiguration.ENABLED_KEY);
140140
if (enabled != null && !Boolean.parseBoolean(enabled)) {

apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
import co.elastic.apm.agent.configuration.converter.TimeDurationValueConverter;
2626
import co.elastic.apm.agent.configuration.validation.RegexValidator;
2727
import co.elastic.apm.agent.impl.transaction.Span;
28-
import co.elastic.apm.agent.matcher.WildcardMatcher;
29-
import co.elastic.apm.agent.matcher.WildcardMatcherValueConverter;
3028
import co.elastic.apm.agent.matcher.MethodMatcher;
3129
import co.elastic.apm.agent.matcher.MethodMatcherValueConverter;
30+
import co.elastic.apm.agent.matcher.WildcardMatcher;
31+
import co.elastic.apm.agent.matcher.WildcardMatcherValueConverter;
3232
import org.stagemonitor.configuration.ConfigurationOption;
3333
import org.stagemonitor.configuration.ConfigurationOptionProvider;
3434
import org.stagemonitor.configuration.converter.MapValueConverter;
@@ -777,25 +777,24 @@ public TimeDuration getSpanMinDuration() {
777777
* Makes sure to not initialize ConfigurationOption, which would initialize the logger
778778
*/
779779
@Nullable
780-
public static String getConfigFileLocation(List<ConfigurationSource> configurationSources) {
781-
String configFileLocation = DEFAULT_CONFIG_FILE;
782-
for (ConfigurationSource configurationSource : configurationSources) {
783-
String valueFromSource = configurationSource.getValue(CONFIG_FILE);
784-
if (valueFromSource != null) {
785-
configFileLocation = valueFromSource;
780+
public static String getConfigFileLocation(List<ConfigurationSource> configurationSources, boolean premain) {
781+
String configFileLocation = premain ? DEFAULT_CONFIG_FILE : null;
782+
for (ConfigurationSource source : configurationSources) {
783+
String value = source.getValue(CONFIG_FILE);
784+
if (value != null) {
785+
configFileLocation = value;
786786
break;
787787
}
788788
}
789-
if (configFileLocation.contains(AGENT_HOME_PLACEHOLDER)) {
789+
790+
if (configFileLocation != null) {
790791
String agentHome = ElasticApmAgent.getAgentHome();
791792
if (agentHome != null) {
792-
return configFileLocation.replace(AGENT_HOME_PLACEHOLDER, agentHome);
793-
} else {
794-
return null;
793+
configFileLocation = configFileLocation.replace(AGENT_HOME_PLACEHOLDER, agentHome);
795794
}
796-
} else {
797-
return configFileLocation;
798795
}
796+
797+
return configFileLocation;
799798
}
800799

801800
@Nullable

0 commit comments

Comments
 (0)