Skip to content

Commit

Permalink
Add file configuration ComponentProvider support for resources (#6625)
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg authored Aug 28, 2024
1 parent 902c46e commit 05fe136
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 14 deletions.
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 @@ static ResourceFactory getInstance() {
}

@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(
"Error configuring "
+ Resource.class.getName()
+ " with name \""
+ componentProvider.getName()
+ "\"",
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

0 comments on commit 05fe136

Please sign in to comment.