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

Update to opentelemetry-configuration v0.3.0 #6733

Merged
merged 7 commits into from
Sep 30, 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
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;

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

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AttributeListFactory.java#L129

Added line #L129 was not covered by tests
}
List<?> list = (List<?>) value;
if (list.isEmpty()) {
return null;

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

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AttributeListFactory.java#L133

Added line #L133 was not covered by tests
}
if (!list.stream().allMatch(entry -> expectedType.isAssignableFrom(entry.getClass()))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very strict indeed.

return null;
}
return (List<T>) value;
}
}
Loading
Loading