Skip to content

Commit

Permalink
Patch exporter for array values(#53)
Browse files Browse the repository at this point in the history
* fix exporter to process array values in attributes

* add deprecation notice of jaeger exporter in README

* skip null attribute
  • Loading branch information
EddeCCC committed Feb 27, 2024
1 parent ab89fcb commit cf78ac6
Show file tree
Hide file tree
Showing 16 changed files with 717 additions and 136 deletions.
36 changes: 19 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ The following properties are nested properties below the `inspectit-eum-server.e
Tracing exporters are responsible for passing the recorded tracing data to a corresponding storage.
The inspectIT Ocelot EUM Server currently supports the following trace exporters:

* [Jaeger](#jaeger-exporter) [[Homepage](https://www.jaegertracing.io/)]
* [OTLP (Traces)](#otlp-exporter-traces) [[Homepage](https://github.com/open-telemetry/opentelemetry-java/tree/main/exporters/otlp/trace)]
* [Jaeger](#jaeger-exporter) [[Homepage](https://www.jaegertracing.io/)] (deprecated)

###### General Trace Exporter Settings

Expand All @@ -231,22 +231,6 @@ These settings apply to all trace exporters and can set below the `inspectit-eum
|-----------------|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `.service-name` | `${inspectit.service-name}` | The value of this property will be used to identify the service a trace came from. Please note that changes of this property only take effect after restarting the agent. |

###### Jaeger Exporter

InspectIT EUM Server supports thrift and gRPC Jaeger exporter.

By default, the Jaeger exporters are enabled but the URL/gRPC `endpoint` needed for the exporter to actually start is set to `null`.

The following properties are nested properties below the `inspectit.exporters.tracing.jaeger` property:

|Property | Default | Description |
|---|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|`.enabled`| `IF_CONFIGURED` | If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Jaeger exporter. If the url is not set, it will log a warning if set to `ENABLED` but fail silently if set to `IF_CONFIGURED`. |
|`.endpoint`| `null` | URL endpoint under which the Jaeger server can be accessed (e.g. http://127.0.0.1:14268/api/traces). |
|`.protocol`| `grpc` | The transport protocol. Supported protocols are `grpc` and `http/thrift`. |
| `.compression` | `NONE` | The compression method, see [OTEL documentation](https://opentelemetry.io/docs/reference/specification/protocol/exporter/). Supported compression methods are `gzip` and `none`. This property only takes effect when the protocol is set to `grpc`. |
| `.timeout` | `10s` | Maximum time the OTLP exporter will wait for each batch export, see [OTEL documentation](https://opentelemetry.io/docs/reference/specification/protocol/exporter/). This property only takes effect when the protocol is set to `grpc`. |

###### OTLP Exporter (Traces)

The OpenTelemetry Protocol (OTLP) exporters export the Traces in OTLP to the desired endpoint at a specified interval.
Expand All @@ -263,6 +247,24 @@ The following properties are nested properties below the `inspectit.exporters.tr
| `.compression` | `NONE` | The compression method, see [OTEL documentation](https://opentelemetry.io/docs/reference/specification/protocol/exporter/). Supported compression methods are `gzip` and `none`. |
| `.timeout` | `10s` | Maximum time the OTLP exporter will wait for each batch export, see [OTEL documentation](https://opentelemetry.io/docs/reference/specification/protocol/exporter/). |


###### Jaeger Exporter

InspectIT EUM Server supports thrift and gRPC Jaeger exporter. However, since OpenTelemetry has announced to [migrate away from the
Jaeger exporter](https://opentelemetry.io/blog/2023/jaeger-exporter-collector-migration/), it is **deprecated**.

By default, the Jaeger exporters are enabled but the URL/gRPC `endpoint` needed for the exporter to actually start is set to `null`.

The following properties are nested properties below the `inspectit.exporters.tracing.jaeger` property:

|Property | Default | Description |
|---|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|`.enabled`| `IF_CONFIGURED` | If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Jaeger exporter. If the url is not set, it will log a warning if set to `ENABLED` but fail silently if set to `IF_CONFIGURED`. |
|`.endpoint`| `null` | URL endpoint under which the Jaeger server can be accessed (e.g. http://127.0.0.1:14268/api/traces). |
|`.protocol`| `grpc` | The transport protocol. Supported protocols are `grpc` and `http/thrift`. |
| `.compression` | `NONE` | The compression method, see [OTEL documentation](https://opentelemetry.io/docs/reference/specification/protocol/exporter/). Supported compression methods are `gzip` and `none`. This property only takes effect when the protocol is set to `grpc`. |
| `.timeout` | `10s` | Maximum time the OTLP exporter will wait for each batch export, see [OTEL documentation](https://opentelemetry.io/docs/reference/specification/protocol/exporter/). This property only takes effect when the protocol is set to `grpc`. |

##### Security
Currently, the EUM Server only supports a simple API token security concept. In future, additional authentication providers
will be supported.
Expand Down
17 changes: 7 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -138,24 +138,25 @@ dependencies {
"io.opencensus:opencensus-api:${openCensusVersion}",
"io.opencensus:opencensus-impl:${openCensusVersion}",
"io.opencensus:opencensus-exporter-stats-prometheus:${openCensusVersion}",
"rocks.inspectit:opencensus-influxdb-exporter:${openCensusInfluxdbExporterVersion}",

"io.grpc:grpc-context:${grpcVersion}",

platform("io.opentelemetry:opentelemetry-bom-alpha:${openTelemetryAlphaVersion}"),
platform("io.opentelemetry:opentelemetry-bom:${openTelemetryVersion}"),
"io.opentelemetry:opentelemetry-exporter-otlp:${openTelemetryVersion}",
"io.opentelemetry:opentelemetry-semconv:${openTelemetrySemConvVersion}",
"io.opentelemetry:opentelemetry-exporter-jaeger:${openTelemetryJaegerVersion}",
"io.opentelemetry:opentelemetry-exporter-jaeger-thrift:${openTelemetryJaegerVersion}",
"io.opentelemetry:opentelemetry-sdk:${openTelemetryVersion}",
"io.opentelemetry:opentelemetry-exporter-otlp:${openTelemetryVersion}",
"io.opentelemetry:opentelemetry-exporter-jaeger:${openTelemetryVersion}",
"io.opentelemetry:opentelemetry-exporter-jaeger-thrift:${openTelemetryVersion}",
"io.opentelemetry:opentelemetry-semconv:${openTelemetryAlphaVersion}",
"io.opentelemetry.proto:opentelemetry-proto:${openTelemetryProtoVersion}",

"com.google.protobuf:protobuf-java:${protobufVersion}",
"com.google.protobuf:protobuf-java-util:${protobufVersion}",

"com.google.guava:guava:${guavaVersion}",

"com.maxmind.geoip2:geoip2:${geoip2Version}",

"commons-net:commons-net:${commonsNetVersion}",
"org.apache.commons:commons-lang3:${commonsLang3Version}",
"org.apache.commons:commons-math3:${commonsMath3Version}",
Expand All @@ -165,9 +166,7 @@ dependencies {
// If there is a higher new version, remove the dependency override of okio-jvm
"org.influxdb:influxdb-java:${influxdbJavaVersion}",
// Override transitive dependency with newer version, due to security concerns
"com.squareup.okio:okio-jvm:${okioJvmVersion}",

"rocks.inspectit:opencensus-influxdb-exporter:${opencensusInfluxdbExporterVersion}",
"com.squareup.okio:okio-jvm:${okioJvmVersion}"
)

compileOnly "org.projectlombok:lombok"
Expand All @@ -182,8 +181,6 @@ dependencies {
"com.linecorp.armeria:armeria-junit5:${armeriaVersion}",
"com.linecorp.armeria:armeria-grpc-protocol:${armeriaVersion}",

"io.opentelemetry:opentelemetry-semconv:${openTelemetryAlphaVersion}",

// for docker test containers
"org.testcontainers:testcontainers:${testContainersVersion}",
"org.testcontainers:junit-jupiter:${testContainersVersion}"
Expand Down
12 changes: 6 additions & 6 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@ snakeYamlVersion=2.2

# Ensure to adapt the netty version (inspectit-ocelot-core/build.gradle) when changing the OpenCensus version
openCensusVersion=0.31.1
openCensusInfluxdbExporterVersion=1.2
grpcVersion=1.61.1

# pin Prometheus client to 0.6.0 to prevent auto prefixing counter metrics with "_total"
# see: https://github.com/prometheus/client_java/issues/640, https://github.com/prometheus/client_java/pull/653
prometheusClientVersion = 0.6.0
# Keep the OpenTelemetry versions consistent
openTelemetryVersion=1.30.0
openTelemetryAlphaVersion=1.30.0-alpha
openTelemetryVersion=1.30.1
openTelemetryAlphaVersion=1.30.1-alpha

openTelemetryProtoVersion=1.1.0-alpha
openTelemetrySemConvVersion=1.30.1-alpha
openTelemetryJaegerVersion=1.34.1
# Use version of opentelemetry-proto
protobufVersion=3.23.4

protobufVersion=3.25.3
guavaVersion=33.0.0-jre
geoip2Version=4.2.0

Expand All @@ -36,7 +37,6 @@ commonsIoVersion=2.14.0
influxdbJavaVersion=2.24
okioJvmVersion=3.5.0

opencensusInfluxdbExporterVersion=1.2
armeriaVersion=1.27.1
testContainersVersion=1.19.5

Expand Down
114 changes: 62 additions & 52 deletions src/main/java/io/opentelemetry/sdk/trace/OcelotSpanUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.proto.common.v1.AnyValue;
import io.opentelemetry.proto.common.v1.ArrayValue;
import io.opentelemetry.proto.common.v1.KeyValue;
import io.opentelemetry.proto.trace.v1.Span;
import io.opentelemetry.proto.trace.v1.Status;
Expand Down Expand Up @@ -164,17 +165,12 @@ static SpanContext createSpanContext(String traceId, String spanId) {
*/
@VisibleForTesting
static StatusCode toStatusCode(Status.StatusCode code) {
switch (code) {
case STATUS_CODE_UNSET:
return StatusCode.UNSET;
case STATUS_CODE_OK:
return StatusCode.OK;
case STATUS_CODE_ERROR:
return StatusCode.ERROR;
case UNRECOGNIZED:
default:
return null;
}
return switch (code) {
case STATUS_CODE_UNSET -> StatusCode.UNSET;
case STATUS_CODE_OK -> StatusCode.OK;
case STATUS_CODE_ERROR -> StatusCode.ERROR;
default -> null;
};
}

/**
Expand Down Expand Up @@ -233,25 +229,18 @@ public static Attributes toAttributes(List<KeyValue> attributesList, Map<String,

if (!CollectionUtils.isEmpty(attributesList)) {
for (KeyValue attribute : attributesList) {
// skip invalid data
if (attribute == null) continue;

AttributeKey attributeKey = toAttributeKey(attribute);
if (attributeKey != null) {
AnyValue value = attribute.getValue();
switch (attribute.getValue().getValueCase()) {
case STRING_VALUE:
builder.put(attributeKey, value.getStringValue());
break;
case BOOL_VALUE:
builder.put(attributeKey, value.getBoolValue());
break;
case INT_VALUE:
builder.put(attributeKey, value.getIntValue());
break;
case DOUBLE_VALUE:
builder.put(attributeKey, value.getDoubleValue());
break;
case ARRAY_VALUE:
builder.put(attributeKey, value.getArrayValue());
break;
case STRING_VALUE -> builder.put(attributeKey, value.getStringValue());
case BOOL_VALUE -> builder.put(attributeKey, value.getBoolValue());
case INT_VALUE -> builder.put(attributeKey, value.getIntValue());
case DOUBLE_VALUE -> builder.put(attributeKey, value.getDoubleValue());
case ARRAY_VALUE -> builder.put(attributeKey, mergeArray(value.getArrayValue()));
}
}
}
Expand All @@ -270,38 +259,59 @@ public static Attributes toAttributes(List<KeyValue> attributesList, Map<String,
private static AttributeKey<?> toAttributeKey(KeyValue attribute) {
String key = attribute.getKey();
AnyValue.ValueCase valueCase = attribute.getValue().getValueCase();
switch (valueCase) {
case STRING_VALUE:
return AttributeKey.stringKey(key);
case BOOL_VALUE:
return AttributeKey.booleanKey(key);
case INT_VALUE:
return AttributeKey.longKey(key);
case DOUBLE_VALUE:
return AttributeKey.doubleKey(key);
case ARRAY_VALUE:
return AttributeKey.stringArrayKey(key);
}
return null;
return switch (valueCase) {
case STRING_VALUE -> AttributeKey.stringKey(key);
case BOOL_VALUE -> AttributeKey.booleanKey(key);
case INT_VALUE -> AttributeKey.longKey(key);
case DOUBLE_VALUE -> AttributeKey.doubleKey(key);
// Currently, OTel is not able to process arrayValue in attributes
case ARRAY_VALUE -> AttributeKey.stringKey(key);
default -> null;
};
}

/**
* @return Returns a {@link SpanKind} representing the given {@link Span.SpanKind} instance.
*/
private static SpanKind toSpanKind(Span.SpanKind spanKind) {
switch (spanKind) {
case SPAN_KIND_SERVER:
return SpanKind.SERVER;
case SPAN_KIND_CLIENT:
return SpanKind.CLIENT;
case SPAN_KIND_PRODUCER:
return SpanKind.PRODUCER;
case SPAN_KIND_CONSUMER:
return SpanKind.CONSUMER;
case SPAN_KIND_INTERNAL:
default:
return switch (spanKind) {
case SPAN_KIND_SERVER -> SpanKind.SERVER;
case SPAN_KIND_CLIENT -> SpanKind.CLIENT;
case SPAN_KIND_PRODUCER -> SpanKind.PRODUCER;
case SPAN_KIND_CONSUMER -> SpanKind.CONSUMER;
default ->
// default value if we can not map
return SpanKind.INTERNAL;
}
SpanKind.INTERNAL;
};
}

/**
* Merges all values of an array. The values will always be converted to strings.
* Currently, OTel is not able to process arrayValue objects in Attributes.
* See <a href="https://github.com/open-telemetry/opentelemetry-java/issues/6243">issue</a>
*
* @param arrayValue the array containing any values
*
* @return the merged string of all values
*/
private static String mergeArray(ArrayValue arrayValue) {
List<AnyValue> values = arrayValue.getValuesList();
String mergedString = values.stream()
.map(OcelotSpanUtils::getValueAsString)
.collect(Collectors.joining(", "));
return mergedString;
}

/**
* @return the {@link AnyValue} as string.
*/
private static String getValueAsString(AnyValue value) {
return switch (value.getValueCase()) {
case STRING_VALUE -> value.getStringValue();
case INT_VALUE -> String.valueOf(value.getIntValue());
case DOUBLE_VALUE -> String.valueOf(value.getDoubleValue());
case BOOL_VALUE -> String.valueOf(value.getBoolValue());
default -> "";
};
}
}
Loading

0 comments on commit cf78ac6

Please sign in to comment.