Skip to content

Commit 0f2612c

Browse files
authored
Add APPLICATIONINSIGHTS_CONFIGURATION_CONTENT env var (#1544)
1 parent 9621df9 commit 0f2612c

File tree

3 files changed

+104
-19
lines changed

3 files changed

+104
-19
lines changed

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/wasbootstrap/configuration/ConfigurationBuilder.java

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
public class ConfigurationBuilder {
4848

4949
private static final String APPLICATIONINSIGHTS_CONFIGURATION_FILE = "APPLICATIONINSIGHTS_CONFIGURATION_FILE";
50+
private static final String APPLICATIONINSIGHTS_CONFIGURATION_CONTENT = "APPLICATIONINSIGHTS_CONFIGURATION_CONTENT";
5051

5152
private static final String APPLICATIONINSIGHTS_CONNECTION_STRING = "APPLICATIONINSIGHTS_CONNECTION_STRING";
5253

@@ -90,6 +91,7 @@ private static void loadLogCaptureEnvVar(Configuration config) {
9091
}
9192
}
9293

94+
// TODO deprecate this
9395
private static void loadJmxMetricsEnvVar(Configuration config) throws IOException {
9496
String jmxMetricsEnvVarJson = getEnvVar(APPLICATIONINSIGHTS_JMX_METRICS);
9597

@@ -150,6 +152,11 @@ private static void loadInstrumentationEnabledEnvVars(Configuration config) {
150152
}
151153

152154
private static Configuration loadConfigurationFile(Path agentJarPath) throws IOException {
155+
String configurationContent = System.getenv(APPLICATIONINSIGHTS_CONFIGURATION_CONTENT);
156+
if (configurationContent != null && !configurationContent.isEmpty()) {
157+
return getConfigurationFromEnvVar(configurationContent, true);
158+
}
159+
153160
if (DiagnosticsHelper.isAnyCodelessAttach()) {
154161
// codeless attach only supports configuration via environment variables (for now at least)
155162
return new Configuration();
@@ -283,6 +290,10 @@ public static class ConfigurationException extends RuntimeException {
283290
ConfigurationException(String message, Exception e) {
284291
super(message, e);
285292
}
293+
294+
ConfigurationException(String message) {
295+
super(message);
296+
}
286297
}
287298

288299
public static class ConfigurationWarnMessage {
@@ -313,23 +324,63 @@ static Configuration getConfigurationFromConfigFile(Path configPath, boolean str
313324
// Try extracting the configuration without failOnUnknown
314325
Configuration configuration = getConfigurationFromConfigFile(configPath, false);
315326
// cannot use logger before loading configuration, so need to store warning messages locally until logger is initialized
316-
configurationWarnMessages.add(new ConfigurationWarnMessage(getJsonEncodingExceptionMessage(configPath.toAbsolutePath().toString(), ex.getMessage())));
327+
configurationWarnMessages.add(new ConfigurationWarnMessage(getJsonEncodingExceptionMessageForFile(configPath, ex.getMessage())));
317328
return configuration;
318329
} else {
319-
throw new FriendlyException(getJsonEncodingExceptionMessage(configPath.toAbsolutePath().toString(), ex.getMessage()),
330+
throw new FriendlyException(getJsonEncodingExceptionMessageForFile(configPath, ex.getMessage()),
320331
"Learn more about configuration options here: https://go.microsoft.com/fwlink/?linkid=2153358");
321332
}
322333
} catch (JsonEncodingException ex) {
323-
throw new FriendlyException(getJsonEncodingExceptionMessage(configPath.toAbsolutePath().toString(), ex.getMessage()),
334+
throw new FriendlyException(getJsonEncodingExceptionMessageForFile(configPath, ex.getMessage()),
324335
"Learn more about configuration options here: https://go.microsoft.com/fwlink/?linkid=2153358");
325336
} catch(Exception e) {
326-
throw new ConfigurationException("Error parsing configuration file: " + configPath.toAbsolutePath().toString(), e);
337+
throw new ConfigurationException("Error parsing configuration from file: " + configPath.toAbsolutePath().toString(), e);
327338
}
328339
}
329340
}
330341

331-
static String getJsonEncodingExceptionMessage(String configPath, String message) {
332-
String defaultMessage = "Application Insights Java agent's configuration file "+ configPath + " has a malformed JSON\n";
342+
static Configuration getConfigurationFromEnvVar(String content, boolean strict) {
343+
Moshi moshi = MoshiBuilderFactory.createBuilderWithAdaptor();
344+
JsonAdapter<Configuration> jsonAdapter = strict ? moshi.adapter(Configuration.class).failOnUnknown() :
345+
moshi.adapter(Configuration.class);
346+
Configuration configuration;
347+
try {
348+
configuration = jsonAdapter.fromJson(content);
349+
} catch(JsonDataException ex) {
350+
if(strict) {
351+
// Try extracting the configuration without failOnUnknown
352+
configuration = getConfigurationFromEnvVar(content, false);
353+
// cannot use logger before loading configuration, so need to store warning messages locally until logger is initialized
354+
configurationWarnMessages.add(new ConfigurationWarnMessage(getJsonEncodingExceptionMessageForEnvVar(ex.getMessage())));
355+
} else {
356+
throw new FriendlyException(getJsonEncodingExceptionMessageForEnvVar(ex.getMessage()),
357+
"Learn more about configuration options here: https://go.microsoft.com/fwlink/?linkid=2153358");
358+
}
359+
} catch (JsonEncodingException ex) {
360+
throw new FriendlyException(getJsonEncodingExceptionMessageForEnvVar(ex.getMessage()),
361+
"Learn more about configuration options here: https://go.microsoft.com/fwlink/?linkid=2153358");
362+
} catch(Exception e) {
363+
throw new ConfigurationException("Error parsing configuration from env var: " + APPLICATIONINSIGHTS_CONFIGURATION_CONTENT, e);
364+
}
365+
366+
if (configuration.connectionString != null) {
367+
throw new ConfigurationException("\"connectionString\" attribute is not supported inside of "
368+
+ APPLICATIONINSIGHTS_CONFIGURATION_CONTENT + ", please use "
369+
+ APPLICATIONINSIGHTS_CONNECTION_STRING + " to specify the connection string");
370+
}
371+
return configuration;
372+
}
373+
374+
static String getJsonEncodingExceptionMessageForFile(Path configPath, String message) {
375+
return getJsonEncodingExceptionMessage("file " + configPath.toAbsolutePath().toString(), message);
376+
}
377+
378+
static String getJsonEncodingExceptionMessageForEnvVar(String message) {
379+
return getJsonEncodingExceptionMessage("env var " + APPLICATIONINSIGHTS_CONFIGURATION_CONTENT, message);
380+
}
381+
382+
static String getJsonEncodingExceptionMessage(String location, String message) {
383+
String defaultMessage = "Application Insights Java agent's configuration "+ location + " has a malformed JSON\n";
333384
if(message == null) {
334385
return defaultMessage;
335386
}
@@ -338,7 +389,7 @@ static String getJsonEncodingExceptionMessage(String configPath, String message)
338389
// Cannot skip unexpected NAME at $.httpProxy
339390
// Removing the 'Cannot Skip' string from the message.
340391
if(message.toLowerCase().contains("cannot skip")) {
341-
return "Application Insights Java agent's configuration file "+ configPath +
392+
return "Application Insights Java agent's configuration "+ location +
342393
" has the following JSON issue: "+message.toLowerCase().replaceAll("cannot skip","") +"\n";
343394
}
344395

@@ -354,7 +405,7 @@ static String getJsonEncodingExceptionMessage(String configPath, String message)
354405
// Use JsonReader.setLenient(true) to accept malformed JSON at path $.selfDiagnostics
355406
int jsonAttributeIndex = message.lastIndexOf("$.");
356407
if(jsonAttributeIndex > 0 && jsonAttributeIndex < message.length() -2) {
357-
return "Application Insights Java agent's configuration file "+ configPath +
408+
return "Application Insights Java agent's configuration "+ location +
358409
" has a malformed JSON at path "+message.substring(jsonAttributeIndex) +"\n";
359410
} else {
360411
return defaultMessage;

agent/agent-tooling/src/test/java/com/microsoft/applicationinsights/agent/internal/wasbootstrap/configuration/ConfigurationBuilderTest.java

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,15 @@ public void testMalformedFaultyJson() throws IOException {
8282
}
8383

8484
public void testGetJsonEncodingExceptionMessage() {
85-
String pathNull = ConfigurationBuilder.getJsonEncodingExceptionMessage("path/to/file",null);
86-
String pathEmpty = ConfigurationBuilder.getJsonEncodingExceptionMessage("path/to/file","");
87-
String pathValid = ConfigurationBuilder.getJsonEncodingExceptionMessage("path/to/file","has a malformed JSON at path $.role.");
88-
String pathInvalidAndNull = ConfigurationBuilder.getJsonEncodingExceptionMessage("path/to/file","has a malformed JSON at path $.null.[0]");
89-
String pathInvalid = ConfigurationBuilder.getJsonEncodingExceptionMessage("path/to/file","has a malformed JSON at path $.");
85+
String pathNull = ConfigurationBuilder.getJsonEncodingExceptionMessage("file path/to/file",null);
86+
String pathEmpty = ConfigurationBuilder.getJsonEncodingExceptionMessage("file path/to/file","");
87+
String pathValid = ConfigurationBuilder.getJsonEncodingExceptionMessage("file path/to/file","has a malformed JSON at path $.role.");
88+
String pathInvalidAndNull = ConfigurationBuilder.getJsonEncodingExceptionMessage("file path/to/file","has a malformed JSON at path $.null.[0]");
89+
String pathInvalid = ConfigurationBuilder.getJsonEncodingExceptionMessage("file path/to/file","has a malformed JSON at path $.");
9090
assertEquals("Application Insights Java agent's configuration file path/to/file has a malformed JSON\n",pathNull);
9191
assertEquals("Application Insights Java agent's configuration file path/to/file has a malformed JSON\n",pathEmpty);
9292
assertEquals("Application Insights Java agent's configuration file path/to/file has a malformed JSON at path $.role.\n",pathValid);
9393
assertEquals("Application Insights Java agent's configuration file path/to/file has a malformed JSON\n",pathInvalid);
9494
assertEquals("Application Insights Java agent's configuration file path/to/file has a malformed JSON\n",pathInvalidAndNull);
9595
}
96-
97-
98-
99-
100-
101-
10296
}

agent/agent-tooling/src/test/java/com/microsoft/applicationinsights/agent/internal/wasbootstrap/configuration/ConfigurationTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.io.IOException;
44
import java.lang.reflect.Type;
5+
import java.nio.file.Paths;
56
import java.util.List;
67

78
import com.google.common.base.Charsets;
@@ -13,6 +14,7 @@
1314
import com.microsoft.applicationinsights.agent.internal.wasbootstrap.configuration.Configuration.ProcessorConfig;
1415
import com.microsoft.applicationinsights.agent.internal.wasbootstrap.configuration.Configuration.ProcessorMatchType;
1516
import com.microsoft.applicationinsights.agent.internal.wasbootstrap.configuration.Configuration.ProcessorType;
17+
import com.microsoft.applicationinsights.agent.internal.wasbootstrap.configuration.ConfigurationBuilder.ConfigurationException;
1618
import com.squareup.moshi.JsonAdapter;
1719
import com.squareup.moshi.JsonDataException;
1820
import com.squareup.moshi.JsonReader;
@@ -68,6 +70,44 @@ public void shouldParse() throws IOException {
6870
assertEquals(2, configuration.selfDiagnostics.file.maxHistory);
6971
}
7072

73+
@Test
74+
public void shouldParseFromEnvVar() throws IOException {
75+
String jmxMetricsJson = "[{" +
76+
"\"objectName\":\"java.lang:type=ClassLoading\"," +
77+
"\"attribute\":\"LoadedClassCount\"," +
78+
"\"display\":\"Loaded Class Count from EnvVar\"}," +
79+
"{\"objectName\":\"java.lang:type=MemoryPool," +
80+
"name=Code Cache\",\"attribute\":\"Usage.used\"," +
81+
"\"display\":\"Code Cache Used from EnvVar\"}]";
82+
String contentJson = "{\"jmxMetrics\": " + jmxMetricsJson + "," +
83+
"\"role\":{" +
84+
"\"name\":\"testrole\"" +
85+
"}}";
86+
envVars.set("APPLICATIONINSIGHTS_CONFIGURATION_CONTENT", contentJson);
87+
envVars.set("APPLICATIONINSIGHTS_CONNECTION_STRING", "InstrumentationKey=11111111-1111-1111-1111-111111111111");
88+
89+
Configuration configuration = ConfigurationBuilder.create(Paths.get("."));
90+
91+
assertEquals("InstrumentationKey=11111111-1111-1111-1111-111111111111", configuration.connectionString);
92+
assertEquals("testrole", configuration.role.name);
93+
94+
List<JmxMetric> jmxMetrics = parseJmxMetricsJson(jmxMetricsJson);
95+
assertEquals(2, jmxMetrics.size());
96+
assertEquals(3, configuration.jmxMetrics.size());
97+
assertEquals(jmxMetrics.get(0).name, configuration.jmxMetrics.get(0).name); // class count is overridden by the env var
98+
assertEquals(jmxMetrics.get(1).name, configuration.jmxMetrics.get(1).name); // code cache is overridden by the env var
99+
assertEquals(configuration.jmxMetrics.get(2).name, "Current Thread Count");
100+
}
101+
102+
@Test(expected = ConfigurationException.class)
103+
public void shouldThrowFromEnvVarIfEmbeddedConnectionString() throws IOException {
104+
String contentJson = "{\"connectionString\":\"InstrumentationKey=55555555-5555-5555-5555-555555555555\"," +
105+
"\"role\":{\"name\":\"testrole\"}}";
106+
envVars.set("APPLICATIONINSIGHTS_CONFIGURATION_CONTENT", contentJson);
107+
108+
ConfigurationBuilder.create(Paths.get("."));
109+
}
110+
71111
@Test
72112
public void shouldParseProcessorConfiguration() throws IOException {
73113

0 commit comments

Comments
 (0)