aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYang Song <songy23@users.noreply.github.com>2018-09-20 14:53:54 -0700
committerGitHub <noreply@github.com>2018-09-20 14:53:54 -0700
commitb2251bf8ec0980800e98efdd775a6e85a39edc13 (patch)
treeea0b883a45b6c8a989c473cf8c44763e783b8934
parentba5b8310495939a2912934836283ca356b353580 (diff)
downloadopencensus-java-b2251bf8ec0980800e98efdd775a6e85a39edc13.tar.gz
Contrib: Add opencensus-contrib-opencensus-proto-util. (#1459)
* Contrib: Add opencensus-contrib-opencensus-proto-util. * Update CHANGELOG.
-rw-r--r--CHANGELOG.md2
-rw-r--r--RELEASING.md1
-rw-r--r--all/build.gradle2
-rw-r--r--build.gradle3
-rw-r--r--buildscripts/import-control.xml6
-rw-r--r--contrib/opencensus_proto_util/README.md24
-rw-r--r--contrib/opencensus_proto_util/build.gradle16
-rw-r--r--contrib/opencensus_proto_util/src/main/java/io/opencensus/contrib/opencensus/proto/util/TraceProtoUtils.java382
-rw-r--r--contrib/opencensus_proto_util/src/test/java/io/opencensus/contrib/opencensus/proto/util/TraceProtoUtilsTest.java318
-rw-r--r--settings.gradle3
10 files changed, 757 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 39cc492e..be1ad8be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,8 @@
- Add `createWithSender` to `JaegerTraceExporter` to allow use of `HttpSender`
with extra configurations
- Add an API `Functions.returnToString()`.
+- Add `opencensus-contrib-opencensus-proto-util` that has helper utilities to convert between
+ Java data models and protos.
## 0.16.1 - 2018-09-18
- Fix ClassCastException in Log4j log correlation
diff --git a/RELEASING.md b/RELEASING.md
index e5feb95a..669fc8fa 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -210,6 +210,7 @@ $ README_FILES=(
contrib/log_correlation/log4j2/README.md
contrib/log_correlation/stackdriver/README.md
contrib/monitored_resource_util/README.md
+ contrib/opencensus_proto_util/README.md
contrib/spring/README.md
contrib/spring_sleuth_v1x/README.md
contrib/zpages/README.md
diff --git a/all/build.gradle b/all/build.gradle
index 3832e6ce..072459d1 100644
--- a/all/build.gradle
+++ b/all/build.gradle
@@ -15,6 +15,7 @@ def subprojects = [
project(':opencensus-contrib-log-correlation-log4j2'),
project(':opencensus-contrib-log-correlation-stackdriver'),
project(':opencensus-contrib-monitored-resource-util'),
+ project(':opencensus-contrib-opencensus-proto-util'),
project(':opencensus-contrib-spring'),
project(':opencensus-contrib-spring-sleuth-v1x'),
project(':opencensus-contrib-zpages'),
@@ -40,6 +41,7 @@ def subprojects_javadoc = [
project(':opencensus-contrib-log-correlation-log4j2'),
project(':opencensus-contrib-log-correlation-stackdriver'),
project(':opencensus-contrib-monitored-resource-util'),
+ project(':opencensus-contrib-opencensus-proto-util'),
project(':opencensus-contrib-spring'),
project(':opencensus-contrib-spring-sleuth-v1x'),
project(':opencensus-contrib-zpages'),
diff --git a/build.gradle b/build.gradle
index e6a8921b..7754eba1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -167,6 +167,7 @@ subprojects {
protobufVersion = '3.5.1'
zipkinReporterVersion = '2.3.2'
jaegerReporterVersion = '0.27.0'
+ opencensusProtoVersion = '0.0.2'
libraries = [
appengine_api: "com.google.appengine:appengine-api-1.0-sdk:${appengineVersion}",
@@ -199,6 +200,7 @@ subprojects {
spring_context_support: "org.springframework:spring-context-support:${springVersion}",
prometheus_simpleclient: "io.prometheus:simpleclient:${prometheusVersion}",
protobuf: "com.google.protobuf:protobuf-java:${protobufVersion}",
+ opencensus_proto: "io.opencensus:opencensus-proto:${opencensusProtoVersion}",
// Test dependencies.
guava_testlib: "com.google.guava:guava-testlib:${guavaVersion}",
@@ -421,6 +423,7 @@ subprojects {
'opencensus-contrib-log-correlation-log4j2',
'opencensus-contrib-log-correlation-stackdriver',
'opencensus-contrib-monitored-resource-util',
+ 'opencensus-contrib-opencensus-proto-util',
'opencensus-contrib-spring',
'opencensus-contrib-spring-sleuth-v1x',
'opencensus-contrib-zpages',
diff --git a/buildscripts/import-control.xml b/buildscripts/import-control.xml
index d9b4b254..69a3de8e 100644
--- a/buildscripts/import-control.xml
+++ b/buildscripts/import-control.xml
@@ -134,6 +134,12 @@ General guidelines on imports:
<subpackage name="monitoredresource.util">
<allow pkg="io.opencensus.contrib.monitoredresource.util"/>
</subpackage>
+ <subpackage name="opencensus.proto.util">
+ <allow pkg="com.google.protobuf"/>
+ <allow pkg="io.opencensus.contrib.opencensus.proto.util"/>
+ <allow pkg="io.opencensus.proto"/>
+ <allow pkg="io.opencensus.trace"/>
+ </subpackage>
</subpackage>
<subpackage name="exporter">
<allow pkg="com.google.common"/>
diff --git a/contrib/opencensus_proto_util/README.md b/contrib/opencensus_proto_util/README.md
new file mode 100644
index 00000000..e495121d
--- /dev/null
+++ b/contrib/opencensus_proto_util/README.md
@@ -0,0 +1,24 @@
+# OpenCensus Proto Util
+
+The *OpenCensus Proto Util for Java* is a collection of utilities for converting OpenCensus Java
+data models to OpenCensus proto data models.
+
+## Quickstart
+
+### Add the dependencies to your project
+
+For Maven add to your `pom.xml`:
+```xml
+<dependencies>
+ <dependency>
+ <groupId>io.opencensus</groupId>
+ <artifactId>opencensus-contrib-opencensus-proto-util</artifactId>
+ <version>0.17.0</version>
+ </dependency>
+</dependencies>
+```
+
+For Gradle add to your dependencies:
+```gradle
+compile 'io.opencensus:opencensus-contrib-opencensus-proto-util:0.17.0'
+```
diff --git a/contrib/opencensus_proto_util/build.gradle b/contrib/opencensus_proto_util/build.gradle
new file mode 100644
index 00000000..8a622246
--- /dev/null
+++ b/contrib/opencensus_proto_util/build.gradle
@@ -0,0 +1,16 @@
+description = 'OpenCensus Proto Util'
+
+apply plugin: 'java'
+
+[compileJava, compileTestJava].each() {
+ it.sourceCompatibility = 1.7
+ it.targetCompatibility = 1.7
+}
+
+dependencies {
+ compile project(':opencensus-api'),
+ libraries.opencensus_proto
+
+ signature "org.codehaus.mojo.signature:java17:1.0@signature"
+ signature "net.sf.androidscents.signature:android-api-level-14:4.0_r4@signature"
+}
diff --git a/contrib/opencensus_proto_util/src/main/java/io/opencensus/contrib/opencensus/proto/util/TraceProtoUtils.java b/contrib/opencensus_proto_util/src/main/java/io/opencensus/contrib/opencensus/proto/util/TraceProtoUtils.java
new file mode 100644
index 00000000..f2ecd01a
--- /dev/null
+++ b/contrib/opencensus_proto_util/src/main/java/io/opencensus/contrib/opencensus/proto/util/TraceProtoUtils.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright 2018, OpenCensus Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.opencensus.contrib.opencensus.proto.util;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.protobuf.BoolValue;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.UInt32Value;
+import io.opencensus.common.Function;
+import io.opencensus.common.Functions;
+import io.opencensus.common.Timestamp;
+import io.opencensus.proto.trace.v1.AttributeValue;
+import io.opencensus.proto.trace.v1.ConstantSampler;
+import io.opencensus.proto.trace.v1.ProbabilitySampler;
+import io.opencensus.proto.trace.v1.Span;
+import io.opencensus.proto.trace.v1.Span.Attributes;
+import io.opencensus.proto.trace.v1.Span.Link;
+import io.opencensus.proto.trace.v1.Span.Links;
+import io.opencensus.proto.trace.v1.Span.SpanKind;
+import io.opencensus.proto.trace.v1.Span.TimeEvent;
+import io.opencensus.proto.trace.v1.Span.TimeEvent.MessageEvent;
+import io.opencensus.proto.trace.v1.Span.Tracestate;
+import io.opencensus.proto.trace.v1.Span.Tracestate.Entry;
+import io.opencensus.proto.trace.v1.Status;
+import io.opencensus.proto.trace.v1.TraceConfig;
+import io.opencensus.proto.trace.v1.TruncatableString;
+import io.opencensus.trace.Annotation;
+import io.opencensus.trace.MessageEvent.Type;
+import io.opencensus.trace.Sampler;
+import io.opencensus.trace.Span.Kind;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.config.TraceParams;
+import io.opencensus.trace.export.SpanData;
+import io.opencensus.trace.export.SpanData.TimedEvent;
+import io.opencensus.trace.export.SpanData.TimedEvents;
+import io.opencensus.trace.samplers.Samplers;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+/**
+ * Utilities for converting the Tracing data models in OpenCensus Java to/from OpenCensus Proto.
+ *
+ * @since 0.17
+ */
+public final class TraceProtoUtils {
+
+ // Constant functions for AttributeValue.
+ private static final Function<String, /*@Nullable*/ AttributeValue> stringAttributeValueFunction =
+ new Function<String, /*@Nullable*/ AttributeValue>() {
+ @Override
+ public AttributeValue apply(String stringValue) {
+ return AttributeValue.newBuilder()
+ .setStringValue(toTruncatableStringProto(stringValue))
+ .build();
+ }
+ };
+
+ private static final Function<Boolean, /*@Nullable*/ AttributeValue>
+ booleanAttributeValueFunction =
+ new Function<Boolean, /*@Nullable*/ AttributeValue>() {
+ @Override
+ public AttributeValue apply(Boolean booleanValue) {
+ return AttributeValue.newBuilder().setBoolValue(booleanValue).build();
+ }
+ };
+
+ private static final Function<Long, /*@Nullable*/ AttributeValue> longAttributeValueFunction =
+ new Function<Long, /*@Nullable*/ AttributeValue>() {
+ @Override
+ public AttributeValue apply(Long longValue) {
+ return AttributeValue.newBuilder().setIntValue(longValue).build();
+ }
+ };
+
+ private static final Function<Double, /*@Nullable*/ AttributeValue> doubleAttributeValueFunction =
+ new Function<Double, /*@Nullable*/ AttributeValue>() {
+ @Override
+ public AttributeValue apply(Double doubleValue) {
+ return AttributeValue.newBuilder().setDoubleValue(doubleValue).build();
+ }
+ };
+
+ /**
+ * Converts {@link SpanData} to {@link Span} proto.
+ *
+ * @param spanData the {@code SpanData}.
+ * @return proto representation of {@code Span}.
+ * @since 0.17
+ */
+ @SuppressWarnings("DefaultCharset")
+ public static Span toSpanProto(SpanData spanData) {
+ SpanContext spanContext = spanData.getContext();
+ TraceId traceId = spanContext.getTraceId();
+ SpanId spanId = spanContext.getSpanId();
+ Span.Builder spanBuilder =
+ Span.newBuilder()
+ .setTraceId(toByteString(traceId.getBytes()))
+ .setSpanId(toByteString(spanId.getBytes()))
+ .setTracestate(toTracestateProto(spanContext.getTracestate()))
+ .setName(toTruncatableStringProto(spanData.getName()))
+ .setStartTime(toTimestampProto(spanData.getStartTimestamp()))
+ .setAttributes(toAttributesProto(spanData.getAttributes()))
+ .setTimeEvents(
+ toTimeEventsProto(spanData.getAnnotations(), spanData.getMessageEvents()))
+ .setLinks(toLinksProto(spanData.getLinks()));
+
+ Kind kind = spanData.getKind();
+ if (kind != null) {
+ spanBuilder.setKind(toSpanKindProto(kind));
+ }
+
+ io.opencensus.trace.Status status = spanData.getStatus();
+ if (status != null) {
+ spanBuilder.setStatus(toStatusProto(status));
+ }
+
+ Timestamp end = spanData.getEndTimestamp();
+ if (end != null) {
+ spanBuilder.setEndTime(toTimestampProto(end));
+ }
+
+ Integer childSpanCount = spanData.getChildSpanCount();
+ if (childSpanCount != null) {
+ spanBuilder.setChildSpanCount(UInt32Value.newBuilder().setValue(childSpanCount).build());
+ }
+
+ Boolean hasRemoteParent = spanData.getHasRemoteParent();
+ if (hasRemoteParent != null) {
+ spanBuilder.setSameProcessAsParentSpan(BoolValue.of(!hasRemoteParent));
+ }
+
+ SpanId parentSpanId = spanData.getParentSpanId();
+ if (parentSpanId != null && parentSpanId.isValid()) {
+ spanBuilder.setParentSpanId(toByteString(parentSpanId.getBytes()));
+ }
+
+ return spanBuilder.build();
+ }
+
+ @VisibleForTesting
+ static ByteString toByteString(byte[] bytes) {
+ return ByteString.copyFrom(bytes);
+ }
+
+ private static Tracestate toTracestateProto(io.opencensus.trace.Tracestate tracestate) {
+ return Tracestate.newBuilder().addAllEntries(toEntriesProto(tracestate.getEntries())).build();
+ }
+
+ private static List<Entry> toEntriesProto(List<io.opencensus.trace.Tracestate.Entry> entries) {
+ List<Entry> entriesProto = new ArrayList<Entry>();
+ for (io.opencensus.trace.Tracestate.Entry entry : entries) {
+ entriesProto.add(
+ Entry.newBuilder().setKey(entry.getKey()).setValue(entry.getValue()).build());
+ }
+ return entriesProto;
+ }
+
+ private static SpanKind toSpanKindProto(Kind kind) {
+ switch (kind) {
+ case CLIENT:
+ return SpanKind.CLIENT;
+ case SERVER:
+ return SpanKind.SERVER;
+ }
+ return SpanKind.UNRECOGNIZED;
+ }
+
+ private static Span.TimeEvents toTimeEventsProto(
+ TimedEvents<Annotation> annotationTimedEvents,
+ TimedEvents<io.opencensus.trace.MessageEvent> messageEventTimedEvents) {
+ Span.TimeEvents.Builder timeEventsBuilder = Span.TimeEvents.newBuilder();
+ timeEventsBuilder.setDroppedAnnotationsCount(annotationTimedEvents.getDroppedEventsCount());
+ for (TimedEvent<Annotation> annotation : annotationTimedEvents.getEvents()) {
+ timeEventsBuilder.addTimeEvent(toTimeAnnotationProto(annotation));
+ }
+ timeEventsBuilder.setDroppedMessageEventsCount(messageEventTimedEvents.getDroppedEventsCount());
+ for (TimedEvent<io.opencensus.trace.MessageEvent> networkEvent :
+ messageEventTimedEvents.getEvents()) {
+ timeEventsBuilder.addTimeEvent(toTimeMessageEventProto(networkEvent));
+ }
+ return timeEventsBuilder.build();
+ }
+
+ private static TimeEvent toTimeAnnotationProto(TimedEvent<Annotation> timedEvent) {
+ TimeEvent.Builder timeEventBuilder =
+ TimeEvent.newBuilder().setTime(toTimestampProto(timedEvent.getTimestamp()));
+ Annotation annotation = timedEvent.getEvent();
+ timeEventBuilder.setAnnotation(
+ TimeEvent.Annotation.newBuilder()
+ .setDescription(toTruncatableStringProto(annotation.getDescription()))
+ .setAttributes(toAttributesBuilderProto(annotation.getAttributes(), 0))
+ .build());
+ return timeEventBuilder.build();
+ }
+
+ private static TimeEvent toTimeMessageEventProto(
+ TimedEvent<io.opencensus.trace.MessageEvent> timedEvent) {
+ TimeEvent.Builder timeEventBuilder =
+ TimeEvent.newBuilder().setTime(toTimestampProto(timedEvent.getTimestamp()));
+ io.opencensus.trace.MessageEvent messageEvent = timedEvent.getEvent();
+ timeEventBuilder.setMessageEvent(
+ TimeEvent.MessageEvent.newBuilder()
+ .setId(messageEvent.getMessageId())
+ .setCompressedSize(messageEvent.getCompressedMessageSize())
+ .setUncompressedSize(messageEvent.getUncompressedMessageSize())
+ .setType(toMessageEventTypeProto(messageEvent))
+ .build());
+ return timeEventBuilder.build();
+ }
+
+ private static TimeEvent.MessageEvent.Type toMessageEventTypeProto(
+ io.opencensus.trace.MessageEvent messageEvent) {
+ if (messageEvent.getType() == Type.RECEIVED) {
+ return MessageEvent.Type.RECEIVED;
+ } else {
+ return MessageEvent.Type.SENT;
+ }
+ }
+
+ private static Attributes toAttributesProto(
+ io.opencensus.trace.export.SpanData.Attributes attributes) {
+ Attributes.Builder attributesBuilder =
+ toAttributesBuilderProto(
+ attributes.getAttributeMap(), attributes.getDroppedAttributesCount());
+ return attributesBuilder.build();
+ }
+
+ private static Attributes.Builder toAttributesBuilderProto(
+ Map<String, io.opencensus.trace.AttributeValue> attributes, int droppedAttributesCount) {
+ Attributes.Builder attributesBuilder =
+ Attributes.newBuilder().setDroppedAttributesCount(droppedAttributesCount);
+ for (Map.Entry<String, io.opencensus.trace.AttributeValue> label : attributes.entrySet()) {
+ AttributeValue value = toAttributeValueProto(label.getValue());
+ if (value != null) {
+ attributesBuilder.putAttributeMap(label.getKey(), value);
+ }
+ }
+ return attributesBuilder;
+ }
+
+ @javax.annotation.Nullable
+ private static AttributeValue toAttributeValueProto(
+ io.opencensus.trace.AttributeValue attributeValue) {
+ return attributeValue.match(
+ stringAttributeValueFunction,
+ booleanAttributeValueFunction,
+ longAttributeValueFunction,
+ doubleAttributeValueFunction,
+ Functions.</*@Nullable*/ AttributeValue>returnNull());
+ }
+
+ private static Status toStatusProto(io.opencensus.trace.Status status) {
+ Status.Builder statusBuilder = Status.newBuilder().setCode(status.getCanonicalCode().value());
+ if (status.getDescription() != null) {
+ statusBuilder.setMessage(status.getDescription());
+ }
+ return statusBuilder.build();
+ }
+
+ @VisibleForTesting
+ static TruncatableString toTruncatableStringProto(String string) {
+ return TruncatableString.newBuilder().setValue(string).setTruncatedByteCount(0).build();
+ }
+
+ private static com.google.protobuf.Timestamp toTimestampProto(Timestamp timestamp) {
+ return com.google.protobuf.Timestamp.newBuilder()
+ .setSeconds(timestamp.getSeconds())
+ .setNanos(timestamp.getNanos())
+ .build();
+ }
+
+ private static Link.Type toLinkTypeProto(io.opencensus.trace.Link.Type type) {
+ if (type == io.opencensus.trace.Link.Type.PARENT_LINKED_SPAN) {
+ return Link.Type.PARENT_LINKED_SPAN;
+ } else {
+ return Link.Type.CHILD_LINKED_SPAN;
+ }
+ }
+
+ @SuppressWarnings("DefaultCharset")
+ private static Link toLinkProto(io.opencensus.trace.Link link) {
+ return Link.newBuilder()
+ .setTraceId(toByteString(link.getTraceId().getBytes()))
+ .setSpanId(toByteString(link.getSpanId().getBytes()))
+ .setType(toLinkTypeProto(link.getType()))
+ .setAttributes(toAttributesBuilderProto(link.getAttributes(), 0))
+ .build();
+ }
+
+ private static Links toLinksProto(io.opencensus.trace.export.SpanData.Links links) {
+ final Links.Builder linksBuilder =
+ Links.newBuilder().setDroppedLinksCount(links.getDroppedLinksCount());
+ for (io.opencensus.trace.Link link : links.getLinks()) {
+ linksBuilder.addLink(toLinkProto(link));
+ }
+ return linksBuilder.build();
+ }
+
+ /**
+ * Converts {@link TraceParams} to {@link TraceConfig}.
+ *
+ * @param traceParams the {@code TraceParams}.
+ * @return {@code TraceConfig}.
+ * @since 0.17
+ */
+ public static TraceConfig toTraceConfigProto(TraceParams traceParams) {
+ TraceConfig.Builder traceConfigProtoBuilder = TraceConfig.newBuilder();
+ Sampler librarySampler = traceParams.getSampler();
+
+ if (Samplers.alwaysSample().equals(librarySampler)) {
+ traceConfigProtoBuilder.setConstantSampler(
+ ConstantSampler.newBuilder().setDecision(true).build());
+ } else if (Samplers.neverSample().equals(librarySampler)) {
+ traceConfigProtoBuilder.setConstantSampler(
+ ConstantSampler.newBuilder().setDecision(false).build());
+ } else {
+ // TODO: consider exposing the sampling probability of ProbabilitySampler.
+ double samplingProbability = parseSamplingProbability(librarySampler);
+ traceConfigProtoBuilder.setProbabilitySampler(
+ ProbabilitySampler.newBuilder().setSamplingProbability(samplingProbability).build());
+ } // TODO: add support for RateLimitingSampler.
+
+ return traceConfigProtoBuilder.build();
+ }
+
+ private static double parseSamplingProbability(Sampler sampler) {
+ String description = sampler.getDescription();
+ // description follows format "ProbabilitySampler{%.6f}", samplingProbability.
+ int leftParenIndex = description.indexOf("{");
+ int rightParenIndex = description.indexOf("}");
+ return Double.parseDouble(description.substring(leftParenIndex + 1, rightParenIndex));
+ }
+
+ /**
+ * Converts {@link TraceConfig} to {@link TraceParams}.
+ *
+ * @param traceConfigProto {@code TraceConfig}.
+ * @param currentTraceParams current {@code TraceParams}.
+ * @return updated {@code TraceParams}.
+ * @since 0.17
+ */
+ public static TraceParams fromTraceConfigProto(
+ TraceConfig traceConfigProto, TraceParams currentTraceParams) {
+ TraceParams.Builder builder = currentTraceParams.toBuilder();
+ if (traceConfigProto.hasConstantSampler()) {
+ ConstantSampler constantSampler = traceConfigProto.getConstantSampler();
+ if (Boolean.TRUE.equals(constantSampler.getDecision())) {
+ builder.setSampler(Samplers.alwaysSample());
+ } else {
+ builder.setSampler(Samplers.neverSample());
+ }
+ } else if (traceConfigProto.hasProbabilitySampler()) {
+ builder.setSampler(
+ Samplers.probabilitySampler(
+ traceConfigProto.getProbabilitySampler().getSamplingProbability()));
+ } // TODO: add support for RateLimitingSampler.
+ return builder.build();
+ }
+
+ private TraceProtoUtils() {}
+}
diff --git a/contrib/opencensus_proto_util/src/test/java/io/opencensus/contrib/opencensus/proto/util/TraceProtoUtilsTest.java b/contrib/opencensus_proto_util/src/test/java/io/opencensus/contrib/opencensus/proto/util/TraceProtoUtilsTest.java
new file mode 100644
index 00000000..37908f6b
--- /dev/null
+++ b/contrib/opencensus_proto_util/src/test/java/io/opencensus/contrib/opencensus/proto/util/TraceProtoUtilsTest.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2018, OpenCensus Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.opencensus.contrib.opencensus.proto.util;
+
+import static com.google.common.truth.Truth.assertThat;
+import static io.opencensus.contrib.opencensus.proto.util.TraceProtoUtils.toByteString;
+import static io.opencensus.contrib.opencensus.proto.util.TraceProtoUtils.toTruncatableStringProto;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.protobuf.BoolValue;
+import com.google.protobuf.UInt32Value;
+import io.opencensus.common.Timestamp;
+import io.opencensus.proto.trace.v1.AttributeValue;
+import io.opencensus.proto.trace.v1.ConstantSampler;
+import io.opencensus.proto.trace.v1.ProbabilitySampler;
+import io.opencensus.proto.trace.v1.Span;
+import io.opencensus.proto.trace.v1.Span.SpanKind;
+import io.opencensus.proto.trace.v1.Span.TimeEvent;
+import io.opencensus.proto.trace.v1.Span.TimeEvent.MessageEvent;
+import io.opencensus.proto.trace.v1.TraceConfig;
+import io.opencensus.trace.Annotation;
+import io.opencensus.trace.Link;
+import io.opencensus.trace.Sampler;
+import io.opencensus.trace.Span.Kind;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.Status;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.Tracestate;
+import io.opencensus.trace.config.TraceParams;
+import io.opencensus.trace.export.SpanData;
+import io.opencensus.trace.export.SpanData.TimedEvent;
+import io.opencensus.trace.export.SpanData.TimedEvents;
+import io.opencensus.trace.samplers.Samplers;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link TraceProtoUtils}. */
+@RunWith(JUnit4.class)
+public class TraceProtoUtilsTest {
+
+ private static final TraceParams DEFAULT_PARAMS = TraceParams.DEFAULT;
+
+ private static final Timestamp startTimestamp = Timestamp.create(123, 456);
+ private static final Timestamp eventTimestamp1 = Timestamp.create(123, 457);
+ private static final Timestamp eventTimestamp2 = Timestamp.create(123, 458);
+ private static final Timestamp eventTimestamp3 = Timestamp.create(123, 459);
+ private static final Timestamp endTimestamp = Timestamp.create(123, 460);
+
+ private static final String TRACE_ID = "4bf92f3577b34da6a3ce929d0e0e4736";
+ private static final String SPAN_ID = "24aa0b2d371f48c9";
+ private static final String PARENT_SPAN_ID = "71da8d631536f5f1";
+ private static final String SPAN_NAME = "MySpanName";
+ private static final String ANNOTATION_TEXT = "MyAnnotationText";
+ private static final String ATTRIBUTE_KEY_1 = "MyAttributeKey1";
+ private static final String ATTRIBUTE_KEY_2 = "MyAttributeKey2";
+
+ private static final String FIRST_KEY = "key_1";
+ private static final String SECOND_KEY = "key_2";
+ private static final String FIRST_VALUE = "value.1";
+ private static final String SECOND_VALUE = "value.2";
+ private static final Tracestate multiValueTracestate =
+ Tracestate.builder().set(FIRST_KEY, FIRST_VALUE).set(SECOND_KEY, SECOND_VALUE).build();
+
+ private static final int DROPPED_ATTRIBUTES_COUNT = 1;
+ private static final int DROPPED_ANNOTATIONS_COUNT = 2;
+ private static final int DROPPED_NETWORKEVENTS_COUNT = 3;
+ private static final int DROPPED_LINKS_COUNT = 4;
+ private static final int CHILD_SPAN_COUNT = 13;
+
+ private static final Annotation annotation = Annotation.fromDescription(ANNOTATION_TEXT);
+ private static final io.opencensus.trace.MessageEvent recvMessageEvent =
+ io.opencensus.trace.MessageEvent.builder(io.opencensus.trace.MessageEvent.Type.RECEIVED, 1)
+ .build();
+ private static final io.opencensus.trace.MessageEvent sentMessageEvent =
+ io.opencensus.trace.MessageEvent.builder(io.opencensus.trace.MessageEvent.Type.SENT, 1)
+ .build();
+ private static final Status status = Status.DEADLINE_EXCEEDED.withDescription("TooSlow");
+ private static final SpanId parentSpanId = SpanId.fromLowerBase16(PARENT_SPAN_ID);
+ private static final SpanId spanId = SpanId.fromLowerBase16(SPAN_ID);
+ private static final TraceId traceId = TraceId.fromLowerBase16(TRACE_ID);
+ private static final TraceOptions traceOptions = TraceOptions.DEFAULT;
+ private static final SpanContext spanContext =
+ SpanContext.create(traceId, spanId, traceOptions, multiValueTracestate);
+
+ private static final List<TimedEvent<Annotation>> annotationsList =
+ ImmutableList.of(
+ SpanData.TimedEvent.create(eventTimestamp1, annotation),
+ SpanData.TimedEvent.create(eventTimestamp3, annotation));
+ private static final List<TimedEvent<io.opencensus.trace.MessageEvent>> networkEventsList =
+ ImmutableList.of(
+ SpanData.TimedEvent.create(eventTimestamp1, recvMessageEvent),
+ SpanData.TimedEvent.create(eventTimestamp2, sentMessageEvent));
+ private static final List<Link> linksList =
+ ImmutableList.of(Link.fromSpanContext(spanContext, Link.Type.CHILD_LINKED_SPAN));
+
+ private static final SpanData.Attributes attributes =
+ SpanData.Attributes.create(
+ ImmutableMap.of(
+ ATTRIBUTE_KEY_1,
+ io.opencensus.trace.AttributeValue.longAttributeValue(10L),
+ ATTRIBUTE_KEY_2,
+ io.opencensus.trace.AttributeValue.booleanAttributeValue(true)),
+ DROPPED_ATTRIBUTES_COUNT);
+ private static final TimedEvents<Annotation> annotations =
+ TimedEvents.create(annotationsList, DROPPED_ANNOTATIONS_COUNT);
+ private static final TimedEvents<io.opencensus.trace.MessageEvent> messageEvents =
+ TimedEvents.create(networkEventsList, DROPPED_NETWORKEVENTS_COUNT);
+ private static final SpanData.Links links = SpanData.Links.create(linksList, DROPPED_LINKS_COUNT);
+
+ @SuppressWarnings("DefaultCharset")
+ @Test
+ public void toSpanProto() {
+ SpanData spanData =
+ SpanData.create(
+ spanContext,
+ parentSpanId,
+ /* hasRemoteParent= */ false,
+ SPAN_NAME,
+ Kind.CLIENT,
+ startTimestamp,
+ attributes,
+ annotations,
+ messageEvents,
+ links,
+ CHILD_SPAN_COUNT,
+ status,
+ endTimestamp);
+ TimeEvent annotationTimeEvent1 =
+ TimeEvent.newBuilder()
+ .setAnnotation(
+ TimeEvent.Annotation.newBuilder()
+ .setDescription(toTruncatableStringProto(ANNOTATION_TEXT))
+ .setAttributes(Span.Attributes.newBuilder().build())
+ .build())
+ .setTime(
+ com.google.protobuf.Timestamp.newBuilder()
+ .setSeconds(eventTimestamp1.getSeconds())
+ .setNanos(eventTimestamp1.getNanos())
+ .build())
+ .build();
+ TimeEvent annotationTimeEvent2 =
+ TimeEvent.newBuilder()
+ .setAnnotation(
+ TimeEvent.Annotation.newBuilder()
+ .setDescription(toTruncatableStringProto(ANNOTATION_TEXT))
+ .setAttributes(Span.Attributes.newBuilder().build())
+ .build())
+ .setTime(
+ com.google.protobuf.Timestamp.newBuilder()
+ .setSeconds(eventTimestamp3.getSeconds())
+ .setNanos(eventTimestamp3.getNanos())
+ .build())
+ .build();
+
+ TimeEvent sentTimeEvent =
+ TimeEvent.newBuilder()
+ .setMessageEvent(
+ TimeEvent.MessageEvent.newBuilder()
+ .setType(MessageEvent.Type.SENT)
+ .setId(sentMessageEvent.getMessageId()))
+ .setTime(
+ com.google.protobuf.Timestamp.newBuilder()
+ .setSeconds(eventTimestamp2.getSeconds())
+ .setNanos(eventTimestamp2.getNanos())
+ .build())
+ .build();
+ TimeEvent recvTimeEvent =
+ TimeEvent.newBuilder()
+ .setMessageEvent(
+ TimeEvent.MessageEvent.newBuilder()
+ .setType(MessageEvent.Type.RECEIVED)
+ .setId(recvMessageEvent.getMessageId()))
+ .setTime(
+ com.google.protobuf.Timestamp.newBuilder()
+ .setSeconds(eventTimestamp1.getSeconds())
+ .setNanos(eventTimestamp1.getNanos())
+ .build())
+ .build();
+
+ Span.Links spanLinks =
+ Span.Links.newBuilder()
+ .setDroppedLinksCount(DROPPED_LINKS_COUNT)
+ .addLink(
+ Span.Link.newBuilder()
+ .setType(Span.Link.Type.CHILD_LINKED_SPAN)
+ .setTraceId(toByteString(traceId.getBytes()))
+ .setSpanId(toByteString(spanId.getBytes()))
+ .setAttributes(Span.Attributes.newBuilder().build())
+ .build())
+ .build();
+
+ io.opencensus.proto.trace.v1.Status spanStatus =
+ io.opencensus.proto.trace.v1.Status.newBuilder()
+ .setCode(com.google.rpc.Code.DEADLINE_EXCEEDED.getNumber())
+ .setMessage("TooSlow")
+ .build();
+
+ com.google.protobuf.Timestamp startTime =
+ com.google.protobuf.Timestamp.newBuilder()
+ .setSeconds(startTimestamp.getSeconds())
+ .setNanos(startTimestamp.getNanos())
+ .build();
+ com.google.protobuf.Timestamp endTime =
+ com.google.protobuf.Timestamp.newBuilder()
+ .setSeconds(endTimestamp.getSeconds())
+ .setNanos(endTimestamp.getNanos())
+ .build();
+
+ Span span = TraceProtoUtils.toSpanProto(spanData);
+ assertThat(span.getName()).isEqualTo(toTruncatableStringProto(SPAN_NAME));
+ assertThat(span.getTraceId()).isEqualTo(toByteString(traceId.getBytes()));
+ assertThat(span.getSpanId()).isEqualTo(toByteString(spanId.getBytes()));
+ assertThat(span.getParentSpanId()).isEqualTo(toByteString(parentSpanId.getBytes()));
+ assertThat(span.getStartTime()).isEqualTo(startTime);
+ assertThat(span.getEndTime()).isEqualTo(endTime);
+ assertThat(span.getKind()).isEqualTo(SpanKind.CLIENT);
+ assertThat(span.getAttributes().getDroppedAttributesCount())
+ .isEqualTo(DROPPED_ATTRIBUTES_COUNT);
+ // The generated attributes map contains more values (e.g. agent). We only test what we added.
+ assertThat(span.getAttributes().getAttributeMapMap())
+ .containsEntry(ATTRIBUTE_KEY_1, AttributeValue.newBuilder().setIntValue(10L).build());
+ assertThat(span.getAttributes().getAttributeMapMap())
+ .containsEntry(ATTRIBUTE_KEY_2, AttributeValue.newBuilder().setBoolValue(true).build());
+ assertThat(span.getTimeEvents().getDroppedMessageEventsCount())
+ .isEqualTo(DROPPED_NETWORKEVENTS_COUNT);
+ assertThat(span.getTimeEvents().getDroppedAnnotationsCount())
+ .isEqualTo(DROPPED_ANNOTATIONS_COUNT);
+ assertThat(span.getTimeEvents().getTimeEventList())
+ .containsAllOf(annotationTimeEvent1, annotationTimeEvent2, sentTimeEvent, recvTimeEvent);
+ assertThat(span.getLinks()).isEqualTo(spanLinks);
+ assertThat(span.getStatus()).isEqualTo(spanStatus);
+ assertThat(span.getSameProcessAsParentSpan()).isEqualTo(BoolValue.of(true));
+ assertThat(span.getChildSpanCount())
+ .isEqualTo(UInt32Value.newBuilder().setValue(CHILD_SPAN_COUNT).build());
+ }
+
+ @Test
+ public void toTraceConfigProto_AlwaysSampler() {
+ assertThat(TraceProtoUtils.toTraceConfigProto(getTraceParams(Samplers.alwaysSample())))
+ .isEqualTo(
+ TraceConfig.newBuilder()
+ .setConstantSampler(ConstantSampler.newBuilder().setDecision(true).build())
+ .build());
+ }
+
+ @Test
+ public void toTraceConfigProto_NeverSampler() {
+ assertThat(TraceProtoUtils.toTraceConfigProto(getTraceParams(Samplers.neverSample())))
+ .isEqualTo(
+ TraceConfig.newBuilder()
+ .setConstantSampler(ConstantSampler.newBuilder().setDecision(false).build())
+ .build());
+ }
+
+ @Test
+ public void toTraceConfigProto_ProbabilitySampler() {
+ assertThat(TraceProtoUtils.toTraceConfigProto(getTraceParams(Samplers.probabilitySampler(0.5))))
+ .isEqualTo(
+ TraceConfig.newBuilder()
+ .setProbabilitySampler(
+ ProbabilitySampler.newBuilder().setSamplingProbability(0.5).build())
+ .build());
+ }
+
+ @Test
+ public void fromTraceConfigProto_AlwaysSampler() {
+ TraceConfig traceConfig =
+ TraceConfig.newBuilder()
+ .setConstantSampler(ConstantSampler.newBuilder().setDecision(true).build())
+ .build();
+ assertThat(TraceProtoUtils.fromTraceConfigProto(traceConfig, DEFAULT_PARAMS).getSampler())
+ .isEqualTo(Samplers.alwaysSample());
+ }
+
+ @Test
+ public void fromTraceConfigProto_NeverSampler() {
+ TraceConfig traceConfig =
+ TraceConfig.newBuilder()
+ .setConstantSampler(ConstantSampler.newBuilder().setDecision(false).build())
+ .build();
+ assertThat(TraceProtoUtils.fromTraceConfigProto(traceConfig, DEFAULT_PARAMS).getSampler())
+ .isEqualTo(Samplers.neverSample());
+ }
+
+ @Test
+ public void fromTraceConfigProto_ProbabilitySampler() {
+ TraceConfig traceConfig =
+ TraceConfig.newBuilder()
+ .setProbabilitySampler(
+ ProbabilitySampler.newBuilder().setSamplingProbability(0.01).build())
+ .build();
+ assertThat(TraceProtoUtils.fromTraceConfigProto(traceConfig, DEFAULT_PARAMS).getSampler())
+ .isEqualTo(Samplers.probabilitySampler(0.01));
+ }
+
+ private static TraceParams getTraceParams(Sampler sampler) {
+ return DEFAULT_PARAMS.toBuilder().setSampler(sampler).build();
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index 4a92661e..dda7557f 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -22,6 +22,7 @@ include ":opencensus-contrib-http-util"
include ":opencensus-contrib-log-correlation-log4j2"
include ":opencensus-contrib-log-correlation-stackdriver"
include ":opencensus-contrib-monitored-resource-util"
+include ":opencensus-contrib-opencensus-proto-util"
include ":opencensus-contrib-spring"
include ":opencensus-contrib-spring-sleuth-v1x"
@@ -43,6 +44,8 @@ project(':opencensus-contrib-log-correlation-stackdriver').projectDir =
"$rootDir/contrib/log_correlation/stackdriver" as File
project(':opencensus-contrib-monitored-resource-util').projectDir =
"$rootDir/contrib/monitored_resource_util" as File
+project(':opencensus-contrib-opencensus-proto-util').projectDir =
+ "$rootDir/contrib/opencensus_proto_util" as File
project(':opencensus-contrib-spring').projectDir = "$rootDir/contrib/spring" as File
project(':opencensus-contrib-spring-sleuth-v1x').projectDir =
"$rootDir/contrib/spring_sleuth_v1x" as File