From 010bf099bfae1d118c4b5bdb10aa1e4b82f1bb53 Mon Sep 17 00:00:00 2001 From: enoch Date: Mon, 11 Sep 2023 18:42:32 +0800 Subject: [PATCH] feat: Compatible with opentelemetry-java env configuration. --- .../easeagent/config/ConfigFactory.java | 97 ++++++++++++----- .../easeagent/config/ConfigFactoryTest.java | 54 +++++++++- .../src/test/resources/user-spec.properties | 2 + .../src/test/resources/user-spec2.properties | 2 + .../megaease/easeagent/core/Bootstrap.java | 16 +-- plugin-api/pom.xml | 4 + .../sdk/resources/EaseAgentResource.java | 5 +- .../sdk/resources/OtelSdkConfigs.java | 102 ++++++++++++++++++ .../sdk/resources/OtelSdkConfigsTest.java | 45 ++++++++ 9 files changed, 284 insertions(+), 43 deletions(-) create mode 100644 config/src/test/resources/user-spec.properties create mode 100644 config/src/test/resources/user-spec2.properties create mode 100644 plugin-api/src/main/java/io/opentelemetry/sdk/resources/OtelSdkConfigs.java create mode 100644 plugin-api/src/test/java/io/opentelemetry/sdk/resources/OtelSdkConfigsTest.java diff --git a/config/src/main/java/com/megaease/easeagent/config/ConfigFactory.java b/config/src/main/java/com/megaease/easeagent/config/ConfigFactory.java index 05fab1f78..b6a013894 100644 --- a/config/src/main/java/com/megaease/easeagent/config/ConfigFactory.java +++ b/config/src/main/java/com/megaease/easeagent/config/ConfigFactory.java @@ -17,54 +17,67 @@ package com.megaease.easeagent.config; +import com.google.common.base.CaseFormat; +import com.google.common.base.Strings; import com.megaease.easeagent.log4j2.Logger; import com.megaease.easeagent.log4j2.LoggerFactory; +import com.megaease.easeagent.plugin.utils.ImmutableMap; import com.megaease.easeagent.plugin.utils.SystemEnv; import com.megaease.easeagent.plugin.utils.common.JsonUtil; import com.megaease.easeagent.plugin.utils.common.StringUtils; +import io.opentelemetry.sdk.resources.OtelSdkConfigs; import java.io.File; -import java.util.*; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; public class ConfigFactory { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigFactory.class); private static final String CONFIG_PROP_FILE = "agent.properties"; private static final String CONFIG_YAML_FILE = "agent.yaml"; - private static final String AGENT_SERVICE_NAME = "easeagent.name"; - private static final String AGENT_SYSTEM_NAME = "easeagent.system"; + public static final String AGENT_CONFIG_PATH = "config.path"; - private static final String AGENT_SERVER_PORT_KEY = "easeagent.server.port"; - private static final String AGENT_SERVER_ENABLED_KEY = "easeagent.server.enabled"; + public static final String AGENT_SERVICE = "name"; + public static final String AGENT_SYSTEM = "system"; + + public static final String AGENT_SERVER_PORT = "easeagent.server.port"; + public static final String AGENT_SERVER_ENABLED = "easeagent.server.enabled"; public static final String EASEAGENT_ENV_CONFIG = "EASEAGENT_ENV_CONFIG"; - private static final List subEnvKeys = new LinkedList<>(); - private static final List envKeys = new LinkedList<>(); + private static final Map AGENT_CONFIG_KEYS_TO_PROPS = + ImmutableMap.builder() + .put("easeagent.config.path", AGENT_CONFIG_PATH) + .put("easeagent.name", AGENT_SERVICE) + .put("easeagent.system", AGENT_SYSTEM) + .put("easeagent.server.port", AGENT_SERVER_PORT) + .put("easeagent.server.enabled", AGENT_SERVER_ENABLED) + .build(); + + // OTEL_SERVICE_NAME=xxx + private static final Map AGENT_ENV_KEY_TO_PROPS = new HashMap<>(); + static { - subEnvKeys.add(AGENT_SERVICE_NAME); - subEnvKeys.add(AGENT_SYSTEM_NAME); - envKeys.add(AGENT_SERVER_ENABLED_KEY); - envKeys.add(AGENT_SERVER_PORT_KEY); + for (Map.Entry entry : AGENT_CONFIG_KEYS_TO_PROPS.entrySet()) { + // lower.hyphen -> UPPER_UNDERSCORE + AGENT_ENV_KEY_TO_PROPS.put( + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, entry.getKey().replace('.', '-')), + entry.getValue() + ); + } } + /** + * update config value from environment variables and java properties + *

+ * java properties > environment variables > env:EASEAGENT_ENV_CONFIG={} > default + */ static Map updateEnvCfg() { Map envCfg = new TreeMap<>(); - for (String key : subEnvKeys) { - String value = System.getProperty(key); - if (!StringUtils.isEmpty(value)) { - envCfg.put(key.substring("easeagent.".length()), value); - } - } - for (String key : envKeys) { - String value = System.getProperty(key); - if (!StringUtils.isEmpty(value)) { - envCfg.put(key, value); - } - } - String configEnv = SystemEnv.get(EASEAGENT_ENV_CONFIG); if (StringUtils.isNotEmpty(configEnv)) { Map map = JsonUtil.toMap(configEnv); @@ -77,12 +90,45 @@ static Map updateEnvCfg() { envCfg.putAll(strMap); } + // override by environment variables, eg: export EASEAGENT_NAME=xxx + for (Map.Entry entry : AGENT_ENV_KEY_TO_PROPS.entrySet()) { + String value = SystemEnv.get(entry.getKey()); + if (!StringUtils.isEmpty(value)) { + envCfg.put(entry.getValue(), value); + } + } + + // override by java properties; eg: java -Deaseagent.name=xxx + for (Map.Entry entry : AGENT_CONFIG_KEYS_TO_PROPS.entrySet()) { + String value = System.getProperty(entry.getKey()); + if (!StringUtils.isEmpty(value)) { + envCfg.put(entry.getValue(), value); + } + } + return envCfg; } private ConfigFactory() { } + /** + * load config from environment variables and java properties and default config file. + *

+ * user special config: + * -Deaseagent.config.path=/easeagent/agent.properties || export EASEAGENT_CONFIG_PATH=/easeagent/agent.properties + * or OTEL config format + * -Dotel.javaagent.configuration-file=/easeagent/agent.properties || export OTEL_JAVAAGENT_CONFIGURATION_FILE=/easeagent/agent.properties + */ + public static GlobalConfigs loadConfigs(ClassLoader loader) { + Map envCfg = updateEnvCfg(); + String configFile = envCfg.get(AGENT_CONFIG_PATH); + if (Strings.isNullOrEmpty(configFile)) { + envCfg = OtelSdkConfigs.updateEnvCfg(); + configFile = envCfg.get(AGENT_CONFIG_PATH); + } + return loadConfigs(configFile, loader); + } public static GlobalConfigs loadConfigs(String pathname, ClassLoader loader) { // load property configuration file if exist @@ -99,6 +145,9 @@ public static GlobalConfigs loadConfigs(String pathname, ClassLoader loader) { configs.mergeConfigs(configsFromOuterFile); } + // override by opentelemetry sdk env config + configs.updateConfigsNotNotify(OtelSdkConfigs.updateEnvCfg()); + // check environment cfg override configs.updateConfigsNotNotify(updateEnvCfg()); diff --git a/config/src/test/java/com/megaease/easeagent/config/ConfigFactoryTest.java b/config/src/test/java/com/megaease/easeagent/config/ConfigFactoryTest.java index cee034acf..be292f19a 100644 --- a/config/src/test/java/com/megaease/easeagent/config/ConfigFactoryTest.java +++ b/config/src/test/java/com/megaease/easeagent/config/ConfigFactoryTest.java @@ -21,21 +21,67 @@ import com.megaease.easeagent.plugin.utils.SystemEnv; import org.junit.Test; +import static com.megaease.easeagent.config.ConfigFactory.AGENT_SERVICE; +import static com.megaease.easeagent.config.ConfigFactory.AGENT_SYSTEM; import static org.junit.Assert.assertEquals; public class ConfigFactoryTest { @Test public void test_yaml() { Configs config = ConfigFactory.loadConfigs(null, this.getClass().getClassLoader()); - assertEquals("test-service", config.getString("name")); - assertEquals("demo-system", config.getString("system")); + assertEquals("test-service", config.getString(AGENT_SERVICE)); + assertEquals("demo-system", config.getString(AGENT_SYSTEM)); } @Test public void test_env() { SystemEnv.set(ConfigFactory.EASEAGENT_ENV_CONFIG, "{\"name\":\"env-service\"}"); Configs config = ConfigFactory.loadConfigs(null, this.getClass().getClassLoader()); - assertEquals("env-service", config.getString("name")); - assertEquals("demo-system", config.getString("system")); + assertEquals("env-service", config.getString(AGENT_SERVICE)); + assertEquals("demo-system", config.getString(AGENT_SYSTEM)); + } + + @Test + public void test_loadConfigs() { + SystemEnv.set("EASEAGENT_NAME", "service1"); + SystemEnv.set("EASEAGENT_SYSTEM", "system1"); + Configs config = ConfigFactory.loadConfigs(this.getClass().getClassLoader()); + assertEquals("service1", config.getString(AGENT_SERVICE)); + assertEquals("system1", config.getString(AGENT_SYSTEM)); + + // override by jvm properties + System.setProperty("easeagent.name", "service2"); + System.setProperty("easeagent.system", "system2"); + config = ConfigFactory.loadConfigs(this.getClass().getClassLoader()); + assertEquals("service2", config.getString(AGENT_SERVICE)); + assertEquals("system2", config.getString(AGENT_SYSTEM)); + } + + @Test + public void test_loadConfigsFromUserSpec() { + SystemEnv.set("EASEAGENT_CONFIG_PATH", "src/test/resources/user-spec.properties"); + Configs config = ConfigFactory.loadConfigs(this.getClass().getClassLoader()); + assertEquals("user-spec", config.getString(AGENT_SERVICE)); + assertEquals("system-spec", config.getString(AGENT_SYSTEM)); + + // override by jvm properties + System.setProperty("easeagent.config.path", "src/test/resources/user-spec2.properties"); + config = ConfigFactory.loadConfigs(this.getClass().getClassLoader()); + assertEquals("user-spec2", config.getString(AGENT_SERVICE)); + assertEquals("system-spec2", config.getString(AGENT_SYSTEM)); + } + + @Test + public void test_loadConfigsFromOtelUserSpec() { + SystemEnv.set("OTEL_JAVAAGENT_CONFIGURATION_FILE", "src/test/resources/user-spec.properties"); + Configs config = ConfigFactory.loadConfigs(this.getClass().getClassLoader()); + assertEquals("user-spec", config.getString(AGENT_SERVICE)); + assertEquals("system-spec", config.getString(AGENT_SYSTEM)); + + // override by jvm properties + System.setProperty("otel.javaagent.configuration-file", "src/test/resources/user-spec2.properties"); + config = ConfigFactory.loadConfigs(this.getClass().getClassLoader()); + assertEquals("user-spec2", config.getString(AGENT_SERVICE)); + assertEquals("system-spec2", config.getString(AGENT_SYSTEM)); } } diff --git a/config/src/test/resources/user-spec.properties b/config/src/test/resources/user-spec.properties new file mode 100644 index 000000000..f39e6ff84 --- /dev/null +++ b/config/src/test/resources/user-spec.properties @@ -0,0 +1,2 @@ +name=user-spec +system=system-spec diff --git a/config/src/test/resources/user-spec2.properties b/config/src/test/resources/user-spec2.properties new file mode 100644 index 000000000..07de5a043 --- /dev/null +++ b/config/src/test/resources/user-spec2.properties @@ -0,0 +1,2 @@ +name=user-spec2 +system=system-spec2 diff --git a/core/src/main/java/com/megaease/easeagent/core/Bootstrap.java b/core/src/main/java/com/megaease/easeagent/core/Bootstrap.java index bce58089c..21b34fd92 100644 --- a/core/src/main/java/com/megaease/easeagent/core/Bootstrap.java +++ b/core/src/main/java/com/megaease/easeagent/core/Bootstrap.java @@ -47,7 +47,6 @@ import net.bytebuddy.dynamic.loading.ClassInjector; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.utility.JavaModule; -import org.apache.commons.lang3.StringUtils; import javax.management.MBeanServer; import javax.management.ObjectName; @@ -64,9 +63,8 @@ public class Bootstrap { private static final Logger LOGGER = LoggerFactory.getLogger(Bootstrap.class); - private static final String AGENT_SERVER_PORT_KEY = "easeagent.server.port"; - private static final String AGENT_CONFIG_PATH = "easeagent.config.path"; - private static final String AGENT_SERVER_ENABLED_KEY = "easeagent.server.enabled"; + private static final String AGENT_SERVER_PORT_KEY = ConfigFactory.AGENT_SERVER_PORT; + private static final String AGENT_SERVER_ENABLED_KEY = ConfigFactory.AGENT_SERVER_ENABLED; private static final String AGENT_MIDDLEWARE_UPDATE = "easeagent.middleware.update"; @@ -90,16 +88,10 @@ public static void start(String args, Instrumentation inst, String javaAgentJarP LOGGER.debug("Injected class: {}", bootstrapClassSet); } - // initiate configuration - String configPath = System.getProperty(AGENT_CONFIG_PATH); - if (StringUtils.isEmpty(configPath)) { - configPath = args; - } - ClassLoader classLoader = Bootstrap.class.getClassLoader(); final AgentInfo agentInfo = AgentInfoFactory.loadAgentInfo(classLoader); EaseAgent.agentInfo = agentInfo; - final GlobalConfigs conf = ConfigFactory.loadConfigs(configPath, classLoader); + final GlobalConfigs conf = ConfigFactory.loadConfigs(classLoader); wrapConfig(conf); // loader check @@ -141,8 +133,6 @@ private static void initHttpServer(Configs conf) { if (port == null) { port = DEF_AGENT_SERVER_PORT; } - String portStr = System.getProperty(AGENT_SERVER_PORT_KEY, String.valueOf(port)); - port = Integer.parseInt(portStr); AgentHttpServer agentHttpServer = new AgentHttpServer(port); diff --git a/plugin-api/pom.xml b/plugin-api/pom.xml index bef7d611f..daafa50e4 100644 --- a/plugin-api/pom.xml +++ b/plugin-api/pom.xml @@ -29,6 +29,10 @@ plugin-api + + com.google.guava + guava + com.squareup javapoet diff --git a/plugin-api/src/main/java/io/opentelemetry/sdk/resources/EaseAgentResource.java b/plugin-api/src/main/java/io/opentelemetry/sdk/resources/EaseAgentResource.java index e55bffde5..19740ee1a 100644 --- a/plugin-api/src/main/java/io/opentelemetry/sdk/resources/EaseAgentResource.java +++ b/plugin-api/src/main/java/io/opentelemetry/sdk/resources/EaseAgentResource.java @@ -6,12 +6,13 @@ import com.megaease.easeagent.plugin.api.otlp.common.SemanticKey; import com.megaease.easeagent.plugin.bridge.EaseAgent; import io.opentelemetry.api.common.Attributes; -import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME; -import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAMESPACE; import javax.annotation.Nullable; import java.util.List; +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME; +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAMESPACE; + public class EaseAgentResource extends Resource implements ConfigChangeListener { static volatile EaseAgentResource agentResource = null; diff --git a/plugin-api/src/main/java/io/opentelemetry/sdk/resources/OtelSdkConfigs.java b/plugin-api/src/main/java/io/opentelemetry/sdk/resources/OtelSdkConfigs.java new file mode 100644 index 000000000..075b74845 --- /dev/null +++ b/plugin-api/src/main/java/io/opentelemetry/sdk/resources/OtelSdkConfigs.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2023, Inspireso and/or its affiliates. All rights reserved. + */ + +package io.opentelemetry.sdk.resources; + +import com.google.common.base.CaseFormat; +import com.google.common.base.Splitter; +import com.megaease.easeagent.plugin.utils.ImmutableMap; +import com.megaease.easeagent.plugin.utils.SystemEnv; +import com.megaease.easeagent.plugin.utils.common.StringUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + + +/** + * Compatible with opentelemetry-java. + *

+ * {@see https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#disabling-opentelemetrysdk} + */ +public class OtelSdkConfigs { + static final String OTEL_RESOURCE_ATTRIBUTES = "OTEL_RESOURCE_ATTRIBUTES"; + + private static final Splitter.MapSplitter OTEL_RESOURCE_ATTRIBUTES_SPLITTER + = Splitter.on(",") + .omitEmptyStrings() + .withKeyValueSeparator("="); + + private static final Map SDK_ATTRIBUTES_TO_EASE_AGENT_PROPS = + ImmutableMap.builder() + .put("javaagent.configuration-file", "config.path") + .put("sdk.disabled", "easeagent.server.enabled") + .put("service.name", "name") //"easeagent.name" + .put("service.namespace", "system") //"easeagent.system" + .build(); + + // -Dotel.service.name=xxx + private static final Map OTEL_SDK_PROPS_TO_EASE_AGENT_PROPS = new HashMap<>(); + + // OTEL_SERVICE_NAME=xxx + private static final Map OTEL_SDK_ENV_VAR_TO_EASE_AGENT_PROPS = new HashMap<>(); + + static { + for (Map.Entry entry : SDK_ATTRIBUTES_TO_EASE_AGENT_PROPS.entrySet()) { + // lower.hyphen -> UPPER_UNDERSCORE + OTEL_SDK_PROPS_TO_EASE_AGENT_PROPS.put( + "otel." + entry.getKey(), + entry.getValue() + ); + } + + for (Map.Entry entry : OTEL_SDK_PROPS_TO_EASE_AGENT_PROPS.entrySet()) { + // lower.hyphen -> UPPER_UNDERSCORE + OTEL_SDK_ENV_VAR_TO_EASE_AGENT_PROPS.put( + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, entry.getKey().replace('.', '-')), + entry.getValue() + ); + } + } + + /** + * update config value from environment variables and java properties + *

+ * java properties > environment variables > OTEL_RESOURCE_ATTRIBUTES + */ + public static Map updateEnvCfg() { + Map envCfg = new TreeMap<>(); + + String configEnv = SystemEnv.get(OTEL_RESOURCE_ATTRIBUTES); + if (StringUtils.isNotEmpty(configEnv)) { + Map map = OTEL_RESOURCE_ATTRIBUTES_SPLITTER.split(configEnv); + if (!map.isEmpty()) { + for (Map.Entry entry : SDK_ATTRIBUTES_TO_EASE_AGENT_PROPS.entrySet()) { + String value = map.get(entry.getKey()); + if (!StringUtils.isEmpty(value)) { + envCfg.put(entry.getValue(), value); + } + } + } + } + + // override by environment variables, eg: export OTEL_SERVICE_NAME=xxx + for (Map.Entry entry : OTEL_SDK_ENV_VAR_TO_EASE_AGENT_PROPS.entrySet()) { + String value = SystemEnv.get(entry.getKey()); + if (!StringUtils.isEmpty(value)) { + envCfg.put(entry.getValue(), value); + } + } + + // override by java properties; eg: java -Dotel.service.name=xxx + for (Map.Entry entry : OTEL_SDK_PROPS_TO_EASE_AGENT_PROPS.entrySet()) { + String value = System.getProperty(entry.getKey()); + if (!StringUtils.isEmpty(value)) { + envCfg.put(entry.getValue(), value); + } + } + + return envCfg; + } +} diff --git a/plugin-api/src/test/java/io/opentelemetry/sdk/resources/OtelSdkConfigsTest.java b/plugin-api/src/test/java/io/opentelemetry/sdk/resources/OtelSdkConfigsTest.java new file mode 100644 index 000000000..ed9d3c5fa --- /dev/null +++ b/plugin-api/src/test/java/io/opentelemetry/sdk/resources/OtelSdkConfigsTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023, Inspireso and/or its affiliates. All rights reserved. + */ + +package io.opentelemetry.sdk.resources; + +import com.megaease.easeagent.plugin.api.config.AutoRefreshConfigSupplier; +import com.megaease.easeagent.plugin.tools.config.AutoRefreshConfigSupplierTest; +import com.megaease.easeagent.plugin.utils.SystemEnv; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Type; +import java.util.Map; + +import static io.opentelemetry.sdk.resources.OtelSdkConfigs.OTEL_RESOURCE_ATTRIBUTES; +import static org.junit.Assert.*; + +public class OtelSdkConfigsTest { + + @Test + public void updateEnvCfg() { + //value from system env "OTEL_RESOURCE_ATTRIBUTES + String attributes="service.name=service1,service.namespace=namespace1"; + SystemEnv.set(OTEL_RESOURCE_ATTRIBUTES, attributes); + Map envCfg = OtelSdkConfigs.updateEnvCfg(); + Assert.assertEquals("service1", envCfg.get("name")); + Assert.assertEquals("namespace1", envCfg.get("system")); + + // override by system env + SystemEnv.set("OTEL_SERVICE_NAME", "service2"); + SystemEnv.set("OTEL_SERVICE_NAMESPACE", "namespace2"); + envCfg = OtelSdkConfigs.updateEnvCfg(); + Assert.assertEquals("service2", envCfg.get("name")); + Assert.assertEquals("namespace2", envCfg.get("system")); + + // override by system property + System.setProperty("otel.service.name", "service3"); + System.setProperty("otel.service.namespace", "namespace3"); + envCfg = OtelSdkConfigs.updateEnvCfg(); + Assert.assertEquals("service3", envCfg.get("name")); + Assert.assertEquals("namespace3", envCfg.get("system")); + + } +}