diff --git a/pom.xml b/pom.xml index 69504a5d..5b41b47d 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,7 @@ trasier-client-spring-grpc trasier-client-spring-grpc-starter trasier-client-opentracing + trasier-client-opentelemetry trasier-client-spring-interceptor trasier-client-spring-interceptor-ws trasier-client-spring-boot diff --git a/trasier-client-api-protobuf/src/main/java/com/trasier/api/client/protobuf/WriteServiceGrpc.java b/trasier-client-api-protobuf/src/main/java/com/trasier/api/client/protobuf/WriteServiceGrpc.java index bc342083..dcfff615 100644 --- a/trasier-client-api-protobuf/src/main/java/com/trasier/api/client/protobuf/WriteServiceGrpc.java +++ b/trasier-client-api-protobuf/src/main/java/com/trasier/api/client/protobuf/WriteServiceGrpc.java @@ -5,11 +5,6 @@ import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall; import static io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall; -/** - */ -@javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.24.0)", - comments = "Source: WriteService.proto") public final class WriteServiceGrpc { private WriteServiceGrpc() {} diff --git a/trasier-client-opentelemetry/pom.xml b/trasier-client-opentelemetry/pom.xml new file mode 100644 index 00000000..3a74e6a7 --- /dev/null +++ b/trasier-client-opentelemetry/pom.xml @@ -0,0 +1,41 @@ + + + + trasier-client + com.trasier + 2.3.9-SNAPSHOT + + 4.0.0 + + trasier-client-opentelemetry + jar + + + 11 + 11 + 4.12 + 0.16.0 + + + + + com.trasier + trasier-client-core + + + io.opentelemetry + opentelemetry-sdk + ${opentelemetry.version} + + + + junit + junit + ${junit.version} + test + + + + \ No newline at end of file diff --git a/trasier-client-opentelemetry/src/main/java/com/trasier/client/opentelemetry/TrasierSpanExporter.java b/trasier-client-opentelemetry/src/main/java/com/trasier/client/opentelemetry/TrasierSpanExporter.java new file mode 100644 index 00000000..65068903 --- /dev/null +++ b/trasier-client-opentelemetry/src/main/java/com/trasier/client/opentelemetry/TrasierSpanExporter.java @@ -0,0 +1,166 @@ +package com.trasier.client.opentelemetry; + +import com.trasier.client.api.Client; +import com.trasier.client.api.Endpoint; +import com.trasier.client.api.Span; +import com.trasier.client.configuration.TrasierClientConfiguration; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.logging.Logger; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +public final class TrasierSpanExporter implements SpanExporter { + + private static final Logger logger = Logger.getLogger(TrasierSpanExporter.class.getName()); + + private Client client; + private TrasierClientConfiguration configuration; + + public TrasierSpanExporter(Client client, TrasierClientConfiguration configuration) { + this.client = client; + this.configuration = configuration; + } + + @Override + public CompletableResultCode export(final Collection spanDataList) { + List spans = new ArrayList<>(spanDataList.size()); + for (SpanData spanData : spanDataList) { + spans.add(convertSpan(spanData)); + } + + final CompletableResultCode result = new CompletableResultCode(); + + client.sendSpans(spans); + + return result; + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + client.close(); + return CompletableResultCode.ofSuccess(); + } + + static final String OTEL_STATUS_CODE = "otel.status_code"; + + static final String KEY_INSTRUMENTATION_LIBRARY_NAME = "otel.library.name"; + static final String KEY_INSTRUMENTATION_LIBRARY_VERSION = "otel.library.version"; + + Span convertSpan(SpanData spanData) { + Endpoint endpoint = getEndpoint(spanData); + + long startTimestamp = toEpochMicros(spanData.getStartEpochNanos()); + long endTimestamp = toEpochMicros(spanData.getEndEpochNanos()); + + final Span.SpanBuilder spanBuilder = + Span.newSpan(spanData.getName(), UUID.randomUUID().toString(), spanData.getTraceId(), spanData.getSpanId()) + //.kind(toSpanKind(spanData)) + .startTimestamp(startTimestamp) + .endTimestamp(endTimestamp); + + if (spanData.getKind() == SpanKind.CLIENT || spanData.getKind() == SpanKind.PRODUCER) { + spanBuilder.incomingEndpoint(endpoint); + } + + if (spanData.getKind() == SpanKind.SERVER || spanData.getKind() == SpanKind.CONSUMER) { + spanBuilder.outgoingEndpoint(endpoint); + } + + if (spanData.getParentSpanContext().isValid()) { + spanBuilder.parentId(spanData.getParentSpanId()); + } +/* + Attributes spanAttributes = spanData.getAttributes(); + spanAttributes.forEach( + (key, value) -> spanBuilder.putTag(key.getKey(), valueToString(key, value))); + StatusData status = spanData.getStatus(); + + // include status code & error. + if (status.getStatusCode() != StatusCode.UNSET) { + spanBuilder.putTag(OTEL_STATUS_CODE, status.getStatusCode().toString()); + + // add the error tag, if it isn't already in the source span. + if (status.getStatusCode() == StatusCode.ERROR && spanAttributes.get(STATUS_ERROR) == null) { + spanBuilder.putTag(STATUS_ERROR.getKey(), nullToEmpty(status.getDescription())); + } + } + + InstrumentationLibraryInfo instrumentationLibraryInfo = + spanData.getInstrumentationLibraryInfo(); + + if (!instrumentationLibraryInfo.getName().isEmpty()) { + spanBuilder.putTag(KEY_INSTRUMENTATION_LIBRARY_NAME, instrumentationLibraryInfo.getName()); + } + if (instrumentationLibraryInfo.getVersion() != null) { + spanBuilder.putTag( + KEY_INSTRUMENTATION_LIBRARY_VERSION, instrumentationLibraryInfo.getVersion()); + } + + for (EventData annotation : spanData.getEvents()) { + spanBuilder.addAnnotation(toEpochMicros(annotation.getEpochNanos()), annotation.getName()); + } +*/ + return spanBuilder.build(); + } + + private Endpoint getEndpoint(SpanData spanData) { + Attributes resourceAttributes = spanData.getResource().getAttributes(); + + // use the service.name from the Resource, if it's been set. + String serviceNameValue = resourceAttributes.get(ResourceAttributes.SERVICE_NAME); + if (serviceNameValue == null) { + serviceNameValue = Resource.getDefault().getAttributes().get(ResourceAttributes.SERVICE_NAME); + } + return new Endpoint(serviceNameValue); + } + + + private static long toEpochMicros(long epochNanos) { + return NANOSECONDS.toMicros(epochNanos); + } +/* + private static String valueToString(AttributeKey key, Object attributeValue) { + AttributeType type = key.getType(); + switch (type) { + case STRING: + case BOOLEAN: + case LONG: + case DOUBLE: + return String.valueOf(attributeValue); + case STRING_ARRAY: + case BOOLEAN_ARRAY: + case LONG_ARRAY: + case DOUBLE_ARRAY: + return commaSeparated((List) attributeValue); + } + throw new IllegalStateException("Unknown attribute type: " + type); + } + + private static String commaSeparated(List values) { + StringBuilder builder = new StringBuilder(); + for (Object value : values) { + if (builder.length() != 0) { + builder.append(','); + } + builder.append(value); + } + return builder.toString(); + } +*/ +} \ No newline at end of file