From ca08d1a28bdacaaded995bf541de4e1dbb30aae9 Mon Sep 17 00:00:00 2001 From: ScienJus Date: Wed, 7 Nov 2018 23:15:36 +0800 Subject: [PATCH] Support for overriding config with agent options (#1887) * Support for overriding config with agent options * Test the separator of options * support quotes in agent options * revert imports change * add more tests * clear properties after each test * add document * fix code style --- .../core/conf/SnifferConfigInitializer.java | 55 +++++++++++++++- .../conf/SnifferConfigInitializerTest.java | 65 +++++++++++++++++-- .../skywalking/apm/agent/SkyWalkingAgent.java | 2 +- .../java-agent/Setting-override.md | 45 ++++++++++--- 4 files changed, 152 insertions(+), 15 deletions(-) diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/SnifferConfigInitializer.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/SnifferConfigInitializer.java index d33d304717..325557140a 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/SnifferConfigInitializer.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/SnifferConfigInitializer.java @@ -24,7 +24,9 @@ import java.io.FileNotFoundException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.skywalking.apm.agent.core.boot.AgentPackageNotFoundException; @@ -56,7 +58,7 @@ public class SnifferConfigInitializer { *

* At the end, `agent.application_code` and `collector.servers` must be not blank. */ - public static void initialize() throws ConfigNotFoundException, AgentPackageNotFoundException { + public static void initialize(String agentOptions) throws ConfigNotFoundException, AgentPackageNotFoundException { InputStreamReader configFileStream; try { @@ -74,6 +76,17 @@ public class SnifferConfigInitializer { logger.error(e, "Failed to read the system env."); } + if (!StringUtil.isEmpty(agentOptions)) { + try { + agentOptions = agentOptions.trim(); + logger.info("Agent options is {}.", agentOptions); + + overrideConfigByAgentOptions(agentOptions); + } catch (Exception e) { + logger.error(e, "Failed to parse the agent options, val is {}.", agentOptions); + } + } + if (StringUtil.isEmpty(Config.Agent.APPLICATION_CODE)) { throw new ExceptionInInitializerError("`agent.application_code` is missing."); } @@ -84,6 +97,46 @@ public class SnifferConfigInitializer { IS_INIT_COMPLETED = true; } + private static void overrideConfigByAgentOptions(String agentOptions) throws IllegalAccessException { + Properties properties = new Properties(); + for (List terms : parseAgentOptions(agentOptions)) { + if (terms.size() != 2) { + throw new IllegalArgumentException("[" + terms + "] is not a key-value pair."); + } + properties.put(terms.get(0), terms.get(1)); + } + if (!properties.isEmpty()) { + ConfigInitializer.initialize(properties, Config.class); + } + } + + private static List> parseAgentOptions(String agentOptions) { + List> options = new ArrayList>(); + List terms = new ArrayList(); + boolean isInQuotes = false; + StringBuilder currentTerm = new StringBuilder(); + for (char c : agentOptions.toCharArray()) { + if (c == '\'' || c == '"') { + isInQuotes = !isInQuotes; + } else if (c == '=' && !isInQuotes) { // key-value pair uses '=' as separator + terms.add(currentTerm.toString()); + currentTerm = new StringBuilder(); + } else if (c == ',' && !isInQuotes) { // multiple options use ',' as separator + terms.add(currentTerm.toString()); + currentTerm = new StringBuilder(); + + options.add(terms); + terms = new ArrayList(); + } else { + currentTerm.append(c); + } + } + // add the last term and option without separator + terms.add(currentTerm.toString()); + options.add(terms); + return options; + } + public static boolean isInitCompleted() { return IS_INIT_COMPLETED; } diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/conf/SnifferConfigInitializerTest.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/conf/SnifferConfigInitializerTest.java index 814c3cd3bc..edd81c97bc 100644 --- a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/conf/SnifferConfigInitializerTest.java +++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/conf/SnifferConfigInitializerTest.java @@ -21,11 +21,15 @@ package org.apache.skywalking.apm.agent.core.conf; import org.apache.skywalking.apm.agent.core.boot.AgentPackageNotFoundException; import org.apache.skywalking.apm.agent.core.logging.core.LogLevel; -import org.junit.AfterClass; +import org.junit.After; import org.junit.Test; +import java.util.Iterator; +import java.util.Map; + import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; public class SnifferConfigInitializerTest { @@ -34,14 +38,67 @@ public class SnifferConfigInitializerTest { System.setProperty("skywalking.agent.application_code", "testApp"); System.setProperty("skywalking.collector.backend_service", "127.0.0.1:8090"); System.setProperty("skywalking.logging.level", "info"); - SnifferConfigInitializer.initialize(); + SnifferConfigInitializer.initialize(null); assertThat(Config.Agent.APPLICATION_CODE, is("testApp")); assertThat(Config.Collector.BACKEND_SERVICE, is("127.0.0.1:8090")); assertThat(Config.Logging.LEVEL, is(LogLevel.INFO)); } - @AfterClass - public static void clear() { + @Test + public void testLoadConfigFromAgentOptions() throws AgentPackageNotFoundException, ConfigNotFoundException { + String agentOptions = "agent.application_code=testApp,collector.backend_service=127.0.0.1:8090,logging.level=info"; + SnifferConfigInitializer.initialize(agentOptions); + assertThat(Config.Agent.APPLICATION_CODE, is("testApp")); + assertThat(Config.Collector.BACKEND_SERVICE, is("127.0.0.1:8090")); + assertThat(Config.Logging.LEVEL, is(LogLevel.INFO)); + } + + @Test + public void testConfigOverriding() throws AgentPackageNotFoundException, ConfigNotFoundException { + System.setProperty("skywalking.agent.application_code", "testAppFromSystem"); + System.setProperty("skywalking.collector.backend_service", "127.0.0.1:8090"); + String agentOptions = "agent.application_code=testAppFromAgentOptions,logging.level=debug"; + SnifferConfigInitializer.initialize(agentOptions); + assertThat(Config.Agent.APPLICATION_CODE, is("testAppFromAgentOptions")); + assertThat(Config.Collector.BACKEND_SERVICE, is("127.0.0.1:8090")); + assertThat(Config.Logging.LEVEL, is(LogLevel.DEBUG)); + } + + @Test + public void testAgentOptionsSeparator() throws AgentPackageNotFoundException, ConfigNotFoundException { + System.setProperty("skywalking.agent.application_code", "testApp"); + System.setProperty("skywalking.collector.backend_service", "127.0.0.1:8090"); + String agentOptions = "agent.ignore_suffix='.jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg'"; + SnifferConfigInitializer.initialize(agentOptions); + assertThat(Config.Agent.IGNORE_SUFFIX, is(".jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg")); + } + + @Test + public void testAgentOptionsParser() throws AgentPackageNotFoundException, ConfigNotFoundException { + System.setProperty("skywalking.collector.backend_service", "127.0.0.1:8090"); + String agentOptions = "agent.application_code=test=abc"; + try { + SnifferConfigInitializer.initialize(agentOptions); + fail("test=abc without quotes is not a valid value"); + } catch (ExceptionInInitializerError e) { + // ignore + } + agentOptions = "agent.application_code='test=abc'"; + SnifferConfigInitializer.initialize(agentOptions); + assertThat(Config.Agent.APPLICATION_CODE, is("test=abc")); + } + + @After + public void clear() { + Iterator> it = System.getProperties().entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + if (entry.getKey().toString().startsWith("skywalking.")) { + it.remove(); + } + } + + Config.Agent.APPLICATION_CODE = ""; Config.Logging.LEVEL = LogLevel.DEBUG; } } diff --git a/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java b/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java index c6ca2ca20c..fd3b4a0c41 100644 --- a/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java +++ b/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java @@ -61,7 +61,7 @@ public class SkyWalkingAgent { public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException { final PluginFinder pluginFinder; try { - SnifferConfigInitializer.initialize(); + SnifferConfigInitializer.initialize(agentArgs); pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins()); diff --git a/docs/en/setup/service-agent/java-agent/Setting-override.md b/docs/en/setup/service-agent/java-agent/Setting-override.md index ddfc0d9357..5abbdcf61f 100644 --- a/docs/en/setup/service-agent/java-agent/Setting-override.md +++ b/docs/en/setup/service-agent/java-agent/Setting-override.md @@ -1,22 +1,49 @@ # Setting Override -In default, SkyWalking provide `agent.config` for agent +In default, SkyWalking provide `agent.config` for agent. -Setting override means end user can override the settings in these config file, through using system properties. - +Setting override means end user can override the settings in these config file, through using system properties or agent options. + + +## System properties Use `skywalking.` + key in config file as system properties key, to override the value. - Why need this prefix? - The agent system properites and env share with target application, this prefix can avoid variable conflict. - -- Override priority - - System.Properties(-D) > Config file + The agent system properties and env share with target application, this prefix can avoid variable conflict. - Example Override `agent.application_code` by this. ``` -Dskywalking.agent.application_code=31200 -``` \ No newline at end of file +``` + +## Agent options + +Add the properties after the agent path in JVM arguments. + +``` +-javaagent:/path/to/skywalking-agent.jar=[option1]=[value1],[option2]=[value2] +``` + +- Example + + Override `agent.application_code` and `logging.level` by this. + +``` +-javaagent:/path/to/skywalking-agent.jar=agent.application_code=31200,logging.level=debug +``` + +- Special characters + + If a separator(`,` or `=`) in the option or value, it should be wrapped in quotes. + +``` +-javaagent:/path/to/skywalking-agent.jar=agent.ignore_suffix='.jpg,.jpeg' +``` + + +## Override priority + +Agent Options > System.Properties(-D) > Config file