From 6c3acd416780e09e62f26811f3959b72ceccfbbb Mon Sep 17 00:00:00 2001 From: cleverchuk Date: Thu, 29 Oct 2026 14:41:48 -0400 Subject: [PATCH 1/3] Remove unused ReporterProvider --- .../extensions/ReporterProvider.java | 48 ------------------- .../extensions/SolarwindsAgentListener.java | 10 ++-- 2 files changed, 3 insertions(+), 55 deletions(-) delete mode 100644 custom/src/main/java/com/solarwinds/opentelemetry/extensions/ReporterProvider.java diff --git a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/ReporterProvider.java b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/ReporterProvider.java deleted file mode 100644 index 393528b7..00000000 --- a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/ReporterProvider.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * © SolarWinds Worldwide, LLC. All rights reserved. - * - * Licensed 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 com.solarwinds.opentelemetry.extensions; - -import com.solarwinds.joboe.core.EventReporter; -import com.solarwinds.joboe.core.ReporterFactory; -import com.solarwinds.joboe.core.rpc.ClientException; -import com.solarwinds.joboe.core.rpc.RpcClientManager; -import com.solarwinds.joboe.logging.Logger; -import com.solarwinds.joboe.logging.LoggerFactory; - -public final class ReporterProvider { - private ReporterProvider() {} - - private static EventReporter eventReporter; - - public static EventReporter getEventReporter() { - if (eventReporter == null) { - Logger logger = LoggerFactory.getLogger(); - try { - logger.debug("Building reporter"); - eventReporter = - ReporterFactory.getInstance() - .createQueuingEventReporter( - RpcClientManager.getClient(RpcClientManager.OperationType.TRACING)); - logger.debug("Built reporter"); - } catch (ClientException clientException) { - logger.error( - String.format("Unable to create event reporter due error -> %s", clientException)); - } - } - return eventReporter; - } -} diff --git a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/SolarwindsAgentListener.java b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/SolarwindsAgentListener.java index 27055c10..8b874c34 100644 --- a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/SolarwindsAgentListener.java +++ b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/SolarwindsAgentListener.java @@ -16,8 +16,6 @@ package com.solarwinds.opentelemetry.extensions; -import static com.solarwinds.opentelemetry.extensions.config.provider.AutoConfigurationCustomizerProviderImpl.isAgentEnabled; - import com.google.auto.service.AutoService; import com.solarwinds.joboe.config.ConfigManager; import com.solarwinds.joboe.config.ConfigProperty; @@ -38,12 +36,15 @@ import com.solarwinds.opentelemetry.extensions.config.HttpSettingsReaderDelegate; import io.opentelemetry.javaagent.extension.AgentListener; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; + import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import static com.solarwinds.opentelemetry.extensions.config.provider.AutoConfigurationCustomizerProviderImpl.isAgentEnabled; + /** * Executes startup task after it's safe to do so. See ... @@ -139,11 +140,6 @@ private void registerShutdownTasks() { new Thread("SolarwindsAPM-shutdown-hook") { @Override public void run() { - if (ReporterProvider.getEventReporter() != null) { - ReporterProvider.getEventReporter() - .close(); // close event reporter properly to give it chance to send out pending - // events in queue - } ClientManagerProvider .closeAllManagers(); // close all rpc client, this should flush out all messages or // stop immediately (if not connected) From b19b65d61bf6f8dc8a761b3c51f8896deed895bd Mon Sep 17 00:00:00 2001 From: cleverchuk Date: Thu, 29 Oct 2026 16:40:19 -0400 Subject: [PATCH 2/3] Factor proxy config to individual components. Prefer explicit configuration setting over auto-injected. Only accept logging config from distribution node --- custom/build.gradle.kts | 1 + .../extensions/SolarwindsAgentListener.java | 5 +- .../extensions/config/DeclarativeLoader.java | 24 +- .../HttpSamplerComponentProvider.java | 25 +- ...ofilingSpanProcessorComponentProvider.java | 58 +- .../config/DeclarativeLoaderTest.java | 84 +- .../HttpSamplerComponentProviderTest.java | 5 +- ...ingSpanProcessorComponentProviderTest.java | 110 +++ custom/src/test/resources/sdk-config-bad.yaml | 1 + custom/src/test/resources/sdk-config.yaml | 14 +- .../joboe/config/ConfigContainer.java | 15 + .../joboe/config/ConfigManager.java | 13 +- .../joboe/config/ConfigManagerTest.java | 74 ++ .../joboe/logging/LoggerConfiguration.java | 1 + .../extensions/DelegatingMetricExporter.java | 11 +- .../extensions/config/ProxyHelper.java | 21 + .../config/SolarwindsConfigResolver.java | 164 ---- .../parser/yaml/DeclarativeConfigParser.java | 6 +- .../config/parser/yaml/LogSettingParser.java | 2 +- .../config/parser/yaml/ProxyParser.java | 44 - .../LogExporterComponentProvider.java | 7 +- .../MetricExporterComponentProvider.java | 10 +- .../SharedConfigCustomizerProvider.java | 278 ------ .../SpanExporterComponentProvider.java | 7 +- .../DelegatingMetricExporterTest.java | 39 +- .../extensions/config/ProxyHelperTest.java | 41 + .../config/SolarwindsConfigResolverTest.java | 151 ---- .../yaml/DeclarativeConfigParserTest.java | 71 +- .../parser/yaml/LogSettingParserTest.java | 5 +- .../config/parser/yaml/ProxyParserTest.java | 74 -- .../config/provider/ExporterConfigs.java | 45 + .../LogExporterComponentProviderTest.java | 32 +- .../MetricExporterComponentProviderTest.java | 56 +- .../SharedConfigCustomizerProviderTest.java | 846 ------------------ .../SpanExporterComponentProviderTest.java | 31 +- .../shared/src/test/resources/sdk-config.yaml | 15 +- sdk-config.yaml | 5 +- smoke-tests/sdk-config.yaml | 42 +- .../test/java/com/solarwinds/SmokeTest.java | 9 +- .../test/java/com/solarwinds/SmokeTestV2.java | 30 +- 40 files changed, 694 insertions(+), 1778 deletions(-) create mode 100644 libs/config/src/test/java/com/solarwinds/joboe/config/ConfigManagerTest.java delete mode 100644 libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/SolarwindsConfigResolver.java delete mode 100644 libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/ProxyParser.java delete mode 100644 libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/SharedConfigCustomizerProvider.java delete mode 100644 libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/SolarwindsConfigResolverTest.java delete mode 100644 libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/ProxyParserTest.java create mode 100644 libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/ExporterConfigs.java delete mode 100644 libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/SharedConfigCustomizerProviderTest.java diff --git a/custom/build.gradle.kts b/custom/build.gradle.kts index 5fd8f17f..1ae58748 100644 --- a/custom/build.gradle.kts +++ b/custom/build.gradle.kts @@ -50,6 +50,7 @@ dependencies { testImplementation(project(":libs:sampling")) testImplementation(project(":libs:shared")) testImplementation(project(":libs:core")) + testImplementation(project(":libs:logging")) testImplementation("io.opentelemetry:opentelemetry-api-incubator") testImplementation("io.opentelemetry:opentelemetry-sdk-extension-declarative-config") diff --git a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/SolarwindsAgentListener.java b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/SolarwindsAgentListener.java index 8b874c34..f1eeb636 100644 --- a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/SolarwindsAgentListener.java +++ b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/SolarwindsAgentListener.java @@ -16,6 +16,8 @@ package com.solarwinds.opentelemetry.extensions; +import static com.solarwinds.opentelemetry.extensions.config.provider.AutoConfigurationCustomizerProviderImpl.isAgentEnabled; + import com.google.auto.service.AutoService; import com.solarwinds.joboe.config.ConfigManager; import com.solarwinds.joboe.config.ConfigProperty; @@ -36,15 +38,12 @@ import com.solarwinds.opentelemetry.extensions.config.HttpSettingsReaderDelegate; import io.opentelemetry.javaagent.extension.AgentListener; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; - import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import static com.solarwinds.opentelemetry.extensions.config.provider.AutoConfigurationCustomizerProviderImpl.isAgentEnabled; - /** * Executes startup task after it's safe to do so. See ... diff --git a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/DeclarativeLoader.java b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/DeclarativeLoader.java index cd1e1048..573a4695 100644 --- a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/DeclarativeLoader.java +++ b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/DeclarativeLoader.java @@ -19,6 +19,7 @@ import com.google.auto.service.AutoService; import com.solarwinds.joboe.config.ConfigContainer; import com.solarwinds.joboe.config.ConfigGroup; +import com.solarwinds.joboe.config.ConfigManager; import com.solarwinds.joboe.config.InvalidConfigException; import com.solarwinds.joboe.config.JavaRuntimeVersionChecker; import com.solarwinds.joboe.logging.Logger; @@ -30,7 +31,6 @@ import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; -import java.util.Objects; @AutoService(DeclarativeConfigurationCustomizerProvider.class) public class DeclarativeLoader implements DeclarativeConfigurationCustomizerProvider { @@ -49,13 +49,10 @@ private void loadConfig(OpenTelemetryConfigurationModel configurationModel) { DeclarativeConfigProperties configProperties = DeclarativeConfiguration.toConfigProperties(configurationModel); - // SolarwindsConfigResolver merges the distribution and instrumentation nodes per key - // (distribution takes precedence, instrumentation fills the rest); it returns null when - // neither node carries any properties. DeclarativeConfigProperties solarwinds = - Objects.requireNonNull( - SolarwindsConfigResolver.resolve(configProperties), - "Solarwinds configuration cannot be null."); + configProperties + .getStructured("distribution", DeclarativeConfigProperties.empty()) + .getStructured("solarwinds", DeclarativeConfigProperties.empty()); ConfigContainer configContainer = new ConfigContainer(); DeclarativeConfigParser declarativeConfigParser = new DeclarativeConfigParser(configContainer); @@ -66,16 +63,11 @@ private void loadConfig(OpenTelemetryConfigurationModel configurationModel) { throw new RuntimeException(e); } - try { - ConfigurationLoader.processConfigs(configContainer); - ConfigContainer agentConfig = configContainer.subset(ConfigGroup.AGENT); - LoggerFactory.init(LoggingConfigProvider.getLoggerConfiguration(agentConfig)); - - logger.info("Loaded via declarative config"); - } catch (InvalidConfigException e) { - throw new RuntimeException(e); - } + ConfigManager.initialize(configContainer); + ConfigContainer agentConfig = configContainer.subset(ConfigGroup.AGENT); + LoggerFactory.init(LoggingConfigProvider.getLoggerConfiguration(agentConfig)); + logger.info("Loaded via declarative config"); if (!JavaRuntimeVersionChecker.isJdkVersionSupported()) { logger.warn( String.format( diff --git a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/HttpSamplerComponentProvider.java b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/HttpSamplerComponentProvider.java index 5fa2f012..a3eb2870 100644 --- a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/HttpSamplerComponentProvider.java +++ b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/HttpSamplerComponentProvider.java @@ -26,6 +26,7 @@ import com.solarwinds.opentelemetry.extensions.config.HttpSettingsFetcher; import com.solarwinds.opentelemetry.extensions.config.HttpSettingsReader; import com.solarwinds.opentelemetry.extensions.config.HttpSettingsReaderDelegate; +import com.solarwinds.opentelemetry.extensions.config.ProxyHelper; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import java.util.function.Function; @@ -43,12 +44,6 @@ public class HttpSamplerComponentProvider extends AbstractSamplerComponentProvid private static final String CONTEXT_MAX_BACKTRACES = "context_max_backtraces"; - private static final String PROXY = "proxy"; - - private static final String PROXY_HOST = "host"; - - private static final String PROXY_PORT = "port"; - private static final long POLLING_INTERVAL_SECONDS = 60; private final Function delegateFactory; @@ -101,7 +96,7 @@ protected SettingsFetcher createSettingsFetcher(DeclarativeConfigProperties prop serviceKey = ServiceKeyUtils.transformServiceKey(serviceKey); return new HttpSettingsFetcher( HttpSettingsReader.forFileConfig( - delegateFactory.apply(parseProxy(properties)), collector, serviceKey), + delegateFactory.apply(ProxyHelper.toProxyConfig(properties)), collector, serviceKey), POLLING_INTERVAL_SECONDS); } @@ -123,20 +118,4 @@ private static String parseNonEmpty(DeclarativeConfigProperties properties, Stri } return value; } - - private ProxyConfig parseProxy(DeclarativeConfigProperties properties) - throws InvalidConfigException { - DeclarativeConfigProperties proxy = properties.getStructured(PROXY); - if (proxy == null) { - return null; - } - - String host = proxy.getString(PROXY_HOST); - Integer port = proxy.getInt(PROXY_PORT); - if (host == null || host.isEmpty() || port == null || port < 1 || port > 65535) { - throw new InvalidConfigException( - "Invalid sampler proxy config, both proxy.host and a valid proxy.port are required"); - } - return new ProxyConfig(host, port, null, null); - } } diff --git a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/ProfilingSpanProcessorComponentProvider.java b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/ProfilingSpanProcessorComponentProvider.java index 9f08fc5c..06d11c38 100644 --- a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/ProfilingSpanProcessorComponentProvider.java +++ b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/ProfilingSpanProcessorComponentProvider.java @@ -21,8 +21,11 @@ import com.solarwinds.joboe.config.ConfigProperty; import com.solarwinds.joboe.config.InvalidConfigException; import com.solarwinds.joboe.config.JavaRuntimeVersionChecker; +import com.solarwinds.joboe.config.ProxyConfig; +import com.solarwinds.joboe.config.ServiceKeyUtils; import com.solarwinds.joboe.core.profiler.ProfilerSetting; import com.solarwinds.opentelemetry.extensions.SolarwindsProfilingSpanProcessor; +import com.solarwinds.opentelemetry.extensions.config.ProxyHelper; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.trace.SpanProcessor; @@ -35,6 +38,10 @@ public class ProfilingSpanProcessorComponentProvider implements ComponentProvide public static final String COMPONENT_NAME = "swo_profiler"; + private static final String SERVICE_KEY = "service_key"; + + private static final String COLLECTOR = "collector"; + private static final List DEFAULT_EXCLUDE_PACKAGES = Arrays.asList("java", "javax", "com.sun", "sun", "sunw"); @@ -51,8 +58,8 @@ public String getName() { @Override public SpanProcessor create(DeclarativeConfigProperties declarativeConfigProperties) { try { - ProfilerSetting convert = convert(declarativeConfigProperties); - ConfigManager.setConfig(ConfigProperty.PROFILER, convert); + ProfilerSetting profilerSetting = convert(declarativeConfigProperties); + ConfigManager.setConfig(ConfigProperty.PROFILER, profilerSetting); } catch (InvalidConfigException e) { throw new RuntimeException(e); } @@ -60,18 +67,20 @@ public SpanProcessor create(DeclarativeConfigProperties declarativeConfigPropert return new SolarwindsProfilingSpanProcessor(); } - ProfilerSetting convert(DeclarativeConfigProperties profilerSettings) + ProfilerSetting convert(DeclarativeConfigProperties profilerProperties) throws InvalidConfigException { boolean enabled = JavaRuntimeVersionChecker.isJdkVersionSupported() - && profilerSettings.getBoolean("enabled", false); + && profilerProperties.getBoolean("enabled", false); + int circuitBreakerDurationThreshold = - profilerSettings.getInt("circuit_breaker_duration_threshold", 100); + profilerProperties.getInt("circuit_breaker_duration_threshold", 100); + int circuitBreakerCountThreshold = - profilerSettings.getInt("circuit_breaker_count_threshold", 2); + profilerProperties.getInt("circuit_breaker_count_threshold", 2); - int interval = profilerSettings.getInt("interval", 20); + int interval = profilerProperties.getInt("interval", 20); if (interval != 0 && interval < ProfilerSetting @@ -83,8 +92,41 @@ ProfilerSetting convert(DeclarativeConfigProperties profilerSettings) + interval); } + // Under declarative config, swo_profiler is the only source of the agent-wide AGENT_PROXY, + // AGENT_SERVICE_KEY and AGENT_COLLECTOR values read by RpcClientManager, and GrpcClient. + // Without a swo_profiler block they stay unset. + ProxyConfig proxyConfig = ProxyHelper.toProxyConfig(profilerProperties); + if (proxyConfig != null) { + ConfigManager.setConfig(ConfigProperty.AGENT_PROXY, proxyConfig); + } + + String serviceKey = profilerProperties.getString(SERVICE_KEY); + if (serviceKey != null) { + if (serviceKey.trim().isEmpty()) { + throw new InvalidConfigException( + "Invalid profiler " + SERVICE_KEY + " config, value cannot be empty"); + } + + ConfigManager.setConfig( + ConfigProperty.AGENT_SERVICE_KEY, ServiceKeyUtils.transformServiceKey(serviceKey)); + + } else if (enabled) { + throw new InvalidConfigException("Missing required profiler " + SERVICE_KEY + " config"); + } + + String collector = profilerProperties.getString(COLLECTOR); + if (collector != null) { + if (collector.trim().isEmpty()) { + throw new InvalidConfigException( + "Invalid profiler " + COLLECTOR + " config, value cannot be empty"); + } + + ConfigManager.setConfig(ConfigProperty.AGENT_COLLECTOR, collector); + } + List excludePackages = - profilerSettings.getScalarList("exclude_packages", String.class, DEFAULT_EXCLUDE_PACKAGES); + profilerProperties.getScalarList( + "exclude_packages", String.class, DEFAULT_EXCLUDE_PACKAGES); return new ProfilerSetting( enabled, new HashSet<>(excludePackages), diff --git a/custom/src/test/java/com/solarwinds/opentelemetry/extensions/config/DeclarativeLoaderTest.java b/custom/src/test/java/com/solarwinds/opentelemetry/extensions/config/DeclarativeLoaderTest.java index df406062..d6763171 100644 --- a/custom/src/test/java/com/solarwinds/opentelemetry/extensions/config/DeclarativeLoaderTest.java +++ b/custom/src/test/java/com/solarwinds/opentelemetry/extensions/config/DeclarativeLoaderTest.java @@ -16,14 +16,16 @@ package com.solarwinds.opentelemetry.extensions.config; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.verify; import com.solarwinds.joboe.config.ConfigManager; import com.solarwinds.joboe.config.ConfigProperty; +import com.solarwinds.joboe.logging.LogSetting; +import com.solarwinds.joboe.logging.Logger; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.DistributionModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; @@ -68,57 +70,28 @@ public static void clearConfig() { @Test public void testCustomizeLoadsDistributionConfig() { - OpenTelemetryConfigurationModel model = model(distribution("token:service"), null); - + OpenTelemetryConfigurationModel model = model(distribution("debug"), null); OpenTelemetryConfigurationModel returned = customize(model); - assertNotNull(ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); - assertNotNull(ConfigManager.getConfig(ConfigProperty.AGENT_SERVICE_KEY)); + assertEquals(Logger.Level.DEBUG, loggingLevel()); assertEquals(model, returned); } @Test - public void testCustomizeLoadsInstrumentationConfig() { - OpenTelemetryConfigurationModel model = model(null, instrumentation("token:service")); - - customize(model); - - assertNotNull(ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); - assertNotNull(ConfigManager.getConfig(ConfigProperty.AGENT_SERVICE_KEY)); - } - - @Test - public void testDistributionTakesPrecedenceWhenBothNodesPresent() { - OpenTelemetryConfigurationModel model = - model( - distribution("distribution-token:distribution-service"), - instrumentation("instrumentation-token:inst-service")); + public void testCustomizeIgnoresInstrumentationConfig() { + OpenTelemetryConfigurationModel model = model(distribution("debug"), instrumentation("trace")); customize(model); - assertEquals( - "distribution-token:distribution-service", - ConfigManager.getConfig(ConfigProperty.AGENT_SERVICE_KEY)); + assertEquals(Logger.Level.DEBUG, loggingLevel()); } @Test - public void testCustomizeWithEmptyModelThrows() { + public void testCustomizeWithEmptyModelDoesNotThrows() { OpenTelemetryConfigurationModel model = new OpenTelemetryConfigurationModel(); - assertThrows(NullPointerException.class, () -> customize(model)); - assertNull(ConfigManager.getConfig(ConfigProperty.AGENT_SERVICE_KEY)); - } - - @Test - public void testCustomizeSkipsEmptySolarwindsNode() { - OpenTelemetryConfigurationModel model = - model( - new DistributionModel().setExtensionProperty("solarwinds", new HashMap<>()), - instrumentation("token:service")); - - customize(model); - - assertEquals("token:service", ConfigManager.getConfig(ConfigProperty.AGENT_SERVICE_KEY)); + assertDoesNotThrow(() -> customize(model)); + assertNull(ConfigManager.getConfig(ConfigProperty.AGENT_LOGGING)); } @Test @@ -134,17 +107,31 @@ public void testCustomizeThrowsOnUnknownKey() { } @Test - public void testCustomizeThrowsWhenServiceKeyMissing() { + public void testCustomizeThrowsOnServiceKey() { OpenTelemetryConfigurationModel model = model( new DistributionModel() .setExtensionProperty( "solarwinds", Collections.singletonMap( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), "apm.collector.com")), + ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), "token:service")), null); assertThrows(RuntimeException.class, () -> customize(model)); + assertNull(ConfigManager.getConfig(ConfigProperty.AGENT_SERVICE_KEY)); + } + + @Test + public void testCustomizeMergesIntoPreviouslySetConfig() throws Exception { + ConfigManager.setConfig(ConfigProperty.AGENT_COLLECTOR, "apm.collector.com"); + customize(model(distribution("debug"), null)); + + assertEquals(Logger.Level.DEBUG, loggingLevel()); + assertEquals("apm.collector.com", ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); + } + + private static Logger.Level loggingLevel() { + return ((LogSetting) ConfigManager.getConfig(ConfigProperty.AGENT_LOGGING)).getLevel(); } private static OpenTelemetryConfigurationModel model( @@ -160,25 +147,24 @@ private static OpenTelemetryConfigurationModel model( return model; } - private static DistributionModel distribution(String serviceKey) { + private static Map logging(String level) { + return Collections.singletonMap("level", level); + } + + private static DistributionModel distribution(String loggingLevel) { Map properties = new HashMap<>(); - properties.put(ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), serviceKey); - properties.put(ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), "apm.collector.com"); + properties.put("logging", logging(loggingLevel)); return new DistributionModel().setExtensionProperty("solarwinds", properties); } - private static ExperimentalInstrumentationModel instrumentation(String serviceKey) { + private static ExperimentalInstrumentationModel instrumentation(String loggingLevel) { return new ExperimentalInstrumentationModel() .setJava( new ExperimentalLanguageSpecificInstrumentationModel() .setAdditionalProperty( "solarwinds", new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), serviceKey) - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.collector.com"))); + .setAdditionalProperty("logging", logging(loggingLevel)))); } private OpenTelemetryConfigurationModel customize(OpenTelemetryConfigurationModel model) { diff --git a/custom/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/HttpSamplerComponentProviderTest.java b/custom/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/HttpSamplerComponentProviderTest.java index 2be06c02..bd65da6a 100644 --- a/custom/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/HttpSamplerComponentProviderTest.java +++ b/custom/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/HttpSamplerComponentProviderTest.java @@ -165,10 +165,7 @@ static Stream invalidConfigs() { SERVICE_KEY_ONLY + " collector: ' '\n", SERVICE_KEY_ONLY + " context_ttl: 0\n", SERVICE_KEY_ONLY + " context_max_events: -1\n", - SERVICE_KEY_ONLY + " context_max_backtraces: 0\n", - SERVICE_KEY_ONLY + " proxy:\n host: proxy.example.com\n", - SERVICE_KEY_ONLY + " proxy:\n port: 8080\n", - SERVICE_KEY_ONLY + " proxy:\n host: proxy.example.com\n port: 70000\n"); + SERVICE_KEY_ONLY + " context_max_backtraces: 0\n"); } @Test diff --git a/custom/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/ProfilingSpanProcessorComponentProviderTest.java b/custom/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/ProfilingSpanProcessorComponentProviderTest.java index b528541d..4dce97de 100644 --- a/custom/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/ProfilingSpanProcessorComponentProviderTest.java +++ b/custom/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/ProfilingSpanProcessorComponentProviderTest.java @@ -18,18 +18,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import com.solarwinds.joboe.config.ConfigManager; import com.solarwinds.joboe.config.ConfigProperty; import com.solarwinds.joboe.config.InvalidConfigException; import com.solarwinds.joboe.config.JavaRuntimeVersionChecker; +import com.solarwinds.joboe.config.ProxyConfig; import com.solarwinds.joboe.core.profiler.ProfilerSetting; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfiguration; import io.opentelemetry.sdk.trace.SpanProcessor; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -97,6 +101,112 @@ void testCreateWithInvalidConfigThrows() { assertThrows(RuntimeException.class, () -> tested.create(config)); } + @Test + void testConvertSetsProxyConfig() throws InvalidConfigException { + tested.convert(fromYaml("swo_profiler:\n proxy:\n host: proxy.local\n port: 3128\n")); + + ProxyConfig proxyConfig = (ProxyConfig) ConfigManager.getConfig(ConfigProperty.AGENT_PROXY); + assertEquals("proxy.local", proxyConfig.getHost()); + assertEquals(3128, proxyConfig.getPort()); + } + + @Test + void testConvertWithoutProxyLeavesProxyConfigUnset() throws InvalidConfigException { + tested.convert(loadProfilerConfig("/sdk-config.yaml")); + assertNull(ConfigManager.getConfig(ConfigProperty.AGENT_PROXY)); + } + + @Test + void testConvertWithIncompleteProxyLeavesProxyConfigUnset() throws InvalidConfigException { + tested.convert(fromYaml("swo_profiler:\n proxy:\n host: proxy.local\n")); + assertNull(ConfigManager.getConfig(ConfigProperty.AGENT_PROXY)); + } + + @Test + void testConvertSetsServiceKey() throws InvalidConfigException { + tested.convert(fromYaml("swo_profiler:\n service_key: token:my service\n")); + + assertEquals("token:my-service", ConfigManager.getConfig(ConfigProperty.AGENT_SERVICE_KEY)); + } + + @Test + void testConvertDisabledWithoutServiceKeyLeavesServiceKeyUnset() throws InvalidConfigException { + ProfilerSetting profilerSetting = tested.convert(fromYaml("swo_profiler:\n enabled: false\n")); + + assertEquals(new ProfilerSetting(false, DEFAULT_EXCLUDES, 20, 100, 2), profilerSetting); + assertNull(ConfigManager.getConfig(ConfigProperty.AGENT_SERVICE_KEY)); + } + + @Test + void testConvertEnabledWithoutServiceKeyThrows() { + try (MockedStatic checker = + Mockito.mockStatic(JavaRuntimeVersionChecker.class)) { + checker.when(JavaRuntimeVersionChecker::isJdkVersionSupported).thenReturn(true); + + DeclarativeConfigProperties config = fromYaml("swo_profiler:\n enabled: true\n"); + assertThrows(InvalidConfigException.class, () -> tested.convert(config)); + } + } + + @Test + void testConvertEnabledOnUnsupportedJdkWithoutServiceKeyIsDisabled() + throws InvalidConfigException { + try (MockedStatic checker = + Mockito.mockStatic(JavaRuntimeVersionChecker.class)) { + checker.when(JavaRuntimeVersionChecker::isJdkVersionSupported).thenReturn(false); + + ProfilerSetting profilerSetting = + tested.convert(fromYaml("swo_profiler:\n enabled: true\n")); + assertEquals(new ProfilerSetting(false, DEFAULT_EXCLUDES, 20, 100, 2), profilerSetting); + } + } + + @Test + void testCreateEnabledWithoutServiceKeyThrows() { + try (MockedStatic checker = + Mockito.mockStatic(JavaRuntimeVersionChecker.class)) { + checker.when(JavaRuntimeVersionChecker::isJdkVersionSupported).thenReturn(true); + + DeclarativeConfigProperties config = fromYaml("swo_profiler:\n enabled: true\n"); + assertThrows(RuntimeException.class, () -> tested.create(config)); + assertNull(ConfigManager.getConfig(ConfigProperty.PROFILER)); + } + } + + @Test + void testConvertWithEmptyServiceKeyThrows() { + DeclarativeConfigProperties config = fromYaml("swo_profiler:\n service_key: \" \"\n"); + assertThrows(InvalidConfigException.class, () -> tested.convert(config)); + assertNull(ConfigManager.getConfig(ConfigProperty.AGENT_SERVICE_KEY)); + } + + @Test + void testConvertSetsCollector() throws InvalidConfigException { + tested.convert(fromYaml("swo_profiler:\n collector: apm.collector.example.com:443\n")); + + assertEquals( + "apm.collector.example.com:443", ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); + } + + @Test + void testConvertWithoutCollectorLeavesCollectorUnset() throws InvalidConfigException { + tested.convert(loadProfilerConfig("/sdk-config.yaml")); + assertNull(ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); + } + + @Test + void testConvertWithEmptyCollectorThrows() { + DeclarativeConfigProperties config = fromYaml("swo_profiler:\n collector: \" \"\n"); + assertThrows(InvalidConfigException.class, () -> tested.convert(config)); + assertNull(ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); + } + + private DeclarativeConfigProperties fromYaml(String yaml) { + return DeclarativeConfiguration.toConfigProperties( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))) + .getStructured("swo_profiler"); + } + private DeclarativeConfigProperties loadProfilerConfig(String resource) { try (InputStream resourceAsStream = ProfilingSpanProcessorComponentProviderTest.class.getResourceAsStream(resource)) { diff --git a/custom/src/test/resources/sdk-config-bad.yaml b/custom/src/test/resources/sdk-config-bad.yaml index 8e362c28..5ffde90f 100644 --- a/custom/src/test/resources/sdk-config-bad.yaml +++ b/custom/src/test/resources/sdk-config-bad.yaml @@ -29,4 +29,5 @@ tracer_provider: processors: - swo_profiler: enabled: true + service_key: token:service interval: 1 diff --git a/custom/src/test/resources/sdk-config.yaml b/custom/src/test/resources/sdk-config.yaml index 501af9f9..d782c844 100644 --- a/custom/src/test/resources/sdk-config.yaml +++ b/custom/src/test/resources/sdk-config.yaml @@ -63,6 +63,7 @@ tracer_provider: headers_list: ${OTEL_EXPORTER_OTLP_HEADERS} - swo_profiler: enabled: true + service_key: token:service - swo_response_time: sampler: @@ -84,9 +85,7 @@ meter_provider: # Configure exporter. exporter: # Configure exporter to be OTLP. - swo/metricExporter: - # Configure protocol. - protocol: grpc + swo_otlp: # Configure endpoint. endpoint: https://otel.collector.na-01.st-ssp.solarwinds.com # Configure certificate. @@ -149,12 +148,15 @@ logger_provider: # Configure max attribute count. Overrides .attribute_limits.attribute_count_limit. attribute_count_limit: 128 +distribution: + solarwinds: + logging: + level: trace + + instrumentation/development: java: common: default-enabled: true swo_jdbc: sql_tag: true - solarwinds: - agent.logging: - level: trace diff --git a/libs/config/src/main/java/com/solarwinds/joboe/config/ConfigContainer.java b/libs/config/src/main/java/com/solarwinds/joboe/config/ConfigContainer.java index cbd843bf..592e2e19 100644 --- a/libs/config/src/main/java/com/solarwinds/joboe/config/ConfigContainer.java +++ b/libs/config/src/main/java/com/solarwinds/joboe/config/ConfigContainer.java @@ -101,6 +101,21 @@ public boolean containsProperty(ConfigProperty propertyKey) { } } + /** + * Copies every property of {@code other} into this container, overwriting values for keys that + * exist in both. + * + * @param other the container whose properties are copied + */ + public void merge(ConfigContainer other) { + other.configMaps.forEach( + (group, configMap) -> { + if (configMap != null) { + configMaps.computeIfAbsent(group, key -> new HashMap<>()).putAll(configMap); + } + }); + } + public Object remove(ConfigProperty propertyKey) { Map configs = configMaps.get(propertyKey.getGroup()); if (configs != null) { diff --git a/libs/config/src/main/java/com/solarwinds/joboe/config/ConfigManager.java b/libs/config/src/main/java/com/solarwinds/joboe/config/ConfigManager.java index 99c572fa..87709b43 100644 --- a/libs/config/src/main/java/com/solarwinds/joboe/config/ConfigManager.java +++ b/libs/config/src/main/java/com/solarwinds/joboe/config/ConfigManager.java @@ -27,8 +27,19 @@ public class ConfigManager { private ConfigManager() {} + /** + * Initializes the configs. If configs were already set, {@code configs} is merged into them and + * takes precedence for keys present in both. + * + * @param configs the configs to initialize with + */ public static void initialize(ConfigContainer configs) { - SINGLETON.configs = configs; + if (SINGLETON.configs == null) { + SINGLETON.configs = configs; + } else { + SINGLETON.configs.merge(configs); + logger.debug("ConfigManager already initialized, merged new configs"); + } } /** For internal testing - reset the states of the ConfigManager */ diff --git a/libs/config/src/test/java/com/solarwinds/joboe/config/ConfigManagerTest.java b/libs/config/src/test/java/com/solarwinds/joboe/config/ConfigManagerTest.java new file mode 100644 index 00000000..9197d035 --- /dev/null +++ b/libs/config/src/test/java/com/solarwinds/joboe/config/ConfigManagerTest.java @@ -0,0 +1,74 @@ +/* + * © SolarWinds Worldwide, LLC. All rights reserved. + * + * Licensed 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 com.solarwinds.joboe.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ConfigManagerTest { + + @BeforeEach + void setUp() { + ConfigManager.reset(); + } + + @AfterEach + void tearDown() { + ConfigManager.reset(); + } + + @Test + void initializeSetsConfigsWhenUninitialized() throws InvalidConfigException { + ConfigContainer configs = new ConfigContainer(); + configs.put(ConfigProperty.AGENT_COLLECTOR, "apm.collector.com"); + + ConfigManager.initialize(configs); + assertEquals("apm.collector.com", ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); + } + + @Test + void initializeKeepsPreviouslySetConfigs() throws InvalidConfigException { + ConfigManager.setConfig(ConfigProperty.AGENT_COLLECTOR, "apm.collector.com"); + ConfigContainer configs = new ConfigContainer(); + configs.put(ConfigProperty.AGENT_SERVICE_KEY, "token:service"); + + ConfigManager.initialize(configs); + assertEquals("apm.collector.com", ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); + assertEquals("token:service", ConfigManager.getConfig(ConfigProperty.AGENT_SERVICE_KEY)); + } + + @Test + void initializeOverridesPreviouslySetConfigsOnConflict() throws InvalidConfigException { + ConfigManager.setConfig(ConfigProperty.AGENT_COLLECTOR, "old.collector.com"); + ConfigContainer configs = new ConfigContainer(); + configs.put(ConfigProperty.AGENT_COLLECTOR, "new.collector.com"); + + ConfigManager.initialize(configs); + assertEquals("new.collector.com", ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); + } + + @Test + void initializeMergesSubsetWithMissingGroup() throws InvalidConfigException { + ConfigManager.setConfig(ConfigProperty.AGENT_COLLECTOR, "apm.collector.com"); + + ConfigManager.initialize(new ConfigContainer().subset(ConfigGroup.AGENT)); + assertEquals("apm.collector.com", ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); + } +} diff --git a/libs/logging/src/main/java/com/solarwinds/joboe/logging/LoggerConfiguration.java b/libs/logging/src/main/java/com/solarwinds/joboe/logging/LoggerConfiguration.java index ab04aad4..e45f8f51 100644 --- a/libs/logging/src/main/java/com/solarwinds/joboe/logging/LoggerConfiguration.java +++ b/libs/logging/src/main/java/com/solarwinds/joboe/logging/LoggerConfiguration.java @@ -23,6 +23,7 @@ @Value @Builder public class LoggerConfiguration { + @Builder.Default LogSetting logSetting = new LogSetting(Logger.Level.INFO, true, true, null, null, null); diff --git a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/DelegatingMetricExporter.java b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/DelegatingMetricExporter.java index 8d85bad4..24285410 100644 --- a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/DelegatingMetricExporter.java +++ b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/DelegatingMetricExporter.java @@ -31,15 +31,24 @@ import lombok.NonNull; public class DelegatingMetricExporter implements MetricExporter { + private final MetricExporter delegate; + private final boolean exportMetrics; + public DelegatingMetricExporter(MetricExporter delegate) { + this(delegate, true); + } + + public DelegatingMetricExporter(MetricExporter delegate, boolean exportMetric) { this.delegate = delegate; + this.exportMetrics = exportMetric; } @Override public CompletableResultCode export(@NonNull Collection metrics) { - if (ConfigManager.getConfigOptional(ConfigProperty.AGENT_EXPORT_METRICS_ENABLED, true)) { + if (ConfigManager.getConfigOptional( + ConfigProperty.AGENT_EXPORT_METRICS_ENABLED, exportMetrics)) { return delegate.export(metrics).whenComplete(TransactionNameManager::clearTransactionNames); } diff --git a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/ProxyHelper.java b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/ProxyHelper.java index 8f5ff407..3e9c333e 100644 --- a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/ProxyHelper.java +++ b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/ProxyHelper.java @@ -19,6 +19,9 @@ import static com.solarwinds.opentelemetry.extensions.SharedNames.SW_OTEL_PROXY_HOST_KEY; import static com.solarwinds.opentelemetry.extensions.SharedNames.SW_OTEL_PROXY_PORT_KEY; +import com.solarwinds.joboe.config.ProxyConfig; +import com.solarwinds.joboe.logging.LoggerFactory; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; public final class ProxyHelper { @@ -28,4 +31,22 @@ public static boolean isProxyConfigured(ConfigProperties properties) { return properties.getString(SW_OTEL_PROXY_HOST_KEY) != null && properties.getInt(SW_OTEL_PROXY_PORT_KEY) != null; } + + public static ProxyConfig toProxyConfig(DeclarativeConfigProperties properties) { + + DeclarativeConfigProperties proxy = properties.getStructured("proxy"); + if (proxy == null) { + return null; + } + + String host = proxy.getString("host"); + Integer port = proxy.getInt("port"); + if (host == null || host.trim().isEmpty() || port == null || port < 1 || port > 65535) { + LoggerFactory.getLogger() + .warn("Ignoring proxy config: proxy.host and proxy.port (1-65535) are both required"); + return null; + } + + return new ProxyConfig(host, port, null, null); + } } diff --git a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/SolarwindsConfigResolver.java b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/SolarwindsConfigResolver.java deleted file mode 100644 index d6626515..00000000 --- a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/SolarwindsConfigResolver.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * © SolarWinds Worldwide, LLC. All rights reserved. - * - * Licensed 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 com.solarwinds.opentelemetry.extensions.config; - -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.common.ComponentLoader; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Resolves the solarwinds agent configuration from a declarative config tree. - * - *

The configuration may live under the top-level {@code distribution.solarwinds} node (shared - * across languages in a multi-language deployment) and/or under the language-specific {@code - * instrumentation/development.java.solarwinds} node. When both are present they are merged per key: - * the distribution node takes precedence for any key it defines and the instrumentation node fills - * in the rest. A node counts as present when it exists in the tree, even if it carries no - * properties; only a node that is entirely absent is skipped (see {@link #resolve}). - * - *

This is the single source of truth for that precedence so every consumer resolves the same - * effective config for a given tree. - */ -public final class SolarwindsConfigResolver { - - private SolarwindsConfigResolver() {} - - /** - * Returns the effective solarwinds config, merging the distribution and instrumentation nodes per - * key, or {@code null} if neither node is present. - * - *

Presence — not emptiness — decides the outcome: an absent node ({@code getStructured} - * returns {@code null}) is ignored, whereas a present-but-empty node is honored. When both nodes - * are present the result is a per-key merge in which the distribution node wins for every key it - * defines and the instrumentation node supplies the rest; an empty distribution node therefore - * simply defers every key to the instrumentation node. - */ - public static DeclarativeConfigProperties resolve(DeclarativeConfigProperties configProperties) { - DeclarativeConfigProperties distribution = getDistributionConfig(configProperties); - DeclarativeConfigProperties instrumentation = getInstrumentationConfig(configProperties); - - if (distribution != null && instrumentation != null) { - return new MergedConfigProperties(distribution, instrumentation); - } - - if (distribution != null) { - return distribution; - } - - return instrumentation; - } - - /** - * Returns the top-level {@code distribution.solarwinds} node, or {@code null} if it is absent. - */ - public static DeclarativeConfigProperties getDistributionConfig( - DeclarativeConfigProperties configProperties) { - return configProperties - .getStructured("distribution", DeclarativeConfigProperties.empty()) - .getStructured("solarwinds"); - } - - /** - * Returns the {@code instrumentation/development.java.solarwinds} node, or {@code null} if it is - * absent. - */ - public static DeclarativeConfigProperties getInstrumentationConfig( - DeclarativeConfigProperties configProperties) { - return configProperties - .getStructured("instrumentation/development", DeclarativeConfigProperties.empty()) - .getStructured("java", DeclarativeConfigProperties.empty()) - .getStructured("solarwinds"); - } - - /** - * Read-only view over two solarwinds config nodes that resolves each key from the primary node - * when present and falls back to the secondary node otherwise, giving a per-key merge rather than - * an all-or-nothing choice between the nodes. - */ - private static final class MergedConfigProperties implements DeclarativeConfigProperties { - private final DeclarativeConfigProperties primary; - private final DeclarativeConfigProperties fallback; - - MergedConfigProperties( - DeclarativeConfigProperties primary, DeclarativeConfigProperties fallback) { - this.primary = primary; - this.fallback = fallback; - } - - @Override - public String getString(String name) { - String value = primary.getString(name); - return value != null ? value : fallback.getString(name); - } - - @Override - public Boolean getBoolean(String name) { - Boolean value = primary.getBoolean(name); - return value != null ? value : fallback.getBoolean(name); - } - - @Override - public Integer getInt(String name) { - Integer value = primary.getInt(name); - return value != null ? value : fallback.getInt(name); - } - - @Override - public Long getLong(String name) { - Long value = primary.getLong(name); - return value != null ? value : fallback.getLong(name); - } - - @Override - public Double getDouble(String name) { - Double value = primary.getDouble(name); - return value != null ? value : fallback.getDouble(name); - } - - @Override - public List getScalarList(String name, Class scalarType) { - List value = primary.getScalarList(name, scalarType); - return value != null ? value : fallback.getScalarList(name, scalarType); - } - - @Override - public DeclarativeConfigProperties getStructured(String name) { - DeclarativeConfigProperties value = primary.getStructured(name); - return value != null ? value : fallback.getStructured(name); - } - - @Override - public List getStructuredList(String name) { - List value = primary.getStructuredList(name); - return value != null ? value : fallback.getStructuredList(name); - } - - @Override - public Set getPropertyKeys() { - Set keys = new HashSet<>(primary.getPropertyKeys()); - keys.addAll(fallback.getPropertyKeys()); - return keys; - } - - @Override - public ComponentLoader getComponentLoader() { - return primary.getComponentLoader(); - } - } -} diff --git a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/DeclarativeConfigParser.java b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/DeclarativeConfigParser.java index 7b98ec45..620fa611 100644 --- a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/DeclarativeConfigParser.java +++ b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/DeclarativeConfigParser.java @@ -21,6 +21,7 @@ import com.solarwinds.joboe.config.ConfigProperty; import com.solarwinds.joboe.config.InvalidConfigException; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -28,6 +29,9 @@ import java.util.Set; public final class DeclarativeConfigParser { + private static final Map DECLARATIVE_KEYS = + Collections.singletonMap("logging", ConfigProperty.AGENT_LOGGING); + private final ConfigContainer configContainer; private final Map> register = @@ -49,7 +53,7 @@ public void parse(DeclarativeConfigProperties declarativeConfigProperties) Set unknownKeys = new HashSet<>(); for (String key : propertyKeys) { - ConfigProperty configProperty = ConfigProperty.fromConfigFileKey(key); + ConfigProperty configProperty = DECLARATIVE_KEYS.get(key); if (configProperty == null) { unknownKeys.add(key); diff --git a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/LogSettingParser.java b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/LogSettingParser.java index 8da5a7ca..56800995 100644 --- a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/LogSettingParser.java +++ b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/LogSettingParser.java @@ -27,7 +27,7 @@ @AutoService(DeclarativeConfigPropertyParser.class) public class LogSettingParser implements DeclarativeConfigPropertyParser { - private static final String CONFIG_KEY = "agent.logging"; + private static final String CONFIG_KEY = "logging"; @Override public LogSetting convert(DeclarativeConfigProperties declarativeConfigProperties) diff --git a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/ProxyParser.java b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/ProxyParser.java deleted file mode 100644 index eb46f2c2..00000000 --- a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/ProxyParser.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * © SolarWinds Worldwide, LLC. All rights reserved. - * - * Licensed 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 com.solarwinds.opentelemetry.extensions.config.parser.yaml; - -import com.google.auto.service.AutoService; -import com.solarwinds.joboe.config.InvalidConfigException; -import com.solarwinds.joboe.config.ProxyConfig; -import com.solarwinds.opentelemetry.extensions.config.parser.json.ProxyConfigParser; -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; - -@AutoService(DeclarativeConfigPropertyParser.class) -public class ProxyParser implements DeclarativeConfigPropertyParser { - - private static final String CONFIG_KEY = "agent.proxy"; - - @Override - public ProxyConfig convert(DeclarativeConfigProperties declarativeConfigProperties) - throws InvalidConfigException { - String proxyString = declarativeConfigProperties.getString(CONFIG_KEY); - if (proxyString == null) { - return null; - } - return ProxyConfigParser.INSTANCE.convert(proxyString); - } - - @Override - public String configKey() { - return CONFIG_KEY; - } -} diff --git a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/LogExporterComponentProvider.java b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/LogExporterComponentProvider.java index 8b41296d..6155f496 100644 --- a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/LogExporterComponentProvider.java +++ b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/LogExporterComponentProvider.java @@ -19,9 +19,8 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.DATA_TYPE_LOGS; import com.google.auto.service.AutoService; -import com.solarwinds.joboe.config.ConfigManager; -import com.solarwinds.joboe.config.ConfigProperty; import com.solarwinds.joboe.config.ProxyConfig; +import com.solarwinds.opentelemetry.extensions.config.ProxyHelper; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; @@ -35,7 +34,7 @@ @AutoService(ComponentProvider.class) public class LogExporterComponentProvider implements ComponentProvider { - public static final String COMPONENT_NAME = "swo/logExporter"; + public static final String COMPONENT_NAME = "swo_otlp"; @Override public Class getType() { @@ -67,7 +66,7 @@ public LogRecordExporter create(DeclarativeConfigProperties config) { builder::setInternalTelemetryVersion, () -> builder.setMeterProvider(MeterProvider::noop)); - ProxyConfig proxyConfig = (ProxyConfig) ConfigManager.getConfig(ConfigProperty.AGENT_PROXY); + ProxyConfig proxyConfig = ProxyHelper.toProxyConfig(config); if (proxyConfig != null) { builder.setProxyOptions( ProxyOptions.create(new InetSocketAddress(proxyConfig.getHost(), proxyConfig.getPort()))); diff --git a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/MetricExporterComponentProvider.java b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/MetricExporterComponentProvider.java index 74620069..a3efba79 100644 --- a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/MetricExporterComponentProvider.java +++ b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/MetricExporterComponentProvider.java @@ -19,10 +19,9 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.DATA_TYPE_METRICS; import com.google.auto.service.AutoService; -import com.solarwinds.joboe.config.ConfigManager; -import com.solarwinds.joboe.config.ConfigProperty; import com.solarwinds.joboe.config.ProxyConfig; import com.solarwinds.opentelemetry.extensions.DelegatingMetricExporter; +import com.solarwinds.opentelemetry.extensions.config.ProxyHelper; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; @@ -37,7 +36,7 @@ @AutoService(ComponentProvider.class) public class MetricExporterComponentProvider implements ComponentProvider { - public static final String COMPONENT_NAME = "swo/metricExporter"; + public static final String COMPONENT_NAME = "swo_otlp"; @Override public Class getType() { @@ -71,12 +70,13 @@ public MetricExporter create(DeclarativeConfigProperties config) { builder.setAggregationTemporalitySelector(AggregationTemporalitySelector.deltaPreferred()); - ProxyConfig proxyConfig = (ProxyConfig) ConfigManager.getConfig(ConfigProperty.AGENT_PROXY); + ProxyConfig proxyConfig = ProxyHelper.toProxyConfig(config); if (proxyConfig != null) { builder.setProxyOptions( ProxyOptions.create(new InetSocketAddress(proxyConfig.getHost(), proxyConfig.getPort()))); } - return new DelegatingMetricExporter(builder.build()); + return new DelegatingMetricExporter( + builder.build(), config.getBoolean("metric_export_enabled", true)); } } diff --git a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/SharedConfigCustomizerProvider.java b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/SharedConfigCustomizerProvider.java deleted file mode 100644 index 96de72d8..00000000 --- a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/SharedConfigCustomizerProvider.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * © SolarWinds Worldwide, LLC. All rights reserved. - * - * Licensed 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 com.solarwinds.opentelemetry.extensions.config.provider; - -import com.google.auto.service.AutoService; -import com.solarwinds.joboe.config.ConfigManager; -import com.solarwinds.joboe.config.ConfigProperty; -import com.solarwinds.joboe.config.InvalidConfigException; -import com.solarwinds.joboe.config.ProxyConfig; -import com.solarwinds.joboe.config.ServiceKeyUtils; -import com.solarwinds.opentelemetry.extensions.config.SolarwindsConfigResolver; -import com.solarwinds.opentelemetry.extensions.config.parser.yaml.ProxyParser; -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfiguration; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizer; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.AttributeLimitsModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.BatchLogRecordProcessorModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.BatchSpanProcessorModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.LogRecordExporterModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.LogRecordProcessorModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.LoggerProviderModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.MeterProviderModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.MetricReaderModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.PeriodicMetricReaderModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.PushMetricExporterModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SpanExporterModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SpanProcessorModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.TracerProviderModel; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -@AutoService(DeclarativeConfigurationCustomizerProvider.class) -public class SharedConfigCustomizerProvider implements DeclarativeConfigurationCustomizerProvider { - - private final String[] serviceKeyAndEndpoint = new String[2]; - - private final ProxyParser parser = new ProxyParser(); - - @Override - public void customize(DeclarativeConfigurationCustomizer customizer) { - customizer.addModelCustomizer( - configurationModel -> { - setServiceKeyAndEndpoint(configurationModel); - parseProxyConfig(configurationModel); - - configurationModel.setAttributeLimits( - new AttributeLimitsModel().setAttributeCountLimit(128)); - - MeterProviderModel meterProvider = configurationModel.getMeterProvider(); - TracerProviderModel tracerProvider = configurationModel.getTracerProvider(); - LoggerProviderModel loggerProvider = configurationModel.getLoggerProvider(); - - if (meterProvider == null) { - meterProvider = new MeterProviderModel().setReaders(Collections.emptyList()); - configurationModel.setMeterProvider(meterProvider); - - try { - ConfigManager.setConfig(ConfigProperty.AGENT_EXPORT_METRICS_ENABLED, false); - } catch (InvalidConfigException ignored) { - } - } - - addMetricExporter(configurationModel); - if (tracerProvider != null) { - addSpanExporter(configurationModel); - } - - if (loggerProvider != null) { - addLogExporter(configurationModel); - } - - return configurationModel; - }); - } - - private static Map properties(Object... keyValues) { - Map properties = new LinkedHashMap<>(); - for (int i = 0; i < keyValues.length; i += 2) { - properties.put((String) keyValues[i], keyValues[i + 1]); - } - - return properties; - } - - private void addSpanExporter(OpenTelemetryConfigurationModel model) { - TracerProviderModel tracerProvider = Objects.requireNonNull(model.getTracerProvider()); - List processors = tracerProvider.getProcessors(); - - if (processors == null) { - processors = new ArrayList<>(); - } - - boolean hasExporter = - processors.stream() - .anyMatch( - spanProcessorModel -> - spanProcessorModel.getBatch() != null - || spanProcessorModel.getSimple() != null); - - if (hasExporter) { - return; - } - - SpanProcessorModel spanProcessorModel = - new SpanProcessorModel() - .setBatch( - new BatchSpanProcessorModel() - .setExportTimeout(60000) - .setMaxQueueSize(1024) - .setMaxExportBatchSize(512) - .setExporter( - new SpanExporterModel() - .setExtensionProperty( - SpanExporterComponentProvider.COMPONENT_NAME, - properties( - "timeout", - 10000, - "protocol", - "http/protobuf", - "compression", - "gzip", - "endpoint", - serviceKeyAndEndpoint[1] + "/v1/traces", - "headers_list", - String.format( - "authorization=Bearer %s", serviceKeyAndEndpoint[0]))))); - - ArrayList spanProcessorModels = new ArrayList<>(processors); - spanProcessorModels.add(spanProcessorModel); - tracerProvider.setProcessors(spanProcessorModels); - } - - private void addMetricExporter(OpenTelemetryConfigurationModel model) { - MeterProviderModel meterProvider = model.getMeterProvider(); - List readers = Objects.requireNonNull(meterProvider).getReaders(); - - if (readers == null) { - readers = new ArrayList<>(); - } - - if (!readers.isEmpty()) { - return; - } - - model.setMeterProvider( - meterProvider.setReaders( - Collections.singletonList( - new MetricReaderModel() - .setPeriodic( - new PeriodicMetricReaderModel() - .setTimeout(30000) - .setInterval(60000) - .setExporter( - new PushMetricExporterModel() - .setExtensionProperty( - MetricExporterComponentProvider.COMPONENT_NAME, - properties( - "timeout", - 10000, - "protocol", - "http/protobuf", - "compression", - "gzip", - "endpoint", - serviceKeyAndEndpoint[1] + "/v1/metrics", - "temporality_preference", - "delta", - "default_histogram_aggregation", - "base2_exponential_bucket_histogram", - "headers_list", - String.format( - "authorization=Bearer %s", - serviceKeyAndEndpoint[0])))))))); - } - - private void addLogExporter(OpenTelemetryConfigurationModel model) { - LoggerProviderModel loggerProvider = Objects.requireNonNull(model.getLoggerProvider()); - List processors = loggerProvider.getProcessors(); - - if (processors == null) { - processors = new ArrayList<>(); - } - - boolean hasExporter = - processors.stream() - .anyMatch( - logRecordProcessorModel -> - logRecordProcessorModel.getBatch() != null - || logRecordProcessorModel.getSimple() != null); - - if (hasExporter) { - return; - } - - LogRecordProcessorModel logRecordProcessorModel = - new LogRecordProcessorModel() - .setBatch( - new BatchLogRecordProcessorModel() - .setScheduleDelay(1000) - .setMaxExportBatchSize(512) - .setMaxQueueSize(1024) - .setExportTimeout(30000) - .setExporter( - new LogRecordExporterModel() - .setExtensionProperty( - LogExporterComponentProvider.COMPONENT_NAME, - properties( - "timeout", - 10000, - "protocol", - "http/protobuf", - "compression", - "gzip", - "endpoint", - serviceKeyAndEndpoint[1] + "/v1/logs", - "headers_list", - String.format( - "authorization=Bearer %s", serviceKeyAndEndpoint[0]))))); - - ArrayList logRecordProcessorModels = new ArrayList<>(processors); - logRecordProcessorModels.add(logRecordProcessorModel); - loggerProvider.setProcessors(logRecordProcessorModels); - } - - private void setServiceKeyAndEndpoint(OpenTelemetryConfigurationModel model) { - DeclarativeConfigProperties solarwinds = getSolarwindsConfig(model); - - serviceKeyAndEndpoint[0] = - solarwinds.getString(ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), ""); - - String endpoint = solarwinds.getString(ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), ""); - endpoint = endpoint.replaceAll("https?://apm|^apm", "https://otel"); - - serviceKeyAndEndpoint[0] = ServiceKeyUtils.getApiKey(serviceKeyAndEndpoint[0]); - serviceKeyAndEndpoint[1] = endpoint; - } - - private void parseProxyConfig(OpenTelemetryConfigurationModel model) { - DeclarativeConfigProperties solarwinds = getSolarwindsConfig(model); - try { - ProxyConfig proxyConfig = parser.convert(solarwinds); - if (proxyConfig != null) { - ConfigManager.setConfig(ConfigProperty.AGENT_PROXY, proxyConfig); - } - } catch (InvalidConfigException e) { - throw new RuntimeException(e); - } - } - - private DeclarativeConfigProperties getSolarwindsConfig(OpenTelemetryConfigurationModel model) { - DeclarativeConfigProperties configProperties = - DeclarativeConfiguration.toConfigProperties(model); - - return Objects.requireNonNull( - SolarwindsConfigResolver.resolve(configProperties), - "Solarwinds configuration cannot be null."); - } -} diff --git a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/SpanExporterComponentProvider.java b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/SpanExporterComponentProvider.java index cb0effe3..f8df5993 100644 --- a/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/SpanExporterComponentProvider.java +++ b/libs/shared/src/main/java/com/solarwinds/opentelemetry/extensions/config/provider/SpanExporterComponentProvider.java @@ -19,9 +19,8 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.DATA_TYPE_TRACES; import com.google.auto.service.AutoService; -import com.solarwinds.joboe.config.ConfigManager; -import com.solarwinds.joboe.config.ConfigProperty; import com.solarwinds.joboe.config.ProxyConfig; +import com.solarwinds.opentelemetry.extensions.config.ProxyHelper; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; @@ -35,7 +34,7 @@ @AutoService(ComponentProvider.class) public class SpanExporterComponentProvider implements ComponentProvider { - public static final String COMPONENT_NAME = "swo/spanExporter"; + public static final String COMPONENT_NAME = "swo_otlp"; @Override public Class getType() { @@ -67,7 +66,7 @@ public SpanExporter create(DeclarativeConfigProperties config) { builder::setInternalTelemetryVersion, () -> builder.setMeterProvider(MeterProvider::noop)); - ProxyConfig proxyConfig = (ProxyConfig) ConfigManager.getConfig(ConfigProperty.AGENT_PROXY); + ProxyConfig proxyConfig = ProxyHelper.toProxyConfig(config); if (proxyConfig != null) { builder.setProxy( ProxyOptions.create(new InetSocketAddress(proxyConfig.getHost(), proxyConfig.getPort()))); diff --git a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/DelegatingMetricExporterTest.java b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/DelegatingMetricExporterTest.java index 2e2fa15c..33733e3e 100644 --- a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/DelegatingMetricExporterTest.java +++ b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/DelegatingMetricExporterTest.java @@ -34,18 +34,18 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) class DelegatingMetricExporterTest { - @InjectMocks private DelegatingMetricExporter tested; + private DelegatingMetricExporter tested; @Mock private MetricExporter metricExporterMock; @@ -59,6 +59,11 @@ class DelegatingMetricExporterTest { @Mock private Aggregation aggregationMock; + @BeforeEach + void setUp() { + tested = new DelegatingMetricExporter(metricExporterMock); + } + @Test void verifyGetDefaultAggregationIsDelegated() { when(metricExporterMock.getDefaultAggregation(any())).thenReturn(aggregationMock); @@ -116,6 +121,36 @@ void verifyThatOnlySwoMetricDataAreExported() throws InvalidConfigException { ConfigManager.removeConfig(ConfigProperty.AGENT_EXPORT_METRICS_ENABLED); } + @Test + void verifyThatOnlySwoMetricDataAreExportedWhenMetricExportDisabled() { + tested = new DelegatingMetricExporter(metricExporterMock, false); + when(metricExporterMock.export(any())).thenReturn(CompletableResultCode.ofSuccess()); + + when(metricDataMock.getInstrumentationScopeInfo()) + .thenReturn(InstrumentationScopeInfo.create(MeterProvider.requestMeterScopeName)); + when(metricDataMock0.getInstrumentationScopeInfo()) + .thenReturn(InstrumentationScopeInfo.create(MeterProvider.samplingMeterScopeName)); + when(metricDataMock1.getInstrumentationScopeInfo()) + .thenReturn(InstrumentationScopeInfo.create("test-scope")); + + tested.export(Arrays.asList(metricDataMock, metricDataMock0, metricDataMock1)); + verify(metricExporterMock).export(metricData.capture()); + assertEquals(Arrays.asList(metricDataMock, metricDataMock0), metricData.getValue()); + } + + @Test + void verifyThatConfigOverridesMetricExportDisabled() throws InvalidConfigException { + ConfigManager.setConfig(ConfigProperty.AGENT_EXPORT_METRICS_ENABLED, true); + tested = new DelegatingMetricExporter(metricExporterMock, false); + when(metricExporterMock.export(any())).thenReturn(CompletableResultCode.ofSuccess()); + + List metrics = Arrays.asList(metricDataMock, metricDataMock1); + tested.export(metrics); + verify(metricExporterMock).export(metrics); + + ConfigManager.removeConfig(ConfigProperty.AGENT_EXPORT_METRICS_ENABLED); + } + @Test void verifyThatFlushIsDelegated() { tested.flush(); diff --git a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/ProxyHelperTest.java b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/ProxyHelperTest.java index 31f2b95c..b02c21e9 100644 --- a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/ProxyHelperTest.java +++ b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/ProxyHelperTest.java @@ -18,12 +18,19 @@ import static com.solarwinds.opentelemetry.extensions.SharedNames.SW_OTEL_PROXY_HOST_KEY; import static com.solarwinds.opentelemetry.extensions.SharedNames.SW_OTEL_PROXY_PORT_KEY; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; +import com.solarwinds.joboe.config.ProxyConfig; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfiguration; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -55,4 +62,38 @@ void shouldReturnFalseWhenProxyPortMissing() { assertFalse(ProxyHelper.isProxyConfigured(configProperties)); } + + @Test + void shouldReturnProxyConfigWhenHostAndPortPresent() { + ProxyConfig proxyConfig = + ProxyHelper.toProxyConfig( + fromYaml("swo_otlp:\n proxy:\n host: proxy.local\n port: 3128\n")); + + assertEquals("proxy.local", proxyConfig.getHost()); + assertEquals(3128, proxyConfig.getPort()); + + assertNull(proxyConfig.getUsername()); + assertNull(proxyConfig.getPassword()); + } + + @Test + void shouldReturnNullWhenProxyAbsent() { + assertNull(ProxyHelper.toProxyConfig(fromYaml("swo_otlp:\n endpoint: https://localhost\n"))); + } + + @Test + void shouldReturnNullWhenProxyHostAbsent() { + assertNull(ProxyHelper.toProxyConfig(fromYaml("swo_otlp:\n proxy:\n port: 3128\n"))); + } + + @Test + void shouldReturnNullWhenProxyPortAbsent() { + assertNull(ProxyHelper.toProxyConfig(fromYaml("swo_otlp:\n proxy:\n host: proxy.local\n"))); + } + + private DeclarativeConfigProperties fromYaml(String yaml) { + return DeclarativeConfiguration.toConfigProperties( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))) + .getStructured("swo_otlp"); + } } diff --git a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/SolarwindsConfigResolverTest.java b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/SolarwindsConfigResolverTest.java deleted file mode 100644 index 1b5b182e..00000000 --- a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/SolarwindsConfigResolverTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * © SolarWinds Worldwide, LLC. All rights reserved. - * - * Licensed 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 com.solarwinds.opentelemetry.extensions.config; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfiguration; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.DistributionModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.ExperimentalInstrumentationModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.ExperimentalLanguageSpecificInstrumentationModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.ExperimentalLanguageSpecificInstrumentationPropertyModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.OpenTelemetryConfigurationModelAccessor; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import org.junit.jupiter.api.Test; - -class SolarwindsConfigResolverTest { - - @Test - void resolveReturnsNullWhenNeitherNodePresent() { - assertNull(resolve(new OpenTelemetryConfigurationModel())); - } - - @Test - void resolveReturnsDistributionWhenOnlyDistributionPresent() { - DeclarativeConfigProperties resolved = - resolve(model(distribution("service-key", "distribution.collector"), null)); - - assertNotNull(resolved); - assertEquals("service-key", resolved.getString("agent.serviceKey")); - assertEquals("distribution.collector", resolved.getString("agent.collector")); - } - - @Test - void resolveReturnsInstrumentationWhenOnlyInstrumentationPresent() { - DeclarativeConfigProperties resolved = - resolve(model(null, instrumentation("service-key", "instrumentation.collector"))); - - assertNotNull(resolved); - assertEquals("service-key", resolved.getString("agent.serviceKey")); - assertEquals("instrumentation.collector", resolved.getString("agent.collector")); - } - - @Test - void presentButEmptyDistributionNodeIsHonoredAndDefersEveryKeyToInstrumentation() { - DeclarativeConfigProperties resolved = - resolve( - model( - new DistributionModel().setExtensionProperty("solarwinds", new HashMap<>()), - instrumentation("service-key", "instrumentation.collector"))); - - assertNotNull(resolved); - assertEquals("service-key", resolved.getString("agent.serviceKey")); - assertEquals("instrumentation.collector", resolved.getString("agent.collector")); - } - - @Test - void distributionWinsPerKeyAndInstrumentationFillsTheRest() { - DeclarativeConfigProperties resolved = - resolve( - model( - new DistributionModel() - .setExtensionProperty( - "solarwinds", - Collections.singletonMap("agent.collector", "distribution.collector")), - instrumentation("instrumentation-key", "instrumentation.collector"))); - - assertNotNull(resolved); - assertEquals("distribution.collector", resolved.getString("agent.collector")); - assertEquals("instrumentation-key", resolved.getString("agent.serviceKey")); - } - - @Test - void getPropertyKeysIsTheUnionOfBothNodes() { - DeclarativeConfigProperties resolved = - resolve( - model( - new DistributionModel() - .setExtensionProperty( - "solarwinds", - Collections.singletonMap("agent.collector", "distribution.collector")), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - "agent.serviceKey", "instrumentation-key"))))); - - assertNotNull(resolved); - assertTrue(resolved.getPropertyKeys().contains("agent.collector")); - assertTrue(resolved.getPropertyKeys().contains("agent.serviceKey")); - } - - private static OpenTelemetryConfigurationModel model( - DistributionModel distribution, ExperimentalInstrumentationModel instrumentation) { - OpenTelemetryConfigurationModel model = new OpenTelemetryConfigurationModel(); - if (distribution != null) { - model.setDistribution(distribution); - } - - if (instrumentation != null) { - OpenTelemetryConfigurationModelAccessor.setInstrumentation(model, instrumentation); - } - return model; - } - - private static DeclarativeConfigProperties resolve(OpenTelemetryConfigurationModel model) { - return SolarwindsConfigResolver.resolve(DeclarativeConfiguration.toConfigProperties(model)); - } - - private static DistributionModel distribution(String serviceKey, String collector) { - Map properties = new HashMap<>(); - properties.put("agent.serviceKey", serviceKey); - properties.put("agent.collector", collector); - return new DistributionModel().setExtensionProperty("solarwinds", properties); - } - - private static ExperimentalInstrumentationModel instrumentation( - String serviceKey, String collector) { - return new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty("agent.serviceKey", serviceKey) - .setAdditionalProperty("agent.collector", collector))); - } -} diff --git a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/DeclarativeConfigParserTest.java b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/DeclarativeConfigParserTest.java index 503bf2b6..1d7a1f60 100644 --- a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/DeclarativeConfigParserTest.java +++ b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/DeclarativeConfigParserTest.java @@ -17,6 +17,7 @@ package com.solarwinds.opentelemetry.extensions.config.parser.yaml; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -36,6 +37,8 @@ import java.util.Set; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -53,65 +56,30 @@ void throwForUnknownConfig() { assertThrows(InvalidConfigException.class, () -> parser.parse(declarativeConfigPropertiesMock)); } - @Test - void testStringParsing() throws InvalidConfigException { - ConfigContainer configContainer = new ConfigContainer(); - DeclarativeConfigParser parser = new DeclarativeConfigParser(configContainer); - Set keys = new HashSet<>(Collections.singletonList("agent.serviceKey")); - - when(declarativeConfigPropertiesMock.getPropertyKeys()).thenReturn(keys); - when(declarativeConfigPropertiesMock.getString(eq("agent.serviceKey"))) - .thenReturn("token:service"); - - parser.parse(declarativeConfigPropertiesMock); - assertEquals("token:service", configContainer.get(ConfigProperty.AGENT_SERVICE_KEY)); - } - - @Test - void testBooleanParsing() throws InvalidConfigException { - ConfigContainer configContainer = new ConfigContainer(); - DeclarativeConfigParser parser = new DeclarativeConfigParser(configContainer); - Set keys = new HashSet<>(Collections.singletonList("agent.sqlTagPrepared")); - - when(declarativeConfigPropertiesMock.getPropertyKeys()).thenReturn(keys); - when(declarativeConfigPropertiesMock.getBoolean(eq("agent.sqlTagPrepared"))).thenReturn(false); - - parser.parse(declarativeConfigPropertiesMock); - assertEquals(false, configContainer.get(ConfigProperty.AGENT_SQL_TAG_PREPARED)); - } - - @Test - void testIntegerParsing() throws InvalidConfigException { - ConfigContainer configContainer = new ConfigContainer(); - DeclarativeConfigParser parser = new DeclarativeConfigParser(configContainer); - Set keys = new HashSet<>(Collections.singletonList("agent.collectorTimeout")); - - when(declarativeConfigPropertiesMock.getPropertyKeys()).thenReturn(keys); - when(declarativeConfigPropertiesMock.getInt(eq("agent.collectorTimeout"))).thenReturn(10); - - parser.parse(declarativeConfigPropertiesMock); - assertEquals(10, configContainer.get(ConfigProperty.AGENT_COLLECTOR_TIMEOUT)); - } - - @Test - void testLongParsing() throws InvalidConfigException { + @ParameterizedTest + @ValueSource( + strings = { + "agent.logging", + "agent.serviceKey", + "agent.collector", + "agent.sqlTagPrepared", + "agent.collectorTimeout", + "agent.configFileWatchPeriod" + }) + void throwForKeysOtherThanLogging(String key) { ConfigContainer configContainer = new ConfigContainer(); DeclarativeConfigParser parser = new DeclarativeConfigParser(configContainer); - Set keys = new HashSet<>(Collections.singletonList("agent.configFileWatchPeriod")); - when(declarativeConfigPropertiesMock.getPropertyKeys()).thenReturn(keys); - when(declarativeConfigPropertiesMock.getLong(eq("agent.configFileWatchPeriod"))) - .thenReturn(1000000L); - - parser.parse(declarativeConfigPropertiesMock); - assertEquals(1000000L, configContainer.get(ConfigProperty.AGENT_CONFIG_FILE_WATCH_PERIOD)); + when(declarativeConfigPropertiesMock.getPropertyKeys()).thenReturn(Collections.singleton(key)); + assertThrows(InvalidConfigException.class, () -> parser.parse(declarativeConfigPropertiesMock)); + assertNull(configContainer.get(ConfigProperty.fromConfigFileKey(key))); } @Test void testStructuredParsing() throws InvalidConfigException { ConfigContainer configContainer = new ConfigContainer(); DeclarativeConfigParser parser = new DeclarativeConfigParser(configContainer); - Set keys = new HashSet<>(Collections.singletonList("agent.logging")); + Set keys = new HashSet<>(Collections.singletonList("logging")); when(declarativeConfigPropertiesMock.getPropertyKeys()).thenReturn(keys); when(declarativeConfigPropertiesMock.getStructured(any(), any())) @@ -121,8 +89,7 @@ void testStructuredParsing() throws InvalidConfigException { when(declarativeConfigPropertiesMock.getString(eq("level"), any())).thenReturn("debug"); parser.parse(declarativeConfigPropertiesMock); - verify(declarativeConfigPropertiesMock, atLeastOnce()) - .getStructured(eq("agent.logging"), any()); + verify(declarativeConfigPropertiesMock, atLeastOnce()).getStructured(eq("logging"), any()); LogSetting logSetting = (LogSetting) configContainer.get(ConfigProperty.AGENT_LOGGING); assertEquals(Logger.Level.DEBUG, logSetting.getLevel()); diff --git a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/LogSettingParserTest.java b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/LogSettingParserTest.java index c0079692..25e8f151 100644 --- a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/LogSettingParserTest.java +++ b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/LogSettingParserTest.java @@ -40,8 +40,7 @@ static void setup() { DeclarativeConfiguration.toConfigProperties(resourceAsStream); declarativeConfigProperties = configProperties - .getStructured("instrumentation/development", DeclarativeConfigProperties.empty()) - .getStructured("java", DeclarativeConfigProperties.empty()) + .getStructured("distribution", DeclarativeConfigProperties.empty()) .getStructured("solarwinds"); } catch (IOException e) { throw new RuntimeException(e); @@ -56,6 +55,6 @@ void testConvert() throws InvalidConfigException { @Test void configKey() { - assertEquals("agent.logging", tested.configKey()); + assertEquals("logging", tested.configKey()); } } diff --git a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/ProxyParserTest.java b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/ProxyParserTest.java deleted file mode 100644 index dfff90ad..00000000 --- a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/parser/yaml/ProxyParserTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * © SolarWinds Worldwide, LLC. All rights reserved. - * - * Licensed 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 com.solarwinds.opentelemetry.extensions.config.parser.yaml; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.solarwinds.joboe.config.InvalidConfigException; -import com.solarwinds.joboe.config.ProxyConfig; -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import org.junit.jupiter.api.Test; - -class ProxyParserTest { - - private final ProxyParser tested = new ProxyParser(); - - @Test - void testConfigKey() { - assertEquals("agent.proxy", tested.configKey()); - } - - @Test - void testConvertReturnsNullWhenProxyStringIsNull() throws InvalidConfigException { - DeclarativeConfigProperties configProperties = mock(DeclarativeConfigProperties.class); - when(configProperties.getString("agent.proxy")).thenReturn(null); - - ProxyConfig result = tested.convert(configProperties); - - assertNull(result); - } - - @Test - void testConvertReturnsProxyConfigWithHostAndPort() throws InvalidConfigException { - DeclarativeConfigProperties configProperties = mock(DeclarativeConfigProperties.class); - when(configProperties.getString("agent.proxy")).thenReturn("http://proxy.example.com:8080"); - - ProxyConfig result = tested.convert(configProperties); - - assertEquals("proxy.example.com", result.getHost()); - assertEquals(8080, result.getPort()); - assertNull(result.getUsername()); - assertNull(result.getPassword()); - } - - @Test - void testConvertReturnsProxyConfigWithCredentials() throws InvalidConfigException { - DeclarativeConfigProperties configProperties = mock(DeclarativeConfigProperties.class); - when(configProperties.getString("agent.proxy")) - .thenReturn("http://user:password@proxy.example.com:3128"); - - ProxyConfig result = tested.convert(configProperties); - - assertEquals("proxy.example.com", result.getHost()); - assertEquals(3128, result.getPort()); - assertEquals("user", result.getUsername()); - assertEquals("password", result.getPassword()); - } -} diff --git a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/ExporterConfigs.java b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/ExporterConfigs.java new file mode 100644 index 00000000..45d5599d --- /dev/null +++ b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/ExporterConfigs.java @@ -0,0 +1,45 @@ +/* + * © SolarWinds Worldwide, LLC. All rights reserved. + * + * Licensed 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 com.solarwinds.opentelemetry.extensions.config.provider; + +import static org.mockito.AdditionalAnswers.delegatesTo; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ExtendedDeclarativeConfigProperties; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; + +final class ExporterConfigs { + + private ExporterConfigs() {} + + static DeclarativeConfigProperties exporterConfig(String yaml) { + DeclarativeConfigProperties exporter = + DeclarativeConfiguration.toConfigProperties( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))) + .getStructured("swo_otlp"); + + ExtendedDeclarativeConfigProperties config = + mock(ExtendedDeclarativeConfigProperties.class, delegatesTo(exporter)); + doReturn(ConfigProvider.noop()).when(config).getConfigProvider(); + return config; + } +} diff --git a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/LogExporterComponentProviderTest.java b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/LogExporterComponentProviderTest.java index 8b1f52ee..77b426e0 100644 --- a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/LogExporterComponentProviderTest.java +++ b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/LogExporterComponentProviderTest.java @@ -16,7 +16,9 @@ package com.solarwinds.opentelemetry.extensions.config.provider; +import static com.solarwinds.opentelemetry.extensions.config.provider.ExporterConfigs.exporterConfig; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import io.opentelemetry.sdk.logs.export.LogRecordExporter; import org.junit.jupiter.api.Test; @@ -26,11 +28,39 @@ class LogExporterComponentProviderTest { @Test void testName() { - assertEquals("swo/logExporter", tested.getName()); + assertEquals("swo_otlp", tested.getName()); } @Test void testGetType() { assertEquals(LogRecordExporter.class, tested.getType()); } + + @Test + void testCreateWithProxy() { + LogRecordExporter exporter = + tested.create( + exporterConfig( + "swo_otlp:\n" + + " endpoint: https://localhost:4318/v1/logs\n" + + " proxy:\n" + + " host: localhost\n" + + " port: 3128\n")); + + String description = exporter.toString(); + exporter.shutdown(); + + assertTrue(description.contains("proxyOptions=ProxyOptions"), description); + assertTrue(description.contains(":3128}"), description); + } + + @Test + void testCreateWithoutProxy() { + LogRecordExporter exporter = + tested.create(exporterConfig("swo_otlp:\n endpoint: https://localhost:4318/v1/logs\n")); + + String description = exporter.toString(); + exporter.shutdown(); + assertTrue(description.contains("proxyOptions=null"), description); + } } diff --git a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/MetricExporterComponentProviderTest.java b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/MetricExporterComponentProviderTest.java index f4bf70be..707977fd 100644 --- a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/MetricExporterComponentProviderTest.java +++ b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/MetricExporterComponentProviderTest.java @@ -16,21 +16,75 @@ package com.solarwinds.opentelemetry.extensions.config.provider; +import static com.solarwinds.opentelemetry.extensions.config.provider.ExporterConfigs.exporterConfig; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mockConstruction; +import com.solarwinds.opentelemetry.extensions.DelegatingMetricExporter; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.metrics.export.MetricExporter; +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; class MetricExporterComponentProviderTest { private final MetricExporterComponentProvider tested = new MetricExporterComponentProvider(); @Test void testName() { - assertEquals("swo/metricExporter", tested.getName()); + assertEquals("swo_otlp", tested.getName()); } @Test void testGetType() { assertEquals(MetricExporter.class, tested.getType()); } + + @Test + void testCreateWithProxyAndMetricExportDisabled() { + List arguments = + createAndCaptureArguments( + "swo_otlp:\n" + + " endpoint: https://localhost:4318/v1/metrics\n" + + " metric_export_enabled: false\n" + + " proxy:\n" + + " host: localhost\n" + + " port: 3128\n"); + + String description = arguments.get(0).toString(); + + assertTrue(description.contains("proxyOptions=ProxyOptions"), description); + assertTrue(description.contains(":3128}"), description); + assertEquals(false, arguments.get(1)); + } + + @Test + void testCreateWithDefaults() { + List arguments = + createAndCaptureArguments("swo_otlp:\n endpoint: https://localhost:4318/v1/metrics\n"); + + String description = arguments.get(0).toString(); + assertTrue(description.contains("proxyOptions=null"), description); + assertEquals(true, arguments.get(1)); + } + + private List createAndCaptureArguments(String yaml) { + DeclarativeConfigProperties config = exporterConfig(yaml); + List> arguments = new ArrayList<>(); + + try (MockedConstruction construction = + mockConstruction( + DelegatingMetricExporter.class, + (mock, context) -> arguments.add(context.arguments()))) { + tested.create(config); + + assertEquals(1, construction.constructed().size()); + } + + MetricExporter delegate = (MetricExporter) arguments.get(0).get(0); + delegate.shutdown(); + return arguments.get(0); + } } diff --git a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/SharedConfigCustomizerProviderTest.java b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/SharedConfigCustomizerProviderTest.java deleted file mode 100644 index 6c53c7c3..00000000 --- a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/SharedConfigCustomizerProviderTest.java +++ /dev/null @@ -1,846 +0,0 @@ -/* - * © SolarWinds Worldwide, LLC. All rights reserved. - * - * Licensed 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 com.solarwinds.opentelemetry.extensions.config.provider; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.doNothing; - -import com.solarwinds.joboe.config.ConfigManager; -import com.solarwinds.joboe.config.ConfigProperty; -import com.solarwinds.opentelemetry.extensions.SharedNames; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizer; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.AttributeLimitsModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.BatchLogRecordProcessorModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.BatchSpanProcessorModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.DistributionModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.LogRecordExporterModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.LogRecordProcessorModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.LoggerProviderModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.MeterProviderModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.MetricReaderModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.PeriodicMetricReaderModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SamplerModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SimpleLogRecordProcessorModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SimpleSpanProcessorModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SpanExporterModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SpanProcessorModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.TracerProviderModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.ExperimentalInstrumentationModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.ExperimentalLanguageSpecificInstrumentationModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.ExperimentalLanguageSpecificInstrumentationPropertyModel; -import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.OpenTelemetryConfigurationModelAccessor; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -@SuppressWarnings("all") -@ExtendWith(MockitoExtension.class) -class SharedConfigCustomizerProviderTest { - - @InjectMocks private SharedConfigCustomizerProvider tested; - - @Mock private DeclarativeConfigurationCustomizer declarativeConfigurationCustomizerMock; - - @Captor - private ArgumentCaptor> - functionArgumentCaptor; - - @Test - void testCustomize() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setTracerProvider(new TracerProviderModel().setProcessors(Collections.emptyList())) - .setLoggerProvider( - new LoggerProviderModel().setProcessors(Collections.emptyList())), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.collector.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - AttributeLimitsModel attributeLimits = openTelemetryConfigurationModel.getAttributeLimits(); - assertEquals(new AttributeLimitsModel().setAttributeCountLimit(128), attributeLimits); - - TracerProviderModel tracerProvider = openTelemetryConfigurationModel.getTracerProvider(); - assertNotNull(tracerProvider); - - assertNull(tracerProvider.getSampler()); - assertEquals(1, tracerProvider.getProcessors().size()); - - PeriodicMetricReaderModel periodic = - openTelemetryConfigurationModel.getMeterProvider().getReaders().get(0).getPeriodic(); - - assertNotNull(periodic); - assertNotNull( - periodic - .getExporter() - .getExtensionProperties() - .get(MetricExporterComponentProvider.COMPONENT_NAME)); - - LoggerProviderModel loggerProvider = openTelemetryConfigurationModel.getLoggerProvider(); - LogRecordProcessorModel logRecordProcessorModel = loggerProvider.getProcessors().get(0); - BatchLogRecordProcessorModel batch = logRecordProcessorModel.getBatch(); - - assertNotNull(batch); - LogRecordExporterModel exporter = batch.getExporter(); - Map logExporterProperty = - asMap(exporter.getExtensionProperties().get(LogExporterComponentProvider.COMPONENT_NAME)); - - assertNotNull(logExporterProperty); - Map logConfigs = logExporterProperty; - assertEquals("https://otel.collector.com/v1/logs", logConfigs.get("endpoint")); - assertEquals("authorization=Bearer token", logConfigs.get("headers_list")); - assertEquals("gzip", logConfigs.get("compression")); - } - - @Test - void testCustomize1() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setTracerProvider( - new TracerProviderModel().setProcessors(Collections.emptyList())), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.collector.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - AttributeLimitsModel attributeLimits = openTelemetryConfigurationModel.getAttributeLimits(); - assertEquals(new AttributeLimitsModel().setAttributeCountLimit(128), attributeLimits); - - TracerProviderModel tracerProvider = openTelemetryConfigurationModel.getTracerProvider(); - assertNotNull(tracerProvider); - - assertNull(tracerProvider.getSampler()); - assertEquals(1, tracerProvider.getProcessors().size()); - - PeriodicMetricReaderModel periodic = - openTelemetryConfigurationModel.getMeterProvider().getReaders().get(0).getPeriodic(); - - Map configs = - asMap( - periodic - .getExporter() - .getExtensionProperties() - .get(MetricExporterComponentProvider.COMPONENT_NAME)); - - assertNotNull(configs); - assertEquals(10000, configs.get("timeout")); - assertEquals("http/protobuf", configs.get("protocol")); - assertEquals("gzip", configs.get("compression")); - - assertEquals("https://otel.collector.com/v1/metrics", configs.get("endpoint")); - assertEquals("delta", configs.get("temporality_preference")); - assertEquals( - "base2_exponential_bucket_histogram", configs.get("default_histogram_aggregation")); - - assertEquals("authorization=Bearer token", configs.get("headers_list")); - } - - @Test - void customizeShouldNotSetMetricReaderWhenOneIsSpecified() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setMeterProvider( - new MeterProviderModel() - .setReaders(Collections.singletonList(new MetricReaderModel()))), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.collector.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - MeterProviderModel meterProvider = openTelemetryConfigurationModel.getMeterProvider(); - MetricReaderModel metricReaderModel = meterProvider.getReaders().get(0); - assertNull(metricReaderModel.getPeriodic()); - } - - @Test - void customizeShouldNotSetTraceExporterWhenBatchIsSpecified() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setTracerProvider( - new TracerProviderModel() - .setProcessors( - Collections.singletonList( - new SpanProcessorModel() - .setBatch( - new BatchSpanProcessorModel() - .setExporter(new SpanExporterModel()))))), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.collector.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - TracerProviderModel tracerProvider = openTelemetryConfigurationModel.getTracerProvider(); - SpanProcessorModel spanProcessorModel = tracerProvider.getProcessors().get(0); - BatchSpanProcessorModel batch = spanProcessorModel.getBatch(); - - assertNotNull(batch); - SpanExporterModel exporter = batch.getExporter(); - Map spanExporterPropertyModel = - asMap(exporter.getExtensionProperties().get(SpanExporterComponentProvider.COMPONENT_NAME)); - - assertNull(spanExporterPropertyModel); - } - - @Test - void customizeShouldNotSetTraceExporterWhenSimpleIsSpecified() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setTracerProvider( - new TracerProviderModel() - .setProcessors( - Collections.singletonList( - new SpanProcessorModel() - .setSimple( - new SimpleSpanProcessorModel() - .setExporter(new SpanExporterModel()))))), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.collector.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - TracerProviderModel tracerProvider = openTelemetryConfigurationModel.getTracerProvider(); - SpanProcessorModel spanProcessorModel = tracerProvider.getProcessors().get(0); - SimpleSpanProcessorModel simple = spanProcessorModel.getSimple(); - - assertNotNull(simple); - SpanExporterModel exporter = simple.getExporter(); - Map spanExporterPropertyModel = - asMap(exporter.getExtensionProperties().get(SpanExporterComponentProvider.COMPONENT_NAME)); - - assertNull(spanExporterPropertyModel); - } - - @Test - void testCustomize2() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setLoggerProvider( - new LoggerProviderModel().setProcessors(Collections.emptyList())), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "http://apm.collector.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - LoggerProviderModel loggerProvider = openTelemetryConfigurationModel.getLoggerProvider(); - LogRecordProcessorModel logRecordProcessorModel = loggerProvider.getProcessors().get(0); - BatchLogRecordProcessorModel batch = logRecordProcessorModel.getBatch(); - - assertNotNull(batch); - LogRecordExporterModel exporter = batch.getExporter(); - Map logExporterProperty = - asMap(exporter.getExtensionProperties().get(LogExporterComponentProvider.COMPONENT_NAME)); - - assertNotNull(logExporterProperty); - Map logConfigs = logExporterProperty; - assertEquals("https://otel.collector.com/v1/logs", logConfigs.get("endpoint")); - assertEquals("authorization=Bearer token", logConfigs.get("headers_list")); - } - - @Test - void customizeShouldNotSetLogExporterWhenBatchIsSpecified() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setLoggerProvider( - new LoggerProviderModel() - .setProcessors( - Collections.singletonList( - new LogRecordProcessorModel() - .setBatch( - new BatchLogRecordProcessorModel() - .setExporter(new LogRecordExporterModel()))))), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "http://apm.collector.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - LoggerProviderModel loggerProvider = openTelemetryConfigurationModel.getLoggerProvider(); - LogRecordProcessorModel logRecordProcessorModel = loggerProvider.getProcessors().get(0); - BatchLogRecordProcessorModel batch = logRecordProcessorModel.getBatch(); - - assertNotNull(batch); - LogRecordExporterModel exporter = batch.getExporter(); - Map logExporterProperty = - asMap(exporter.getExtensionProperties().get(LogExporterComponentProvider.COMPONENT_NAME)); - - assertNull(logExporterProperty); - } - - @Test - void customizeShouldNotSetLogExporterWhenSimpleIsSpecified() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setLoggerProvider( - new LoggerProviderModel() - .setProcessors( - Collections.singletonList( - new LogRecordProcessorModel() - .setSimple( - new SimpleLogRecordProcessorModel() - .setExporter(new LogRecordExporterModel()))))), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "http://apm.collector.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - LoggerProviderModel loggerProvider = openTelemetryConfigurationModel.getLoggerProvider(); - LogRecordProcessorModel logRecordProcessorModel = loggerProvider.getProcessors().get(0); - SimpleLogRecordProcessorModel simple = logRecordProcessorModel.getSimple(); - - assertNotNull(simple); - LogRecordExporterModel exporter = simple.getExporter(); - Map logExporterProperty = - asMap(exporter.getExtensionProperties().get(LogExporterComponentProvider.COMPONENT_NAME)); - - assertNull(logExporterProperty); - } - - @Test - void UrlShouldNotChangeWhenNotApm() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setLoggerProvider( - new LoggerProviderModel().setProcessors(Collections.emptyList())), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "http://example.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - LoggerProviderModel loggerProvider = openTelemetryConfigurationModel.getLoggerProvider(); - LogRecordProcessorModel logRecordProcessorModel = loggerProvider.getProcessors().get(0); - BatchLogRecordProcessorModel batch = logRecordProcessorModel.getBatch(); - - assertNotNull(batch); - LogRecordExporterModel exporter = batch.getExporter(); - Map logExporterProperty = - asMap(exporter.getExtensionProperties().get(LogExporterComponentProvider.COMPONENT_NAME)); - - assertNotNull(logExporterProperty); - Map logConfigs = logExporterProperty; - assertEquals("http://example.com/v1/logs", logConfigs.get("endpoint")); - assertEquals("authorization=Bearer token", logConfigs.get("headers_list")); - } - - @Test - void UrlShouldNotChangeWhenNotApm2() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setLoggerProvider( - new LoggerProviderModel().setProcessors(Collections.emptyList())), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "http://localhost:4317")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - LoggerProviderModel loggerProvider = openTelemetryConfigurationModel.getLoggerProvider(); - LogRecordProcessorModel logRecordProcessorModel = loggerProvider.getProcessors().get(0); - BatchLogRecordProcessorModel batch = logRecordProcessorModel.getBatch(); - - assertNotNull(batch); - LogRecordExporterModel exporter = batch.getExporter(); - Map logExporterProperty = - asMap(exporter.getExtensionProperties().get(LogExporterComponentProvider.COMPONENT_NAME)); - - assertNotNull(logExporterProperty); - Map logConfigs = logExporterProperty; - assertEquals("http://localhost:4317/v1/logs", logConfigs.get("endpoint")); - assertEquals("authorization=Bearer token", logConfigs.get("headers_list")); - } - - @Test - void tracesNotConfiguredWhenTracerProviderAbsent() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setLoggerProvider( - new LoggerProviderModel().setProcessors(Collections.emptyList())), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.collector.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - assertNull(openTelemetryConfigurationModel.getTracerProvider()); - assertNotNull(openTelemetryConfigurationModel.getLoggerProvider()); - assertNotNull(openTelemetryConfigurationModel.getMeterProvider()); - } - - @Test - void customizeShouldNotOverrideSamplerWhenOneIsAlreadyConfigured() { - SamplerModel userSampler = new SamplerModel().setExtensionProperty("cel", null); - - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setTracerProvider( - new TracerProviderModel() - .setProcessors(Collections.emptyList()) - .setSampler(userSampler)), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.collector.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - TracerProviderModel tracerProvider = openTelemetryConfigurationModel.getTracerProvider(); - SamplerModel sampler = tracerProvider.getSampler(); - - assertNull(sampler.getExtensionProperties().get(SharedNames.HTTP_SAMPLER_NAME)); - assertTrue(sampler.getExtensionProperties().containsKey("cel")); - } - - @Test - void logsNotConfiguredWhenLoggerProviderAbsent() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setTracerProvider( - new TracerProviderModel().setProcessors(Collections.emptyList())), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.collector.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - assertNull(openTelemetryConfigurationModel.getLoggerProvider()); - assertNotNull(openTelemetryConfigurationModel.getTracerProvider()); - assertNull(openTelemetryConfigurationModel.getTracerProvider().getSampler()); - assertNotNull(openTelemetryConfigurationModel.getMeterProvider()); - } - - @Test - void metricsExportDisabledWhenMeterProviderAbsent() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel(), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.collector.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - assertNotNull(openTelemetryConfigurationModel.getMeterProvider()); - assertFalse((Boolean) ConfigManager.getConfig(ConfigProperty.AGENT_EXPORT_METRICS_ENABLED)); - } - - @Test - void readsSolarwindsConfigFromDistributionNode() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - new OpenTelemetryConfigurationModel() - .setLoggerProvider(new LoggerProviderModel().setProcessors(Collections.emptyList())) - .setDistribution( - new DistributionModel() - .setExtensionProperty( - "solarwinds", - new DistributionProperties() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.collector.com"))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - Map logConfigs = logExporterConfigs(openTelemetryConfigurationModel); - assertEquals("https://otel.collector.com/v1/logs", logConfigs.get("endpoint")); - assertEquals("authorization=Bearer token", logConfigs.get("headers_list")); - } - - @Test - void distributionNodeTakesPrecedenceOverInstrumentationNode() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setLoggerProvider(new LoggerProviderModel().setProcessors(Collections.emptyList())) - .setDistribution( - new DistributionModel() - .setExtensionProperty( - "solarwinds", - new DistributionProperties() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.distribution.com"))), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.instrumentation.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - Map logConfigs = logExporterConfigs(openTelemetryConfigurationModel); - assertEquals("https://otel.distribution.com/v1/logs", logConfigs.get("endpoint")); - } - - @Test - void emptyDistributionSolarwindsFallsBackToInstrumentationNode() { - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setLoggerProvider(new LoggerProviderModel().setProcessors(Collections.emptyList())) - .setDistribution( - new DistributionModel() - .setExtensionProperty("solarwinds", new DistributionProperties())), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.instrumentation.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - Map logConfigs = logExporterConfigs(openTelemetryConfigurationModel); - assertEquals("https://otel.instrumentation.com/v1/logs", logConfigs.get("endpoint")); - } - - @Test - void partialDistributionConfigIsFilledFromInstrumentationNode() { - // distribution supplies only the collector; the service key must still be picked up from the - // instrumentation node so the merged config is complete. - OpenTelemetryConfigurationModel openTelemetryConfigurationModel = - withInstrumentation( - new OpenTelemetryConfigurationModel() - .setLoggerProvider(new LoggerProviderModel().setProcessors(Collections.emptyList())) - .setDistribution( - new DistributionModel() - .setExtensionProperty( - "solarwinds", - new DistributionProperties() - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.distribution.com"))), - new ExperimentalInstrumentationModel() - .setJava( - new ExperimentalLanguageSpecificInstrumentationModel() - .setAdditionalProperty( - "solarwinds", - new ExperimentalLanguageSpecificInstrumentationPropertyModel() - .setAdditionalProperty( - ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), - "token:service") - .setAdditionalProperty( - ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), - "apm.instrumentation.com")))); - - doNothing() - .when(declarativeConfigurationCustomizerMock) - .addModelCustomizer(functionArgumentCaptor.capture()); - - tested.customize(declarativeConfigurationCustomizerMock); - functionArgumentCaptor.getValue().apply(openTelemetryConfigurationModel); - - Map logConfigs = logExporterConfigs(openTelemetryConfigurationModel); - // collector comes from distribution (wins per key)... - assertEquals("https://otel.distribution.com/v1/logs", logConfigs.get("endpoint")); - // ...and the service key is filled in from the instrumentation node. - assertEquals("authorization=Bearer token", logConfigs.get("headers_list")); - } - - private static Map logExporterConfigs( - OpenTelemetryConfigurationModel openTelemetryConfigurationModel) { - LoggerProviderModel loggerProvider = openTelemetryConfigurationModel.getLoggerProvider(); - LogRecordProcessorModel logRecordProcessorModel = loggerProvider.getProcessors().get(0); - BatchLogRecordProcessorModel batch = logRecordProcessorModel.getBatch(); - - assertNotNull(batch); - LogRecordExporterModel exporter = batch.getExporter(); - Map logExporterProperty = - asMap(exporter.getExtensionProperties().get(LogExporterComponentProvider.COMPONENT_NAME)); - - assertNotNull(logExporterProperty); - return logExporterProperty; - } - - private static OpenTelemetryConfigurationModel withInstrumentation( - OpenTelemetryConfigurationModel model, ExperimentalInstrumentationModel instrumentation) { - return OpenTelemetryConfigurationModelAccessor.setInstrumentation(model, instrumentation); - } - - @SuppressWarnings("unchecked") - private static Map asMap(Object value) { - return (Map) value; - } - - @SuppressWarnings("serial") - private static class DistributionProperties extends HashMap { - DistributionProperties setAdditionalProperty(String key, Object value) { - put(key, value); - return this; - } - } -} diff --git a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/SpanExporterComponentProviderTest.java b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/SpanExporterComponentProviderTest.java index e90f87f6..e17286a8 100644 --- a/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/SpanExporterComponentProviderTest.java +++ b/libs/shared/src/test/java/com/solarwinds/opentelemetry/extensions/config/provider/SpanExporterComponentProviderTest.java @@ -16,7 +16,9 @@ package com.solarwinds.opentelemetry.extensions.config.provider; +import static com.solarwinds.opentelemetry.extensions.config.provider.ExporterConfigs.exporterConfig; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import io.opentelemetry.sdk.trace.export.SpanExporter; import org.junit.jupiter.api.Test; @@ -26,11 +28,38 @@ class SpanExporterComponentProviderTest { @Test void testName() { - assertEquals("swo/spanExporter", tested.getName()); + assertEquals("swo_otlp", tested.getName()); } @Test void testGetType() { assertEquals(SpanExporter.class, tested.getType()); } + + @Test + void testCreateWithProxy() { + SpanExporter exporter = + tested.create( + exporterConfig( + "swo_otlp:\n" + + " endpoint: https://localhost:4318/v1/traces\n" + + " proxy:\n" + + " host: localhost\n" + + " port: 3128\n")); + + String description = exporter.toString(); + exporter.shutdown(); + assertTrue(description.contains("proxyOptions=ProxyOptions"), description); + assertTrue(description.contains(":3128}"), description); + } + + @Test + void testCreateWithoutProxy() { + SpanExporter exporter = + tested.create(exporterConfig("swo_otlp:\n endpoint: https://localhost:4318/v1/traces\n")); + + String description = exporter.toString(); + exporter.shutdown(); + assertTrue(description.contains("proxyOptions=null"), description); + } } diff --git a/libs/shared/src/test/resources/sdk-config.yaml b/libs/shared/src/test/resources/sdk-config.yaml index 0b8addc4..37269c20 100644 --- a/libs/shared/src/test/resources/sdk-config.yaml +++ b/libs/shared/src/test/resources/sdk-config.yaml @@ -96,7 +96,7 @@ meter_provider: # Configure exporter. exporter: # Configure exporter to be OTLP. - swo/metricExporter: + swo_otlp: # Configure endpoint. endpoint: https://otel.collector.na-01.st-ssp.solarwinds.com # Configure compression. @@ -145,6 +145,12 @@ logger_provider: # Configure max attribute count. Overrides .attribute_limits.attribute_count_limit. attribute_count_limit: 128 +distribution: + solarwinds: + logging: + level: trace + + instrumentation/development: java: common: @@ -155,10 +161,3 @@ instrumentation/development: sql_tag_databases: - postgresql sql_query_max_length: 4096 - solarwinds: - agent.logging: - level: trace - agent.spanStacktraceFilters: - - http.request.method - agent.serviceKey: ${SW_APM_SERVICE_KEY} - agent.collector: apm.collector.na-01.st-ssp.solarwinds.com \ No newline at end of file diff --git a/sdk-config.yaml b/sdk-config.yaml index 57a3f2d8..41952b4f 100644 --- a/sdk-config.yaml +++ b/sdk-config.yaml @@ -25,12 +25,9 @@ resource: - swo_uams: - swo_k8s: -# Provide shared config for multi-language deployment distribution: solarwinds: - agent.serviceKey: "" - agent.collector: "" - agent.logging: + logging: level: info # Configure general attribute limits. See also tracer_provider.limits, logger_provider.limits. diff --git a/smoke-tests/sdk-config.yaml b/smoke-tests/sdk-config.yaml index cac43f20..9d232ba8 100644 --- a/smoke-tests/sdk-config.yaml +++ b/smoke-tests/sdk-config.yaml @@ -27,12 +27,9 @@ resource: - swo_uams: - swo_k8s: -# Provide shared config for multi-language deployment distribution: solarwinds: - agent.serviceKey: ${SW_APM_SERVICE_KEY} - agent.collector: apm.collector.na-01.st-ssp.solarwinds.com - agent.logging: + logging: level: trace # Configure general attribute limits. See also tracer_provider.limits, logger_provider.limits. @@ -65,6 +62,11 @@ tracer_provider: - http.method - swo_profiler: enabled: true + service_key: ${SW_APM_SERVICE_KEY} + collector: apm.collector.na-01.st-ssp.solarwinds.com + proxy: + host: squid-proxy + port: 3128 interval: 20 exclude_packages: - java @@ -82,9 +84,13 @@ tracer_provider: # Configure exporter. exporter: # Configure exporter to be OTLP. - otlp_grpc: + swo_otlp: # Configure endpoint. - endpoint: https://otel.collector.na-01.st-ssp.solarwinds.com + endpoint: https://otel.collector.na-01.st-ssp.solarwinds.com/v1/traces + # Configure proxy. + proxy: + host: squid-proxy + port: 3128 # Configure compression. compression: gzip # Configure max time (in milliseconds) to wait for each export. @@ -99,6 +105,9 @@ tracer_provider: swo_http: service_key: ${SW_APM_SERVICE_KEY} collector: apm.collector.na-01.st-ssp.solarwinds.com + proxy: + host: squid-proxy + port: 3128 transaction_settings: - regex: ".*/specialties" tracing: "disabled" @@ -118,11 +127,13 @@ meter_provider: # Configure exporter. exporter: # Configure exporter to be OTLP. - swo/metricExporter: - # Configure protocol. - protocol: grpc + swo_otlp: # Configure endpoint. - endpoint: https://otel.collector.na-01.st-ssp.solarwinds.com + endpoint: https://otel.collector.na-01.st-ssp.solarwinds.com/v1/metrics + # Configure proxy. + proxy: + host: squid-proxy + port: 3128 # Configure compression. compression: gzip # Configure max time (in milliseconds) to wait for each export. @@ -151,9 +162,13 @@ logger_provider: # Configure exporter. exporter: # Configure exporter to be OTLP. - otlp_grpc: + swo_otlp: # Configure endpoint. - endpoint: https://otel.collector.na-01.st-ssp.solarwinds.com + endpoint: https://otel.collector.na-01.st-ssp.solarwinds.com/v1/logs + # Configure proxy. + proxy: + host: squid-proxy + port: 3128 # Configure compression. compression: gzip # Configure max time (in milliseconds) to wait for each export. @@ -180,6 +195,3 @@ instrumentation/development: sql_tag_prepared: true sql_tag_databases: - postgresql - solarwinds: - agent.serviceKey: ${SW_APM_SERVICE_KEY} - agent.collector: apm.collector.na-01.st-ssp.solarwinds.com diff --git a/smoke-tests/src/test/java/com/solarwinds/SmokeTest.java b/smoke-tests/src/test/java/com/solarwinds/SmokeTest.java index 8703dd0d..a5456ffc 100644 --- a/smoke-tests/src/test/java/com/solarwinds/SmokeTest.java +++ b/smoke-tests/src/test/java/com/solarwinds/SmokeTest.java @@ -104,11 +104,12 @@ static void runAppOnce(Agent agent) throws Exception { GenericContainer k6 = new K6Container(NETWORK, agent, namingConventions).build(); k6.start(); - petClinic.execInContainer("kill", "1"); - webMvc.execInContainer("kill", "1"); - webMvcAo.execInContainer("kill", "1"); + petClinic.stop(); + webMvc.stop(); + webMvcAo.stop(); - squid.execInContainer("kill", "1"); + k6.stop(); + squid.stop(); postgres.stop(); } diff --git a/smoke-tests/src/test/java/com/solarwinds/SmokeTestV2.java b/smoke-tests/src/test/java/com/solarwinds/SmokeTestV2.java index 81fba1f7..e5dd9469 100644 --- a/smoke-tests/src/test/java/com/solarwinds/SmokeTestV2.java +++ b/smoke-tests/src/test/java/com/solarwinds/SmokeTestV2.java @@ -26,6 +26,7 @@ import com.solarwinds.containers.PetClinicRestContainer; import com.solarwinds.containers.PostgresContainer; import com.solarwinds.containers.SpringBootWebMvcContainer; +import com.solarwinds.containers.SquidContainer; import com.solarwinds.results.ResultsCollector; import com.solarwinds.util.LogStreamAnalyzer; import com.solarwinds.util.NamingConventions; @@ -61,7 +62,9 @@ public class SmokeTestV2 { "Extension attached!", "trace_id=[a-z0-9]+\\s+span_id=[a-z0-9]+\\s+trace_flags=[0-9a-f]{2}", "Applying instrumentation: sw-jdbc", - "Clearing transaction name buffer. Unique transaction count: \\d+") + "Clearing transaction name buffer. Unique transaction count: \\d+", + "CONNECT otel.collector.na-01.st-ssp.solarwinds.com:443", + "CONNECT apm.collector.na-01.st-ssp.solarwinds.com:443") , new Slf4jLogConsumer(LoggerFactory.getLogger("k6"))); @@ -81,6 +84,10 @@ static void runTestConfig() { } static void runAppOnce(Agent agent) throws Exception { + GenericContainer squid = new SquidContainer(NETWORK).build(); + squid.start(); + squid.followOutput(logStreamAnalyzer); + GenericContainer webMvc = new SpringBootWebMvcContainer(new SwoAgentResolver(), NETWORK, agent).build(); webMvc.start(); webMvc.followOutput(logStreamAnalyzer); @@ -99,9 +106,12 @@ static void runAppOnce(Agent agent) throws Exception { GenericContainer k6 = new K6Container(NETWORK, agent, namingConventions).build(); k6.start(); - petClinic.execInContainer("kill", "1"); - webMvc.execInContainer("kill", "1"); - webMvcAo.execInContainer("kill", "1"); + petClinic.stop(); + webMvc.stop(); + webMvcAo.stop(); + + k6.stop(); + squid.stop(); postgres.stop(); } @@ -164,4 +174,16 @@ void assertThatRuntimeMetrics17AreExported() throws IOException { "$.root_group.checks.['otel-metrics-17'].passes"); assertTrue(passes > 0, "JVM 17+ runtime metric export is broken"); } + + @Test + void assertThatProxyIsUsed() { + Boolean actual = logStreamAnalyzer.getAnswer().get("CONNECT otel.collector.na-01.st-ssp.solarwinds.com:443"); + assertTrue(actual, "not using proxy"); + } + + @Test + void assertThatSamplerUsesProxy() { + Boolean actual = logStreamAnalyzer.getAnswer().get("CONNECT apm.collector.na-01.st-ssp.solarwinds.com:443"); + assertTrue(actual, "sampler not using proxy"); + } } From b1d3156670d339b4ea622c8912b8f01997b63312 Mon Sep 17 00:00:00 2001 From: cleverchuk Date: Mon, 2 Nov 2026 14:10:05 -0500 Subject: [PATCH 3/3] Move config merge from ConfigManager.initialize to DeclarativeLoader --- .../extensions/config/DeclarativeLoader.java | 11 ++++++++-- .../joboe/config/ConfigManager.java | 21 ++++++++----------- .../joboe/config/ConfigManagerTest.java | 21 +++++++++++++------ 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/DeclarativeLoader.java b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/DeclarativeLoader.java index 573a4695..de45c823 100644 --- a/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/DeclarativeLoader.java +++ b/custom/src/main/java/com/solarwinds/opentelemetry/extensions/config/DeclarativeLoader.java @@ -63,8 +63,15 @@ private void loadConfig(OpenTelemetryConfigurationModel configurationModel) { throw new RuntimeException(e); } - ConfigManager.initialize(configContainer); - ConfigContainer agentConfig = configContainer.subset(ConfigGroup.AGENT); + ConfigContainer configs = ConfigManager.getInstance().getConfigContainer(); + if (configs == null) { + configs = configContainer; + } else { + configs.merge(configContainer); + } + + ConfigManager.initialize(configs); + ConfigContainer agentConfig = configs.subset(ConfigGroup.AGENT); LoggerFactory.init(LoggingConfigProvider.getLoggerConfiguration(agentConfig)); logger.info("Loaded via declarative config"); diff --git a/libs/config/src/main/java/com/solarwinds/joboe/config/ConfigManager.java b/libs/config/src/main/java/com/solarwinds/joboe/config/ConfigManager.java index 87709b43..eaa56268 100644 --- a/libs/config/src/main/java/com/solarwinds/joboe/config/ConfigManager.java +++ b/libs/config/src/main/java/com/solarwinds/joboe/config/ConfigManager.java @@ -27,19 +27,16 @@ public class ConfigManager { private ConfigManager() {} - /** - * Initializes the configs. If configs were already set, {@code configs} is merged into them and - * takes precedence for keys present in both. - * - * @param configs the configs to initialize with - */ public static void initialize(ConfigContainer configs) { - if (SINGLETON.configs == null) { - SINGLETON.configs = configs; - } else { - SINGLETON.configs.merge(configs); - logger.debug("ConfigManager already initialized, merged new configs"); - } + SINGLETON.configs = configs; + } + + public static ConfigManager getInstance() { + return SINGLETON; + } + + public ConfigContainer getConfigContainer() { + return configs; } /** For internal testing - reset the states of the ConfigManager */ diff --git a/libs/config/src/test/java/com/solarwinds/joboe/config/ConfigManagerTest.java b/libs/config/src/test/java/com/solarwinds/joboe/config/ConfigManagerTest.java index 9197d035..add5b2aa 100644 --- a/libs/config/src/test/java/com/solarwinds/joboe/config/ConfigManagerTest.java +++ b/libs/config/src/test/java/com/solarwinds/joboe/config/ConfigManagerTest.java @@ -17,6 +17,8 @@ package com.solarwinds.joboe.config; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -44,13 +46,13 @@ void initializeSetsConfigsWhenUninitialized() throws InvalidConfigException { } @Test - void initializeKeepsPreviouslySetConfigs() throws InvalidConfigException { + void initializeReplacesPreviouslySetConfigs() throws InvalidConfigException { ConfigManager.setConfig(ConfigProperty.AGENT_COLLECTOR, "apm.collector.com"); ConfigContainer configs = new ConfigContainer(); configs.put(ConfigProperty.AGENT_SERVICE_KEY, "token:service"); ConfigManager.initialize(configs); - assertEquals("apm.collector.com", ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); + assertNull(ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); assertEquals("token:service", ConfigManager.getConfig(ConfigProperty.AGENT_SERVICE_KEY)); } @@ -65,10 +67,17 @@ void initializeOverridesPreviouslySetConfigsOnConflict() throws InvalidConfigExc } @Test - void initializeMergesSubsetWithMissingGroup() throws InvalidConfigException { - ConfigManager.setConfig(ConfigProperty.AGENT_COLLECTOR, "apm.collector.com"); + void getConfigsReturnsInitializedContainer() { + ConfigContainer configs = new ConfigContainer(); + ConfigManager.initialize(configs); + assertSame(configs, ConfigManager.getInstance().getConfigContainer()); + } - ConfigManager.initialize(new ConfigContainer().subset(ConfigGroup.AGENT)); - assertEquals("apm.collector.com", ConfigManager.getConfig(ConfigProperty.AGENT_COLLECTOR)); + @Test + void getConfigsReturnsNullAfterReset() { + ConfigManager.initialize(new ConfigContainer()); + + ConfigManager.reset(); + assertNull(ConfigManager.getInstance().getConfigContainer()); } }