Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* © 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.ConfigParser;
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;

@SuppressWarnings("rawtypes")
@AutoService(ConfigParser.class)
public class ProxyParser implements ConfigParser<DeclarativeConfigProperties, ProxyConfig> {

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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* © 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 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 io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder;
import io.opentelemetry.exporter.otlp.internal.OtlpDeclarativeConfigUtil;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import java.net.InetSocketAddress;

@AutoService(ComponentProvider.class)
public class LogExporterComponentProvider implements ComponentProvider {

public static final String COMPONENT_NAME = "swo/logExporter";

@Override
public Class<LogRecordExporter> getType() {
return LogRecordExporter.class;
}

@Override
public String getName() {
return COMPONENT_NAME;
}

@Override
public LogRecordExporter create(DeclarativeConfigProperties config) {
OtlpHttpLogRecordExporterBuilder builder = OtlpHttpLogRecordExporter.builder();

OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_LOGS,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,
builder::setTimeout,
builder::setTrustedCertificates,
builder::setClientTls,
builder::setRetryPolicy,
builder::setMemoryMode,
true);

ProxyConfig proxyConfig = (ProxyConfig) ConfigManager.getConfig(ConfigProperty.AGENT_PROXY);
if (proxyConfig != null) {
builder.setProxyOptions(
ProxyOptions.create(new InetSocketAddress(proxyConfig.getHost(), proxyConfig.getPort())));
}

return builder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@
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 io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder;
import io.opentelemetry.exporter.otlp.internal.OtlpDeclarativeConfigUtil;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import java.net.InetSocketAddress;

@AutoService(ComponentProvider.class)
public class MetricExporterComponentProvider implements ComponentProvider {
Expand All @@ -45,7 +50,7 @@ public String getName() {

@Override
public MetricExporter create(DeclarativeConfigProperties config) {
OtlpGrpcMetricExporterBuilder builder = OtlpGrpcMetricExporter.builder();
OtlpHttpMetricExporterBuilder builder = OtlpHttpMetricExporter.builder();

OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_METRICS,
Expand All @@ -59,9 +64,16 @@ public MetricExporter create(DeclarativeConfigProperties config) {
builder::setClientTls,
builder::setRetryPolicy,
builder::setMemoryMode,
false);
true);

builder.setAggregationTemporalitySelector(AggregationTemporalitySelector.deltaPreferred());

ProxyConfig proxyConfig = (ProxyConfig) ConfigManager.getConfig(ConfigProperty.AGENT_PROXY);
if (proxyConfig != null) {
builder.setProxyOptions(
ProxyOptions.create(new InetSocketAddress(proxyConfig.getHost(), proxyConfig.getPort())));
}

return new DelegatingMetricExporter(builder.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@
import static com.solarwinds.opentelemetry.extensions.SharedNames.SPAN_STACKTRACE_FILTER_CLASS;

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.parser.yaml.ProxyParser;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration;
import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer;
Expand All @@ -29,19 +33,20 @@
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchLogRecordProcessorModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchSpanProcessorModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordExporterModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordExporterPropertyModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordProcessorModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LoggerProviderModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.MeterProviderModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.MetricReaderModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpGrpcExporterModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PeriodicMetricReaderModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PropagatorModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PushMetricExporterModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PushMetricExporterPropertyModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SamplerModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SamplerPropertyModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanExporterModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanExporterPropertyModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessorModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessorPropertyModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.TracerProviderModel;
Expand All @@ -57,11 +62,15 @@ public class SharedConfigCustomizerProvider implements DeclarativeConfigurationC

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.withAttributeLimits(
new AttributeLimitsModel().withAttributeCountLimit(128));

Expand Down Expand Up @@ -172,7 +181,21 @@ private void addSpanExporter(OpenTelemetryConfigurationModel model) {
.withExportTimeout(60000)
.withMaxQueueSize(1024)
.withMaxExportBatchSize(512)
.withExporter(new SpanExporterModel().withOtlpGrpc(createModel())));
.withExporter(
new SpanExporterModel()
.withAdditionalProperty(
SpanExporterComponentProvider.COMPONENT_NAME,
new SpanExporterPropertyModel()
.withAdditionalProperty("timeout", 10000)
.withAdditionalProperty("protocol", "http/protobuf")
.withAdditionalProperty("compression", "gzip")
.withAdditionalProperty(
"endpoint", serviceKeyAndEndpoint[1] + "/v1/traces")
.withAdditionalProperty(
"headers_list",
String.format(
"authorization=Bearer %s",
serviceKeyAndEndpoint[0])))));

ArrayList<SpanProcessorModel> spanProcessorModels = new ArrayList<>(processors);
spanProcessorModels.add(spanProcessorModel);
Expand Down Expand Up @@ -201,10 +224,11 @@ private void addMetricExporter(OpenTelemetryConfigurationModel model) {
MetricExporterComponentProvider.COMPONENT_NAME,
new PushMetricExporterPropertyModel()
.withAdditionalProperty("timeout", 10000)
.withAdditionalProperty("protocol", "grpc")
.withAdditionalProperty("protocol", "http/protobuf")
.withAdditionalProperty("compression", "gzip")
.withAdditionalProperty(
"endpoint", serviceKeyAndEndpoint[1])
"endpoint",
serviceKeyAndEndpoint[1] + "/v1/metrics")
.withAdditionalProperty(
"temporality_preference", "delta")
.withAdditionalProperty(
Expand Down Expand Up @@ -237,26 +261,32 @@ private void addLogExporter(OpenTelemetryConfigurationModel model) {
.withMaxExportBatchSize(512)
.withMaxQueueSize(1024)
.withExportTimeout(30000)
.withExporter(new LogRecordExporterModel().withOtlpGrpc(createModel())));
.withExporter(
new LogRecordExporterModel()
.withAdditionalProperty(
LogExporterComponentProvider.COMPONENT_NAME,
new LogRecordExporterPropertyModel()
.withAdditionalProperty("timeout", 10000)
.withAdditionalProperty("protocol", "http/protobuf")
.withAdditionalProperty("compression", "gzip")
.withAdditionalProperty(
"endpoint", serviceKeyAndEndpoint[1] + "/v1/logs")
.withAdditionalProperty(
"headers_list",
String.format(
"authorization=Bearer %s",
serviceKeyAndEndpoint[0])))));

ArrayList<LogRecordProcessorModel> logRecordProcessorModels = new ArrayList<>(processors);
logRecordProcessorModels.add(logRecordProcessorModel);
loggerProvider.withProcessors(logRecordProcessorModels);
}

private void setServiceKeyAndEndpoint(OpenTelemetryConfigurationModel model) {
DeclarativeConfigProperties configProperties =
DeclarativeConfiguration.toConfigProperties(model);

DeclarativeConfigProperties solarwinds =
configProperties
.getStructured("instrumentation/development", DeclarativeConfigProperties.empty())
.getStructured("java", DeclarativeConfigProperties.empty())
.getStructured("solarwinds");
DeclarativeConfigProperties solarwinds = getSolarwindsConfig(model);

serviceKeyAndEndpoint[0] =
Objects.requireNonNull(solarwinds, "Solarwinds configuration cannot be null.")
.getString(ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), "");
solarwinds.getString(ConfigProperty.AGENT_SERVICE_KEY.getConfigFileKey(), "");

String endpoint = solarwinds.getString(ConfigProperty.AGENT_COLLECTOR.getConfigFileKey(), "");
endpoint = endpoint.replaceAll("https?://apm|^apm", "https://otel");
Expand All @@ -265,11 +295,27 @@ private void setServiceKeyAndEndpoint(OpenTelemetryConfigurationModel model) {
serviceKeyAndEndpoint[1] = endpoint;
}

private OtlpGrpcExporterModel createModel() {
return new OtlpGrpcExporterModel()
.withCompression("gzip")
.withEndpoint(serviceKeyAndEndpoint[1])
.withTimeout(10000)
.withHeadersList(String.format("authorization=Bearer %s", serviceKeyAndEndpoint[0]));
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);
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The InvalidConfigException is being wrapped in a RuntimeException without additional context. Consider providing a more descriptive error message that explains the proxy configuration failure, such as throw new RuntimeException(\"Failed to parse proxy configuration\", e);.

Suggested change
throw new RuntimeException(e);
throw new RuntimeException("Failed to parse proxy configuration", e);

Copilot uses AI. Check for mistakes.
}
}

private DeclarativeConfigProperties getSolarwindsConfig(OpenTelemetryConfigurationModel model) {
DeclarativeConfigProperties configProperties =
DeclarativeConfiguration.toConfigProperties(model);

return Objects.requireNonNull(
configProperties
.getStructured("instrumentation/development", DeclarativeConfigProperties.empty())
.getStructured("java", DeclarativeConfigProperties.empty())
.getStructured("solarwinds"),
"Solarwinds configuration cannot be null.");
}
}
Loading