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 file configuration ComponentProvider support for resources #6625

Merged
merged 3 commits into from
Aug 28, 2024
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 @@ -81,9 +81,9 @@ public <T, S> NamedSpiManager<T> loadConfigurable(
}

/**
* Find a registered {@link ComponentProvider} which {@link ComponentProvider#getType()} matching
* Find a registered {@link ComponentProvider} with {@link ComponentProvider#getType()} matching
* {@code type}, {@link ComponentProvider#getName()} matching {@code name}, and call {@link
* ComponentProvider#create(StructuredConfigProperties)} with the given {@code model}.
* ComponentProvider#create(StructuredConfigProperties)} with the given {@code config}.
*
* @throws ConfigurationException if no matching providers are found, or if multiple are found
* (i.e. conflict), or if {@link ComponentProvider#create(StructuredConfigProperties)} throws
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,25 @@
package io.opentelemetry.sdk.extension.incubator.fileconfig;

import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.Ordered;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
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 io.opentelemetry.sdk.resources.Resource;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

final class ResourceFactory implements Factory<Resource, io.opentelemetry.sdk.resources.Resource> {
final class ResourceFactory
implements Factory<
io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Resource, Resource> {

private static final StructuredConfigProperties EMPTY_CONFIG =
FileConfiguration.toConfigProperties(Collections.emptyMap());
private static final ResourceFactory INSTANCE = new ResourceFactory();

private ResourceFactory() {}
Expand All @@ -23,16 +34,82 @@
}

@Override
public io.opentelemetry.sdk.resources.Resource create(
Resource model, SpiHelper spiHelper, List<Closeable> closeables) {
ResourceBuilder builder = io.opentelemetry.sdk.resources.Resource.getDefault().toBuilder();
public Resource create(
io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Resource model,
SpiHelper spiHelper,
List<Closeable> closeables) {
Resource result = Resource.getDefault();

List<Resource> resourceDetectorResources = loadFromResourceDetectors(spiHelper);
for (Resource resourceProviderResource : resourceDetectorResources) {
result = result.merge(resourceProviderResource);
}

Attributes attributesModel = model.getAttributes();
if (attributesModel != null) {
builder.putAll(
AttributesFactory.getInstance().create(attributesModel, spiHelper, closeables));
result =
result.toBuilder()
.putAll(
AttributesFactory.getInstance().create(attributesModel, spiHelper, closeables))
.build();
}

return builder.build();
return result;
}

/**
* Load resources from resource detectors, in order of lowest priority to highest priority.
*
* <p>In file configuration, a resource detector is a {@link ComponentProvider} with {@link
* ComponentProvider#getType()} set to {@link Resource}. Unlike other {@link ComponentProvider}s,
* the resource detector version does not use {@link ComponentProvider#getName()} (except for
* debug messages), and {@link ComponentProvider#create(StructuredConfigProperties)} is called
* with an empty instance. Additionally, the {@link Ordered#order()} value is respected for
* resource detectors which implement {@link Ordered}.
*/
@SuppressWarnings("rawtypes")
private static List<Resource> loadFromResourceDetectors(SpiHelper spiHelper) {
List<ComponentProvider> componentProviders = spiHelper.load(ComponentProvider.class);
List<ResourceAndOrder> resourceAndOrders = new ArrayList<>();
for (ComponentProvider<?> componentProvider : componentProviders) {
if (componentProvider.getType() != Resource.class) {
continue;
}
Resource resource;
try {
resource = (Resource) componentProvider.create(EMPTY_CONFIG);
} catch (Throwable throwable) {
throw new ConfigurationException(

Check warning on line 82 in sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java#L81-L82

Added lines #L81 - L82 were not covered by tests
"Error configuring "
+ Resource.class.getName()

Check warning on line 84 in sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java#L84

Added line #L84 was not covered by tests
+ " with name \""
+ componentProvider.getName()

Check warning on line 86 in sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java#L86

Added line #L86 was not covered by tests
+ "\"",
throwable);
}
int order =
(componentProvider instanceof Ordered) ? ((Ordered) componentProvider).order() : 0;
resourceAndOrders.add(new ResourceAndOrder(resource, order));
}
resourceAndOrders.sort(Comparator.comparing(ResourceAndOrder::order));
return resourceAndOrders.stream().map(ResourceAndOrder::resource).collect(Collectors.toList());
}

private static final class ResourceAndOrder {
private final Resource resource;
private final int order;

private ResourceAndOrder(Resource resource, int order) {
this.resource = resource;
this.order = order;
}

private Resource resource() {
return resource;
}

private int order() {
return order;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ void create_Configured() {
io.opentelemetry.sdk.resources.Resource.getDefault().toBuilder()
.put("service.name", "my-service")
.put("key", "val")
// resource attributes from resource ComponentProviders
.put("color", "red")
.put("shape", "square")
.put("order", "second")
.build();
OpenTelemetrySdk expectedSdk =
OpenTelemetrySdk.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
package io.opentelemetry.sdk.extension.incubator.fileconfig;

import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;

import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Attributes;
Expand All @@ -16,8 +16,11 @@

class ResourceFactoryTest {

private SpiHelper spiHelper = SpiHelper.create(MetricExporterFactoryTest.class.getClassLoader());

@Test
void create() {
spiHelper = spy(spiHelper);
assertThat(
ResourceFactory.getInstance()
.create(
Expand All @@ -26,13 +29,21 @@ void create() {
.withAttributes(
new Attributes()
.withServiceName("my-service")
.withAdditionalProperty("key", "val")),
mock(SpiHelper.class),
.withAdditionalProperty("key", "val")
// Should override shape attribute from ResourceComponentProvider
.withAdditionalProperty("shape", "circle")),
spiHelper,
Collections.emptyList()))
.isEqualTo(
Resource.getDefault().toBuilder()
.put("service.name", "my-service")
.put("key", "val")
.put("shape", "circle")
// From ResourceComponentProvider
.put("color", "red")
// From ResourceOrderedSecondComponentProvider, which takes priority over
// ResourceOrderedFirstComponentProvider
.put("order", "second")
.build());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.extension.incubator.fileconfig.component;

import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
import io.opentelemetry.sdk.resources.Resource;

public class ResourceComponentProvider implements ComponentProvider<Resource> {
@Override
public Class<Resource> getType() {
return Resource.class;
}

@Override
public String getName() {
return "unused";
}

@Override
public Resource create(StructuredConfigProperties config) {
return Resource.builder().put("shape", "square").put("color", "red").build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.extension.incubator.fileconfig.component;

import io.opentelemetry.sdk.autoconfigure.spi.Ordered;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
import io.opentelemetry.sdk.resources.Resource;

public class ResourceOrderedFirstComponentProvider implements ComponentProvider<Resource>, Ordered {
@Override
public Class<Resource> getType() {
return Resource.class;
}

@Override
public String getName() {
return "unused";
}

@Override
public Resource create(StructuredConfigProperties config) {
return Resource.builder().put("order", "first").build();
}

@Override
public int order() {
return 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.extension.incubator.fileconfig.component;

import io.opentelemetry.sdk.autoconfigure.spi.Ordered;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
import io.opentelemetry.sdk.resources.Resource;

public class ResourceOrderedSecondComponentProvider
implements ComponentProvider<Resource>, Ordered {
@Override
public Class<Resource> getType() {
return Resource.class;
}

@Override
public String getName() {
return "unused";
}

@Override
public Resource create(StructuredConfigProperties config) {
return Resource.builder().put("order", "second").build();
}

@Override
public int order() {
return 2;
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
io.opentelemetry.sdk.extension.incubator.fileconfig.component.MetricExporterComponentProvider
io.opentelemetry.sdk.extension.incubator.fileconfig.component.SpanExporterComponentProvider
io.opentelemetry.sdk.extension.incubator.fileconfig.component.LogRecordExporterComponentProvider
io.opentelemetry.sdk.extension.incubator.fileconfig.component.ResourceComponentProvider
io.opentelemetry.sdk.extension.incubator.fileconfig.component.ResourceOrderedFirstComponentProvider
io.opentelemetry.sdk.extension.incubator.fileconfig.component.ResourceOrderedSecondComponentProvider
Loading