Skip to content
Merged
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
Expand Up @@ -22,71 +22,95 @@
import com.splunk.opentelemetry.profiler.util.DeclarativeConfigPropertiesUtil;
import io.opentelemetry.api.incubator.config.ConfigProvider;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder;
import io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil;
import io.opentelemetry.exporter.otlp.internal.OtlpGrpcLogRecordExporterComponentProvider;
import io.opentelemetry.exporter.otlp.internal.OtlpHttpLogRecordExporterComponentProvider;
import io.opentelemetry.exporter.otlp.internal.OtlpDeclarativeConfigUtil;
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter;
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ExtendedDeclarativeConfigProperties;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
import javax.annotation.Nullable;

class LogExporterBuilder {

static final String EXTRA_CONTENT_TYPE = "Extra-Content-Type";
static final String STACKTRACES_HEADER_VALUE = "otel-profiling-stacktraces";
private static final String DEFAULT_HTTP_LOG_ENDPOINT = "http://localhost:4318/v1/logs";

static LogRecordExporter fromDeclarativeConfig(
DeclarativeConfigProperties exporterConfigProperties) {
if (exporterConfigProperties != null) {
Set<String> propertyKeys = exporterConfigProperties.getPropertyKeys();
if (propertyKeys.isEmpty()) {
DeclarativeConfigProperties otlpHttp = defaultOtlpHttpConfig(exporterConfigProperties);
OtlpHttpLogRecordExporterComponentProvider provider =

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[for reviewer] Using OtlpHttpLogRecordExporterComponentProvider was shorter, but actually an awkward solution. I decided to replace it with use OtlpDeclarativeConfigUtil class instead. This is also more aligned with env-vars config approach.

new OtlpHttpLogRecordExporterComponentProvider();
return provider.create(toExtended(otlpHttp));
// Create a default exporter if no exporter config provided
return buildDeclarativeHttpExporter(exporterConfigProperties);
}

if (propertyKeys.contains("otlp_log_http")) {
DeclarativeConfigProperties otlpHttp =
DeclarativeConfigPropertiesUtil.getStructuredOrEmpty(
exporterConfigProperties, "otlp_log_http");
OtlpHttpLogRecordExporterComponentProvider provider =
new OtlpHttpLogRecordExporterComponentProvider();
return provider.create(toExtended(otlpHttp));
return buildDeclarativeHttpExporter(otlpHttp);
}

if (propertyKeys.contains("otlp_log_grpc")) {
DeclarativeConfigProperties otlpGrpc =
DeclarativeConfigPropertiesUtil.getStructuredOrEmpty(
exporterConfigProperties, "otlp_log_grpc");
OtlpGrpcLogRecordExporterComponentProvider provider =
new OtlpGrpcLogRecordExporterComponentProvider();
return provider.create(toExtended(otlpGrpc));
return buildDeclarativeGrpcExporter(otlpGrpc);
}
}

throw new ConfigurationException("Profiler exporter configuration is invalid");
}

private static DeclarativeConfigProperties defaultOtlpHttpConfig(
DeclarativeConfigProperties exporterConfigProperties) {
Map<String, Object> properties = new HashMap<>();
properties.put("endpoint", DEFAULT_HTTP_LOG_ENDPOINT);
properties.put("encoding", "protobuf");
return DeclarativeConfigPropertiesUtil.createPreservingLoader(
exporterConfigProperties, properties);
private static LogRecordExporter buildDeclarativeHttpExporter(
DeclarativeConfigProperties config) {
OtlpHttpLogRecordExporterBuilder builder = OtlpHttpLogRecordExporter.builder();
OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_LOGS,
toExtended(config),
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,
builder::setTimeout,
builder::setTrustedCertificates,
builder::setClientTls,
builder::setRetryPolicy,
builder::setMemoryMode,
/* isHttpProtobuf= */ true,
builder::setInternalTelemetryVersion,
() -> builder.setMeterProvider(MeterProvider::noop));

return builder.build();
}

private static LogRecordExporter buildDeclarativeGrpcExporter(
DeclarativeConfigProperties config) {
OtlpGrpcLogRecordExporterBuilder builder = OtlpGrpcLogRecordExporter.builder();
OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_LOGS,
toExtended(config),
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,
builder::setTimeout,
builder::setTrustedCertificates,
builder::setClientTls,
builder::setRetryPolicy,
builder::setMemoryMode,
/* isHttpProtobuf= */ false,
builder::setInternalTelemetryVersion,
() -> builder.setMeterProvider(MeterProvider::noop));

return builder.build();
}

static LogRecordExporter fromEnvironmentConfig() {
Expand All @@ -104,15 +128,21 @@ static LogRecordExporter fromEnvironmentConfig() {
static LogRecordExporter buildGrpcExporter(
ProfilerConfiguration config, Supplier<OtlpGrpcLogRecordExporterBuilder> makeBuilder) {
OtlpGrpcLogRecordExporterBuilder builder = makeBuilder.get();

String ingestUrl = config.getIngestUrl();
builder.setEndpoint(ingestUrl);
if (ingestUrl != null) {
builder.setEndpoint(ingestUrl);
}

return builder.addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE).build();
return builder.build();
}

@VisibleForTesting
static LogRecordExporter buildHttpExporter(
ProfilerConfiguration config, Supplier<OtlpHttpLogRecordExporterBuilder> makeBuilder) {
Objects.requireNonNull(
config.getConfigProperties(),
"Null env-vars based ConfigProperties in ProfilerConfiguration.");
OtlpHttpLogRecordExporterBuilder builder = makeBuilder.get();
String ingestUrl = config.getIngestUrl();

Expand All @@ -130,9 +160,11 @@ static LogRecordExporter buildHttpExporter(
builder::setMemoryMode,
builder::setInternalTelemetryVersion);

builder.setEndpoint(ingestUrl);
if (ingestUrl != null) {
builder.setEndpoint(ingestUrl);
}

return builder.addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE).build();
return builder.build();
}

public static ExtendedDeclarativeConfigProperties toExtended(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ public static DeclarativeConfigProperties getStructuredOrEmpty(
if (structured != null) {
return structured;
}
return emptyPreservingLoader(parent);
return emptyWithLoaderPreserved(parent);
}

public static DeclarativeConfigProperties emptyPreservingLoader(
public static DeclarativeConfigProperties emptyWithLoaderPreserved(
DeclarativeConfigProperties parent) {
return createPreservingLoader(parent, Collections.emptyMap());
return createWithLoaderPreserved(parent, Collections.emptyMap());
}

public static DeclarativeConfigProperties createPreservingLoader(
public static DeclarativeConfigProperties createWithLoaderPreserved(
DeclarativeConfigProperties parent, Map<String, Object> properties) {
return YamlDeclarativeConfigProperties.create(properties, parent.getComponentLoader());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

package com.splunk.opentelemetry.profiler;

import static com.splunk.opentelemetry.profiler.LogExporterBuilder.EXTRA_CONTENT_TYPE;
import static com.splunk.opentelemetry.profiler.LogExporterBuilder.STACKTRACES_HEADER_VALUE;
import static com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil.getProfilingConfig;
import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
Expand Down Expand Up @@ -64,7 +62,6 @@ void testBuildSimpleGrpc() {
OtlpGrpcLogRecordExporterBuilder builder = mock(OtlpGrpcLogRecordExporterBuilder.class);
OtlpGrpcLogRecordExporter expected = mock(OtlpGrpcLogRecordExporter.class);

when(builder.addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE)).thenReturn(builder);
when(builder.build()).thenReturn(expected);
when(config.getIngestUrl()).thenReturn(DEFAULT_GRPC_LOG_ENDPOINT);

Expand All @@ -74,6 +71,7 @@ void testBuildSimpleGrpc() {
// then
assertThat(exporter).isSameAs(expected);
verify(builder).setEndpoint(DEFAULT_GRPC_LOG_ENDPOINT);
verify(builder, never()).addHeader(anyString(), anyString());
}

@Test
Expand All @@ -84,7 +82,6 @@ void testBuildSimpleHttp() {
OtlpHttpLogRecordExporterBuilder builder = mock(OtlpHttpLogRecordExporterBuilder.class);
OtlpHttpLogRecordExporter expected = mock(OtlpHttpLogRecordExporter.class);

when(builder.addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE)).thenReturn(builder);
when(builder.build()).thenReturn(expected);
when(config.getConfigProperties()).thenReturn(configProperties);
when(configProperties.getString(eq("otel.exporter.otlp.protocol"), anyString()))
Expand All @@ -97,6 +94,7 @@ void testBuildSimpleHttp() {
// then
assertThat(exporter).isSameAs(expected);
verify(builder).setEndpoint(DEFAULT_HTTP_LOG_ENDPOINT);
verify(builder, never()).addHeader(anyString(), anyString());
}

@Test
Expand All @@ -117,15 +115,13 @@ void extraOtlpHeaders() {
OtlpHttpLogRecordExporterBuilder builder = mock(OtlpHttpLogRecordExporterBuilder.class);
OtlpHttpLogRecordExporter expected = mock(OtlpHttpLogRecordExporter.class);

when(builder.addHeader(anyString(), anyString())).thenReturn(builder);
when(builder.build()).thenReturn(expected);

// when
LogRecordExporter exporter = LogExporterBuilder.buildHttpExporter(config, () -> builder);

// then
assertThat(exporter).isSameAs(expected);
verify(builder).addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE);
verify(builder).addHeader("log", "lady");
verify(builder, never()).addHeader("foo", "bar");
verify(builder, never()).addHeader("bar", "baz");
Expand All @@ -147,15 +143,15 @@ void extraOtlpLogSpecificHeaders() {
OtlpHttpLogRecordExporterBuilder builder = mock(OtlpHttpLogRecordExporterBuilder.class);
OtlpHttpLogRecordExporter expected = mock(OtlpHttpLogRecordExporter.class);

when(builder.addHeader(anyString(), anyString())).thenReturn(builder);
when(builder.build()).thenReturn(expected);

// when
LogRecordExporter exporter = LogExporterBuilder.buildHttpExporter(config, () -> builder);

// then
assertThat(exporter).isSameAs(expected);
verify(builder).addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE);
verify(builder).addHeader("foo", "bar");
verify(builder).addHeader("bar", "baz");
}
}

Expand Down Expand Up @@ -233,6 +229,29 @@ void shouldCreateHttpExporter_defaultEndpoint() {
assertThat(exporter).isNotNull();
}

@Test
void shouldCreateDefaultExporter() {
// given
OpenTelemetryConfigurationModel model =
DeclarativeConfigTestUtil.parse(
"""
file_format: "1.1"
distribution:
splunk:
profiling:
""");

DeclarativeConfigProperties exporterConfig = getExporterConfig(model);

// when
LogRecordExporter exporter = LogExporterBuilder.fromDeclarativeConfig(exporterConfig);

// then
assertThat(exporter).isNotNull();
assertThat(exporter).isInstanceOf(OtlpHttpLogRecordExporter.class);
assertThat(exporter.toString()).contains("http://localhost:4318/v1/logs");
}

@Test
void shouldCreateGrpcExporter_defaultEndpoint() {
// given
Expand Down
Loading