Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Resource configuration factory #5757

Merged
merged 3 commits into from
Aug 24, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk-extensions/incubator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
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;
Expand Down Expand Up @@ -42,12 +43,16 @@ public OpenTelemetrySdk create(
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()));
}

Expand All @@ -57,6 +62,7 @@ public OpenTelemetrySdk create(
closeables,
TracerProviderFactory.getInstance()
.create(model.getTracerProvider(), spiHelper, closeables)
.setResource(resource)
.build()));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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());
}
}