Add Resource configuration factory (#5757)

This commit is contained in:
jack-berg 2023-08-24 10:57:23 -05:00 committed by GitHub
parent b78145fa3c
commit 4fdd2ed81e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 369 additions and 0 deletions

View File

@ -28,6 +28,7 @@ dependencies {
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml")
implementation(project(":sdk-extensions:autoconfigure"))
implementation(project(":semconv"))
testImplementation(project(":sdk:testing"))
testImplementation(project(":sdk-extensions:autoconfigure"))

View File

@ -0,0 +1,156 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.extension.incubator.fileconfig;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Attributes;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.io.Closeable;
import java.util.List;
import javax.annotation.Nullable;
final class AttributesFactory
implements Factory<Attributes, io.opentelemetry.api.common.Attributes> {
private static final AttributesFactory INSTANCE = new AttributesFactory();
private AttributesFactory() {}
static AttributesFactory getInstance() {
return INSTANCE;
}
@Override
public io.opentelemetry.api.common.Attributes create(
@Nullable Attributes model, SpiHelper spiHelper, List<Closeable> closeables) {
if (model == null) {
return io.opentelemetry.api.common.Attributes.empty();
}
AttributesBuilder builder = io.opentelemetry.api.common.Attributes.builder();
String serviceName = model.getServiceName();
if (serviceName != null) {
builder.put(ResourceAttributes.SERVICE_NAME, serviceName);
}
model
.getAdditionalProperties()
.forEach(
(key, value) -> {
if (value == null) {
throw new ConfigurationException(
"Error processing attribute with key \"" + key + "\": unexpected null value");
}
if (value instanceof String) {
builder.put(key, (String) value);
return;
}
if (value instanceof Integer) {
builder.put(key, (int) value);
return;
}
if (value instanceof Long) {
builder.put(key, (long) value);
return;
}
if (value instanceof Double) {
builder.put(key, (double) value);
return;
}
if (value instanceof Float) {
builder.put(key, (float) value);
return;
}
if (value instanceof Boolean) {
builder.put(key, (boolean) value);
return;
}
if (value instanceof List) {
List<?> values = (List<?>) value;
if (values.isEmpty()) {
return;
}
Object first = values.get(0);
if (first instanceof String) {
checkAllEntriesOfType(key, values, String.class);
builder.put(
AttributeKey.stringArrayKey(key),
values.stream().map(obj -> (String) obj).toArray(String[]::new));
return;
}
if (first instanceof Long) {
checkAllEntriesOfType(key, values, Long.class);
builder.put(
AttributeKey.longArrayKey(key),
values.stream().map(obj -> (long) obj).toArray(Long[]::new));
return;
}
if (first instanceof Integer) {
checkAllEntriesOfType(key, values, Integer.class);
builder.put(
AttributeKey.longArrayKey(key),
values.stream().map(obj -> Long.valueOf((int) obj)).toArray(Long[]::new));
return;
}
if (first instanceof Double) {
checkAllEntriesOfType(key, values, Double.class);
builder.put(
AttributeKey.doubleArrayKey(key),
values.stream().map(obj -> (double) obj).toArray(Double[]::new));
return;
}
if (first instanceof Float) {
checkAllEntriesOfType(key, values, Float.class);
builder.put(
AttributeKey.doubleArrayKey(key),
values.stream()
.map(obj -> Double.valueOf((float) obj))
.toArray(Double[]::new));
return;
}
if (first instanceof Boolean) {
checkAllEntriesOfType(key, values, Boolean.class);
builder.put(
AttributeKey.booleanArrayKey(key),
values.stream().map(obj -> (Boolean) obj).toArray(Boolean[]::new));
return;
}
}
throw new ConfigurationException(
"Error processing attribute with key \""
+ key
+ "\": unrecognized value type "
+ value.getClass().getName());
});
return builder.build();
}
private static void checkAllEntriesOfType(String key, List<?> values, Class<?> expectedType) {
values.forEach(
value -> {
if (value == null) {
throw new ConfigurationException(
"Error processing attribute with key \""
+ key
+ "\": unexpected null element in value");
}
if (!expectedType.isAssignableFrom(value.getClass())) {
throw new ConfigurationException(
"Error processing attribute with key \""
+ key
+ "\": expected value entries to be of type "
+ expectedType
+ " but found entry with type "
+ value.getClass());
}
});
}
}

View File

@ -10,6 +10,7 @@ import io.opentelemetry.sdk.OpenTelemetrySdkBuilder;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfiguration;
import io.opentelemetry.sdk.resources.Resource;
import java.io.Closeable;
import java.util.List;
import javax.annotation.Nullable;
@ -42,12 +43,16 @@ final class OpenTelemetryConfigurationFactory
builder.setPropagators(
PropagatorsFactory.getInstance().create(model.getPropagators(), spiHelper, closeables));
Resource resource =
ResourceFactory.getInstance().create(model.getResource(), spiHelper, closeables);
if (model.getLoggerProvider() != null) {
builder.setLoggerProvider(
FileConfigUtil.addAndReturn(
closeables,
LoggerProviderFactory.getInstance()
.create(model.getLoggerProvider(), spiHelper, closeables)
.setResource(resource)
.build()));
}
@ -57,6 +62,7 @@ final class OpenTelemetryConfigurationFactory
closeables,
TracerProviderFactory.getInstance()
.create(model.getTracerProvider(), spiHelper, closeables)
.setResource(resource)
.build()));
}

View File

@ -0,0 +1,43 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.extension.incubator.fileconfig;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Attributes;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Resource;
import io.opentelemetry.sdk.resources.ResourceBuilder;
import java.io.Closeable;
import java.util.List;
import javax.annotation.Nullable;
final class ResourceFactory implements Factory<Resource, io.opentelemetry.sdk.resources.Resource> {
private static final ResourceFactory INSTANCE = new ResourceFactory();
private ResourceFactory() {}
static ResourceFactory getInstance() {
return INSTANCE;
}
@Override
public io.opentelemetry.sdk.resources.Resource create(
@Nullable Resource model, SpiHelper spiHelper, List<Closeable> closeables) {
if (model == null) {
return io.opentelemetry.sdk.resources.Resource.getDefault();
}
ResourceBuilder builder = io.opentelemetry.sdk.resources.Resource.getDefault().toBuilder();
Attributes attributesModel = model.getAttributes();
if (attributesModel != null) {
builder.putAll(
AttributesFactory.getInstance().create(attributesModel, spiHelper, closeables));
}
return builder.build();
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.extension.incubator.fileconfig;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Attributes;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class AttributesFactoryTest {
@Test
void create_Null() {
assertThat(
AttributesFactory.getInstance()
.create(null, mock(SpiHelper.class), Collections.emptyList()))
.isEqualTo(io.opentelemetry.api.common.Attributes.empty());
}
@ParameterizedTest
@MethodSource("invalidAttributes")
void create_InvalidAttributes(Attributes model, String expectedMessage) {
assertThatThrownBy(
() ->
AttributesFactory.getInstance()
.create(model, mock(SpiHelper.class), Collections.emptyList()))
.isInstanceOf(ConfigurationException.class)
.hasMessageContaining(expectedMessage);
}
private static Stream<Arguments> invalidAttributes() {
return Stream.of(
Arguments.of(
new Attributes().withAdditionalProperty("key", null),
"Error processing attribute with key \"key\": unexpected null value"),
Arguments.of(
new Attributes().withAdditionalProperty("key", new Object()),
"Error processing attribute with key \"key\": unrecognized value type java.lang.Object"),
Arguments.of(
new Attributes().withAdditionalProperty("key", Arrays.asList(1L, 1)),
"Error processing attribute with key \"key\": expected value entries to be of type class java.lang.Long but found entry with type class java.lang.Integer"),
Arguments.of(
new Attributes().withAdditionalProperty("key", Arrays.asList(1L, null)),
"Error processing attribute with key \"key\": unexpected null element in value"));
}
@Test
void create() {
assertThat(
AttributesFactory.getInstance()
.create(
new Attributes()
.withServiceName("my-service")
.withAdditionalProperty("strKey", "val")
.withAdditionalProperty("longKey", 1L)
.withAdditionalProperty("intKey", 2)
.withAdditionalProperty("doubleKey", 1.0d)
.withAdditionalProperty("floatKey", 2.0f)
.withAdditionalProperty("boolKey", true)
.withAdditionalProperty("strArrKey", Arrays.asList("val1", "val2"))
.withAdditionalProperty("longArrKey", Arrays.asList(1L, 2L))
.withAdditionalProperty("intArrKey", Arrays.asList(1, 2))
.withAdditionalProperty("doubleArrKey", Arrays.asList(1.0d, 2.0d))
.withAdditionalProperty("floatArrKey", Arrays.asList(1.0f, 2.0f))
.withAdditionalProperty("boolArrKey", Arrays.asList(true, false))
.withAdditionalProperty("emptyArrKey", Collections.emptyList()),
mock(SpiHelper.class),
Collections.emptyList()))
.isEqualTo(
io.opentelemetry.api.common.Attributes.builder()
.put(ResourceAttributes.SERVICE_NAME, "my-service")
.put("strKey", "val")
.put("longKey", 1L)
.put("intKey", 2)
.put("doubleKey", 1.0d)
.put("floatKey", 2.0f)
.put("boolKey", true)
.put("strArrKey", "val1", "val2")
.put("longArrKey", 1L, 2L)
.put("intArrKey", 1, 2)
.put("doubleArrKey", 1.0d, 2.0d)
.put("floatArrKey", 1.0f, 2.0f)
.put("boolArrKey", true, false)
.build());
}
}

View File

@ -21,6 +21,7 @@ import io.opentelemetry.internal.testing.CleanupExtension;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Attributes;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchLogRecordProcessor;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchSpanProcessor;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordExporter;
@ -29,6 +30,7 @@ import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRec
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LoggerProvider;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfiguration;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Otlp;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Resource;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanExporter;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessor;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.TracerProvider;
@ -36,6 +38,7 @@ import io.opentelemetry.sdk.logs.LogLimits;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SpanLimits;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.Arrays;
@ -108,6 +111,11 @@ class OpenTelemetryConfigurationFactoryTest {
@Test
void create_Configured() {
List<Closeable> closeables = new ArrayList<>();
io.opentelemetry.sdk.resources.Resource expectedResource =
io.opentelemetry.sdk.resources.Resource.getDefault().toBuilder()
.put(ResourceAttributes.SERVICE_NAME, "my-service")
.put("key", "val")
.build();
OpenTelemetrySdk expectedSdk =
OpenTelemetrySdk.builder()
.setPropagators(
@ -121,6 +129,7 @@ class OpenTelemetryConfigurationFactoryTest {
JaegerPropagator.getInstance())))
.setLoggerProvider(
SdkLoggerProvider.builder()
.setResource(expectedResource)
.setLogLimits(
() ->
LogLimits.builder()
@ -134,6 +143,7 @@ class OpenTelemetryConfigurationFactoryTest {
.build())
.setTracerProvider(
SdkTracerProvider.builder()
.setResource(expectedResource)
.setSpanLimits(
SpanLimits.builder()
.setMaxNumberOfAttributes(1)
@ -159,6 +169,12 @@ class OpenTelemetryConfigurationFactoryTest {
.withPropagators(
Arrays.asList(
"tracecontext", "baggage", "ottrace", "b3multi", "b3", "jaeger"))
.withResource(
new Resource()
.withAttributes(
new Attributes()
.withServiceName("my-service")
.withAdditionalProperty("key", "val")))
.withLoggerProvider(
new LoggerProvider()
.withLimits(

View File

@ -0,0 +1,47 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.extension.incubator.fileconfig;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.mockito.Mockito.mock;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Attributes;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.util.Collections;
import org.junit.jupiter.api.Test;
class ResourceFactoryTest {
@Test
void create_Null() {
assertThat(
ResourceFactory.getInstance()
.create(null, mock(SpiHelper.class), Collections.emptyList()))
.isEqualTo(Resource.getDefault());
}
@Test
void create() {
assertThat(
ResourceFactory.getInstance()
.create(
new io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model
.Resource()
.withAttributes(
new Attributes()
.withServiceName("my-service")
.withAdditionalProperty("key", "val")),
mock(SpiHelper.class),
Collections.emptyList()))
.isEqualTo(
Resource.getDefault().toBuilder()
.put(ResourceAttributes.SERVICE_NAME, "my-service")
.put("key", "val")
.build());
}
}