Skip to content

Commit

Permalink
Add public API to access environment resource (#5554)
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg authored Jun 22, 2023
1 parent 0e318fb commit bcec7e9
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,69 @@
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
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.resources.Resource;
import io.opentelemetry.sdk.resources.ResourceBuilder;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;

/** Auto-configuration for the OpenTelemetry {@link Resource}. */
final class ResourceConfiguration {
public final class ResourceConfiguration {

// Visible for testing
static final String ATTRIBUTE_PROPERTY = "otel.resource.attributes";
static final String SERVICE_NAME_PROPERTY = "otel.service.name";
static final String DISABLED_ATTRIBUTE_KEYS = "otel.experimental.resource.disabled.keys";

/**
* Create a {@link Resource} from the environment. The resource contains attributes parsed from
* environment variables and system property keys {@code otel.resource.attributes} and {@code
* otel.service.name}.
*
* @return the resource.
*/
public static Resource createEnvironmentResource() {
return createEnvironmentResource(DefaultConfigProperties.create(Collections.emptyMap()));
}

/**
* Create a {@link Resource} from the environment. The resource contains attributes parsed from
* environment variables and system property keys {@code otel.resource.attributes} and {@code
* otel.service.name}.
*
* @param config the {@link ConfigProperties} used to obtain resource properties
* @return the resource.
*/
public static Resource createEnvironmentResource(ConfigProperties config) {
AttributesBuilder resourceAttributes = Attributes.builder();
try {
for (Map.Entry<String, String> entry : config.getMap(ATTRIBUTE_PROPERTY).entrySet()) {
resourceAttributes.put(
entry.getKey(),
// Attributes specified via otel.resource.attributes follow the W3C Baggage spec and
// characters outside the baggage-octet range are percent encoded
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/sdk.md#specifying-resource-information-via-an-environment-variable
URLDecoder.decode(entry.getValue(), StandardCharsets.UTF_8.displayName()));
}
} catch (UnsupportedEncodingException e) {
// Should not happen since always using standard charset
throw new ConfigurationException("Unable to decode resource attributes.", e);
}
String serviceName = config.getString(SERVICE_NAME_PROPERTY);
if (serviceName != null) {
resourceAttributes.put(ResourceAttributes.SERVICE_NAME, serviceName);
}

return Resource.create(resourceAttributes.build(), ResourceAttributes.SCHEMA_URL);
}

static Resource configureResource(
ConfigProperties config,
ClassLoader serviceClassLoader,
Expand All @@ -56,41 +100,11 @@ static Resource configureResource(
result = result.merge(resourceProvider.createResource(config));
}

result = result.merge(createEnvironmentResource(config));

result = filterAttributes(result, config);

return resourceCustomizer.apply(result, config);
}

private static Resource createEnvironmentResource(ConfigProperties config) {
return Resource.create(getAttributes(config), ResourceAttributes.SCHEMA_URL);
}

// visible for testing
static Attributes getAttributes(ConfigProperties configProperties) {
AttributesBuilder resourceAttributes = Attributes.builder();
try {
for (Map.Entry<String, String> entry :
configProperties.getMap(ATTRIBUTE_PROPERTY).entrySet()) {
resourceAttributes.put(
entry.getKey(),
// Attributes specified via otel.resource.attributes follow the W3C Baggage spec and
// characters outside the baggage-octet range are percent encoded
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/sdk.md#specifying-resource-information-via-an-environment-variable
URLDecoder.decode(entry.getValue(), StandardCharsets.UTF_8.displayName()));
}
} catch (UnsupportedEncodingException e) {
// Should not happen since always using standard charset
throw new ConfigurationException("Unable to decode resource attributes.", e);
}
String serviceName = configProperties.getString(SERVICE_NAME_PROPERTY);
if (serviceName != null) {
resourceAttributes.put(ResourceAttributes.SERVICE_NAME, serviceName);
}
return resourceAttributes.build();
}

// visible for testing
static Resource filterAttributes(Resource resource, ConfigProperties configProperties) {
Set<String> disabledKeys = new HashSet<>(configProperties.getList(DISABLED_ATTRIBUTE_KEYS));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.autoconfigure.internal;

import io.opentelemetry.sdk.autoconfigure.ResourceConfiguration;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;

/**
* {@link ResourceProvider} for automatically configuring {@link
* ResourceConfiguration#createEnvironmentResource(ConfigProperties)}.
*/
public final class EnvironmentResourceProvider implements ResourceProvider {
@Override
public Resource createResource(ConfigProperties config) {
return ResourceConfiguration.createEnvironmentResource(config);
}

@Override
public int order() {
// Environment resource takes precedent over all other ResourceProviders
return Integer.MAX_VALUE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.opentelemetry.sdk.autoconfigure.internal.EnvironmentResourceProvider
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ public static class TestCases {
@Fuzz
public void getAttributesWithRandomValues(String value1, String value2) {
Attributes attributes =
ResourceConfiguration.getAttributes(
DefaultConfigProperties.createForTest(
singletonMap(
ResourceConfiguration.ATTRIBUTE_PROPERTY,
"key1=" + escaper.escape(value1) + ",key2=" + escaper.escape(value2))));
ResourceConfiguration.createEnvironmentResource(
DefaultConfigProperties.createForTest(
singletonMap(
ResourceConfiguration.ATTRIBUTE_PROPERTY,
"key1=" + escaper.escape(value1) + ",key2=" + escaper.escape(value2))))
.getAttributes();

assertThat(attributes).hasSize(2).containsEntry("key1", value1).containsEntry("key2", value2);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import static io.opentelemetry.sdk.autoconfigure.ResourceConfiguration.DISABLED_ATTRIBUTE_KEYS;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;

import com.google.common.collect.ImmutableMap;
Expand Down Expand Up @@ -47,21 +46,21 @@ void customConfigResource() {
}

@Test
void resourceFromConfig_empty() {
Attributes attributes =
ResourceConfiguration.getAttributes(DefaultConfigProperties.createForTest(emptyMap()));
void createEnvironmentResource_Empty() {
Attributes attributes = ResourceConfiguration.createEnvironmentResource().getAttributes();

assertThat(attributes).isEmpty();
}

@Test
void resourceFromConfig() {
void createEnvironmentResource_WithResourceAttributes() {
Attributes attributes =
ResourceConfiguration.getAttributes(
DefaultConfigProperties.createForTest(
singletonMap(
ResourceConfiguration.ATTRIBUTE_PROPERTY,
"service.name=myService,appName=MyApp")));
ResourceConfiguration.createEnvironmentResource(
DefaultConfigProperties.createForTest(
singletonMap(
ResourceConfiguration.ATTRIBUTE_PROPERTY,
"service.name=myService,appName=MyApp")))
.getAttributes();

assertThat(attributes)
.hasSize(2)
Expand All @@ -70,25 +69,27 @@ void resourceFromConfig() {
}

@Test
void serviceName() {
void createEnvironmentResource_WithServiceName() {
Attributes attributes =
ResourceConfiguration.getAttributes(
DefaultConfigProperties.createForTest(
singletonMap(ResourceConfiguration.SERVICE_NAME_PROPERTY, "myService")));
ResourceConfiguration.createEnvironmentResource(
DefaultConfigProperties.createForTest(
singletonMap(ResourceConfiguration.SERVICE_NAME_PROPERTY, "myService")))
.getAttributes();

assertThat(attributes).hasSize(1).containsEntry(ResourceAttributes.SERVICE_NAME, "myService");
}

@Test
void resourceFromConfig_overrideServiceName() {
void createEnvironmentResource_ServiceNamePriority() {
Attributes attributes =
ResourceConfiguration.getAttributes(
DefaultConfigProperties.createForTest(
ImmutableMap.of(
ResourceConfiguration.ATTRIBUTE_PROPERTY,
"service.name=myService,appName=MyApp",
ResourceConfiguration.SERVICE_NAME_PROPERTY,
"ReallyMyService")));
ResourceConfiguration.createEnvironmentResource(
DefaultConfigProperties.createForTest(
ImmutableMap.of(
ResourceConfiguration.ATTRIBUTE_PROPERTY,
"service.name=myService,appName=MyApp",
ResourceConfiguration.SERVICE_NAME_PROPERTY,
"ReallyMyService")))
.getAttributes();

assertThat(attributes)
.hasSize(2)
Expand All @@ -97,11 +98,12 @@ void resourceFromConfig_overrideServiceName() {
}

@Test
void resourceFromConfig_emptyEnvVar() {
void createEnvironmentResource_EmptyResourceAttributes() {
Attributes attributes =
ResourceConfiguration.getAttributes(
DefaultConfigProperties.createForTest(
singletonMap(ResourceConfiguration.ATTRIBUTE_PROPERTY, "")));
ResourceConfiguration.createEnvironmentResource(
DefaultConfigProperties.createForTest(
singletonMap(ResourceConfiguration.ATTRIBUTE_PROPERTY, "")))
.getAttributes();

assertThat(attributes).isEmpty();
}
Expand Down

0 comments on commit bcec7e9

Please sign in to comment.