Skip to content

Commit

Permalink
add new ResourceDetector
Browse files Browse the repository at this point in the history
  • Loading branch information
zeitlinger committed Feb 26, 2024
1 parent 1d4a2f4 commit d2d4324
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.opentelemetry.sdk.autoconfigure.spi.internal;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.Ordered;
import java.util.Optional;
import java.util.function.Function;

public interface ResourceDetector<D> extends Ordered {
Optional<D> readData(ConfigProperties config);

void registerAttributes(Builder<D> builder);

/**
* Greater order means lower priority. The default order is 0.
*/
@Override
int order();

String name();

default boolean defaultEnabled() {
return true;
}

interface Builder<D> {
<T> Builder<D> add(AttributeKey<T> key, Function<D, Optional<T>> getter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,24 @@
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.autoconfigure.internal.ResourceDetectorReader;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.Ordered;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ResourceDetector;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.resources.ResourceBuilder;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
Expand Down Expand Up @@ -81,6 +86,7 @@ public static Resource createEnvironmentResource(ConfigProperties config) {
return Resource.create(resourceAttributes.build());
}

@SuppressWarnings({"rawtypes", "unchecked"})
static Resource configureResource(
ConfigProperties config,
SpiHelper spiHelper,
Expand All @@ -91,19 +97,39 @@ static Resource configureResource(
new HashSet<>(config.getList("otel.java.enabled.resource.providers"));
Set<String> disabledProviders =
new HashSet<>(config.getList("otel.java.disabled.resource.providers"));
for (ResourceProvider resourceProvider : spiHelper.loadOrdered(ResourceProvider.class)) {
if (!enabledProviders.isEmpty()
&& !enabledProviders.contains(resourceProvider.getClass().getName())) {
continue;
List<Ordered> providers = (List) spiHelper.load(ResourceProvider.class);
providers.addAll(spiHelper.load(ResourceDetector.class));
providers.sort(Comparator.comparingInt(Ordered::order));
for (Ordered ordered : providers) {
if (ordered instanceof ResourceProvider) {
ResourceProvider provider = (ResourceProvider) ordered;

if (!enabledProviders.isEmpty()
&& !enabledProviders.contains(provider.getClass().getName())) {
continue;
}
if (disabledProviders.contains(provider.getClass().getName())) {
continue;
}
if (provider instanceof ConditionalResourceProvider
&& !((ConditionalResourceProvider) provider).shouldApply(config, result)) {
continue;
}
result = result.merge(provider.createResource(config));
} else {
ResourceDetector<Object> detector = (ResourceDetector<Object>) ordered;

String key = String.format("otel.java.resource-detector.%s.enabled", detector.name());
if (!config.getBoolean(key, detector.defaultEnabled())) {
continue;
}

ResourceDetectorReader<Object> reader = new ResourceDetectorReader<>(detector);

if (reader.shouldApply(config, result)) {
result = result.merge(reader.createResource(config, result));
}
}
if (disabledProviders.contains(resourceProvider.getClass().getName())) {
continue;
}
if (resourceProvider instanceof ConditionalResourceProvider
&& !((ConditionalResourceProvider) resourceProvider).shouldApply(config, result)) {
continue;
}
result = result.merge(resourceProvider.createResource(config));
}

result = filterAttributes(result, config);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.autoconfigure.internal;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ResourceDetector;
import io.opentelemetry.sdk.resources.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;

@SuppressWarnings({"unchecked", "rawtypes"})
public final class ResourceDetectorReader<D> {

private final ResourceDetector<D> resourceDetector;

public class AttributeBuilder implements ResourceDetector.Builder<D> {

private AttributeBuilder() {}

@Override
public <T> AttributeBuilder add(AttributeKey<T> key, Function<D, Optional<T>> getter) {
attributeGetters.put((AttributeKey) key, Objects.requireNonNull((Function) getter));
return this;
}
}

private final Map<AttributeKey<Object>, Function<D, Optional<?>>> attributeGetters =
new HashMap<>();

public ResourceDetectorReader(ResourceDetector<D> resourceDetector) {
this.resourceDetector = resourceDetector;
resourceDetector.registerAttributes(new AttributeBuilder());
}

public boolean shouldApply(ConfigProperties config, Resource existing) {
Map<String, String> resourceAttributes = getResourceAttributes(config);
return attributeGetters.keySet().stream()
.allMatch(key -> shouldUpdate(config, existing, key, resourceAttributes));
}

public Resource createResource(ConfigProperties config, Resource existing) {
return resourceDetector
.readData(config)
.map(
data -> {
Map<String, String> resourceAttributes = getResourceAttributes(config);
AttributesBuilder builder = Attributes.builder();
attributeGetters.entrySet().stream()
.filter(e -> shouldUpdate(config, existing, e.getKey(), resourceAttributes))
.forEach(
e ->
e.getValue()
.apply(data)
.ifPresent(value -> putAttribute(builder, e.getKey(), value)));
return Resource.create(builder.build());
})
.orElse(Resource.empty());
}

private static <T> void putAttribute(AttributesBuilder builder, AttributeKey<T> key, T value) {
builder.put(key, value);
}

private static Map<String, String> getResourceAttributes(ConfigProperties config) {
return config.getMap("otel.resource.attributes");
}

private static boolean shouldUpdate(
ConfigProperties config,
Resource existing,
AttributeKey<?> key,
Map<String, String> resourceAttributes) {
if (resourceAttributes.containsKey(key.getKey())) {
return false;
}

Object value = existing.getAttribute(key);

if (key.getKey().equals("service.name")) {
return config.getString("otel.service.name") == null && "unknown_service:java".equals(value);
}

return value == null;
}
}

0 comments on commit d2d4324

Please sign in to comment.