Skip to content

Commit

Permalink
Update to opentelemetry-configuration v0.3.0 (#6733)
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg authored Sep 30, 2024
1 parent 697b4e0 commit 2dbb8de
Show file tree
Hide file tree
Showing 26 changed files with 890 additions and 438 deletions.
2 changes: 1 addition & 1 deletion dependencyManagement/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ val DEPENDENCIES = listOf(
"eu.rekawek.toxiproxy:toxiproxy-java:2.1.7",
"io.github.netmikey.logunit:logunit-jul:2.0.0",
"io.jaegertracing:jaeger-client:1.8.1",
"io.opentelemetry.contrib:opentelemetry-aws-xray-propagator:1.29.0-alpha",
"io.opentelemetry.contrib:opentelemetry-aws-xray-propagator:1.39.0-alpha",
"io.opentelemetry.semconv:opentelemetry-semconv-incubating:1.27.0-alpha",
"io.opentelemetry.proto:opentelemetry-proto:1.3.2-alpha",
"io.opentracing:opentracing-api:0.33.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
import io.opentelemetry.sdk.common.export.MemoryMode;
import io.opentelemetry.sdk.common.export.RetryPolicy;
Expand All @@ -27,6 +28,8 @@
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.BiConsumer;
Expand Down Expand Up @@ -97,21 +100,7 @@ public static void configureOtlpExporterBuilder(
setEndpoint.accept(endpoint.toString());
}

Map<String, String> headers = config.getMap("otel.exporter.otlp." + dataType + ".headers");
if (headers.isEmpty()) {
headers = config.getMap("otel.exporter.otlp.headers");
}
for (Map.Entry<String, String> entry : headers.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
try {
// headers are encoded as URL - see
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#specifying-headers-via-environment-variables
addHeader.accept(key, URLDecoder.decode(value, StandardCharsets.UTF_8.displayName()));
} catch (Exception e) {
throw new ConfigurationException("Cannot decode header value: " + value, e);
}
}
configureOtlpHeaders(config, dataType, addHeader);

String compression = config.getString("otel.exporter.otlp." + dataType + ".compression");
if (compression == null) {
Expand Down Expand Up @@ -190,29 +179,28 @@ public static void configureOtlpExporterBuilder(
String protocol = getStructuredConfigOtlpProtocol(config);
boolean isHttpProtobuf = protocol.equals(PROTOCOL_HTTP_PROTOBUF);
URL endpoint = validateEndpoint(config.getString("endpoint"), isHttpProtobuf);
if (endpoint != null && isHttpProtobuf) {
String path = endpoint.getPath();
if (!path.endsWith("/")) {
path += "/";
}
path += signalPath(dataType);
endpoint = createUrl(endpoint, path);
}
if (endpoint != null) {
setEndpoint.accept(endpoint.toString());
}

StructuredConfigProperties headers = config.getStructured("headers");
String headerList = config.getString("headers_list");
if (headerList != null) {
ConfigProperties headersListConfig =
DefaultConfigProperties.createFromMap(
Collections.singletonMap("otel.exporter.otlp.headers", headerList));
configureOtlpHeaders(headersListConfig, dataType, addHeader);
}

List<StructuredConfigProperties> headers = config.getStructuredList("headers");
if (headers != null) {
headers
.getPropertyKeys()
.forEach(
header -> {
String value = headers.getString(header);
if (value != null) {
addHeader.accept(header, value);
}
});
headers.forEach(
header -> {
String name = header.getString("name");
String value = header.getString("value");
if (name != null && value != null) {
addHeader.accept(name, value);
}
});
}

String compression = config.getString("compression");
Expand Down Expand Up @@ -249,6 +237,25 @@ public static void configureOtlpExporterBuilder(
ExporterBuilderUtil.configureExporterMemoryMode(config, setMemoryMode);
}

private static void configureOtlpHeaders(
ConfigProperties config, String dataType, BiConsumer<String, String> addHeader) {
Map<String, String> headers = config.getMap("otel.exporter.otlp." + dataType + ".headers");
if (headers.isEmpty()) {
headers = config.getMap("otel.exporter.otlp.headers");
}
for (Map.Entry<String, String> entry : headers.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
try {
// headers are encoded as URL - see
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#specifying-headers-via-environment-variables
addHeader.accept(key, URLDecoder.decode(value, StandardCharsets.UTF_8.displayName()));
} catch (Exception e) {
throw new ConfigurationException("Cannot decode header value: " + value, e);
}
}
}

/**
* Invoke the {@code aggregationTemporalitySelectorConsumer} with the configured {@link
* AggregationTemporality}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ void configFile(@TempDir Path tempDir) throws IOException {
"file_format: \"0.1\"\n"
+ "resource:\n"
+ " attributes:\n"
+ " service.name: test\n"
+ " - name: service.name\n"
+ " value: test\n"
+ "tracer_provider:\n"
+ " processors:\n"
+ " - simple:\n"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ void setup() throws IOException {
"file_format: \"0.1\"\n"
+ "resource:\n"
+ " attributes:\n"
+ " service.name: test\n"
+ " - name: service.name\n"
+ " value: test\n"
+ "tracer_provider:\n"
+ " processors:\n"
+ " - simple:\n"
Expand Down Expand Up @@ -162,7 +163,8 @@ void configFile_Error(@TempDir Path tempDir) throws IOException {
"file_format: \"0.1\"\n"
+ "resource:\n"
+ " attributes:\n"
+ " service.name: test\n"
+ " - name: service.name\n"
+ " value: test\n"
+ "tracer_provider:\n"
+ " processors:\n"
+ " - simple:\n"
Expand Down
18 changes: 12 additions & 6 deletions sdk-extensions/incubator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,14 @@ dependencies {
// The sequence of tasks is:
// 1. downloadConfigurationSchema - download configuration schema from open-telemetry/opentelemetry-configuration
// 2. unzipConfigurationSchema - unzip the configuration schema archive contents to $buildDir/configuration/
// 3. generateJsonSchema2Pojo - generate java POJOs from the configuration schema
// 4. jsonSchema2PojoPostProcessing - perform various post processing on the generated POJOs, e.g. replace javax.annotation.processing.Generated with javax.annotation.Generated, add @SuppressWarning("rawtypes") annotation
// 5. overwriteJs2p - overwrite original generated classes with versions containing updated @Generated annotation
// 6. deleteJs2pTmp - delete tmp directory
// 3. deleteTypeDescriptions - delete type_descriptions.yaml $buildDir/configuration/schema, which is not part of core schema and causes problems resolving type refs
// 4. generateJsonSchema2Pojo - generate java POJOs from the configuration schema
// 5. jsonSchema2PojoPostProcessing - perform various post processing on the generated POJOs, e.g. replace javax.annotation.processing.Generated with javax.annotation.Generated, add @SuppressWarning("rawtypes") annotation
// 6. overwriteJs2p - overwrite original generated classes with versions containing updated @Generated annotation
// 7. deleteJs2pTmp - delete tmp directory
// ... proceed with normal sourcesJar, compileJava, etc

val configurationTag = "0.1.0"
val configurationTag = "0.3.0"
val configurationRef = "refs/tags/v$configurationTag" // Replace with commit SHA to point to experiment with a specific commit
val configurationRepoZip = "https://github.com/open-telemetry/opentelemetry-configuration/archive/$configurationRef.zip"
val buildDirectory = layout.buildDirectory.asFile.get()
Expand All @@ -78,6 +79,11 @@ val unzipConfigurationSchema by tasks.registering(Copy::class) {
into("$buildDirectory/configuration/")
}

val deleteTypeDescriptions by tasks.registering(Delete::class) {
dependsOn(unzipConfigurationSchema)
delete("$buildDirectory/configuration/schema/type_descriptions.yaml")
}

jsonSchema2Pojo {
sourceFiles = setOf(file("$buildDirectory/configuration/schema"))
targetDirectory = file("$buildDirectory/generated/sources/js2p/java/main")
Expand Down Expand Up @@ -106,7 +112,7 @@ jsonSchema2Pojo {
}

val generateJsonSchema2Pojo = tasks.getByName("generateJsonSchema2Pojo")
generateJsonSchema2Pojo.dependsOn(unzipConfigurationSchema)
generateJsonSchema2Pojo.dependsOn(deleteTypeDescriptions)

val jsonSchema2PojoPostProcessing by tasks.registering(Copy::class) {
dependsOn(generateJsonSchema2Pojo)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

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

import static java.util.stream.Collectors.toList;

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.AttributeNameValueModel;
import java.io.Closeable;
import java.util.List;
import javax.annotation.Nullable;

final class AttributeListFactory
implements Factory<List<AttributeNameValueModel>, io.opentelemetry.api.common.Attributes> {

private static final AttributeListFactory INSTANCE = new AttributeListFactory();

private AttributeListFactory() {}

static AttributeListFactory getInstance() {
return INSTANCE;
}

@Override
public io.opentelemetry.api.common.Attributes create(
List<AttributeNameValueModel> model, SpiHelper spiHelper, List<Closeable> closeables) {
AttributesBuilder builder = io.opentelemetry.api.common.Attributes.builder();

for (AttributeNameValueModel nameValueModel : model) {
addToBuilder(nameValueModel, builder);
}

return builder.build();
}

private static void addToBuilder(
AttributeNameValueModel nameValueModel, AttributesBuilder builder) {
String name = FileConfigUtil.requireNonNull(nameValueModel.getName(), "attribute name");
Object value = FileConfigUtil.requireNonNull(nameValueModel.getValue(), "attribute value");
AttributeNameValueModel.Type type = nameValueModel.getType();
if (type == null) {
type = AttributeNameValueModel.Type.STRING;
}
switch (type) {
case STRING:
if (value instanceof String) {
builder.put(name, (String) value);
return;
}
break;
case BOOL:
if (value instanceof Boolean) {
builder.put(name, (boolean) value);
return;
}
break;
case INT:
if ((value instanceof Integer) || (value instanceof Long)) {
builder.put(name, ((Number) value).longValue());
return;
}
break;
case DOUBLE:
if (value instanceof Number) {
builder.put(name, ((Number) value).doubleValue());
return;
}
break;
case STRING_ARRAY:
List<String> stringList = checkListOfType(value, String.class);
if (stringList != null) {
builder.put(AttributeKey.stringArrayKey(name), stringList);
return;
}
break;
case BOOL_ARRAY:
List<Boolean> boolList = checkListOfType(value, Boolean.class);
if (boolList != null) {
builder.put(AttributeKey.booleanArrayKey(name), boolList);
return;
}
break;
case INT_ARRAY:
List<Long> longList = checkListOfType(value, Long.class);
if (longList != null) {
builder.put(AttributeKey.longArrayKey(name), longList);
return;
}
List<Integer> intList = checkListOfType(value, Integer.class);
if (intList != null) {
builder.put(
AttributeKey.longArrayKey(name),
intList.stream().map(i -> (long) i).collect(toList()));
return;
}
break;
case DOUBLE_ARRAY:
List<Double> doubleList = checkListOfType(value, Double.class);
if (doubleList != null) {
builder.put(AttributeKey.doubleArrayKey(name), doubleList);
return;
}
List<Float> floatList = checkListOfType(value, Float.class);
if (floatList != null) {
builder.put(
AttributeKey.doubleArrayKey(name),
floatList.stream().map(i -> (double) i).collect(toList()));
return;
}
break;
}
throw new ConfigurationException(
"Error processing attribute with name \""
+ name
+ "\": value did not match type "
+ type.name());
}

@SuppressWarnings("unchecked")
@Nullable
private static <T> List<T> checkListOfType(Object value, Class<T> expectedType) {
if (!(value instanceof List)) {
return null;
}
List<?> list = (List<?>) value;
if (list.isEmpty()) {
return null;
}
if (!list.stream().allMatch(entry -> expectedType.isAssignableFrom(entry.getClass()))) {
return null;
}
return (List<T>) value;
}
}
Loading

0 comments on commit 2dbb8de

Please sign in to comment.