aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Lange <lange.fabian@gmail.com>2018-02-09 14:33:30 -0500
committerBogdan Drutu <bdrutu@google.com>2018-02-09 11:33:30 -0800
commit17a1a1a0d19739a19b449c6d28817687ccd5b9d4 (patch)
treeb5ee100816bc7f762d19a21359538f93118af596
parentad1a3fb10f5d8a1d958a3d82be264e22ca9e5c5c (diff)
downloadopencensus-java-17a1a1a0d19739a19b449c6d28817687ccd5b9d4.tar.gz
First proof of concept for Instana integration (#972)
-rw-r--r--README.md12
-rw-r--r--RELEASING.md1
-rw-r--r--build.gradle1
-rw-r--r--exporters/trace/instana/README.md73
-rw-r--r--exporters/trace/instana/build.gradle14
-rw-r--r--exporters/trace/instana/src/main/java/io/opencensus/exporter/trace/instana/InstanaExporterHandler.java228
-rw-r--r--exporters/trace/instana/src/main/java/io/opencensus/exporter/trace/instana/InstanaTraceExporter.java103
-rw-r--r--exporters/trace/instana/src/test/java/io/opencensus/exporter/trace/instana/InstanaExporterHandlerTest.java100
-rw-r--r--exporters/trace/instana/src/test/java/io/opencensus/exporter/trace/instana/InstanaTraceExporterTest.java54
-rw-r--r--settings.gradle3
10 files changed, 584 insertions, 5 deletions
diff --git a/README.md b/README.md
index ccc254e4..93b681ca 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
[![Coverage Status][codecov-image]][codecov-url]
-OpenCensus is a toolkit for collecting application performance and behavior data. It currently
+OpenCensus is a toolkit for collecting application performance and behavior data. It currently
includes 3 apis: stats, tracing and tags.
The library is in alpha stage and the API is subject to change.
@@ -53,13 +53,13 @@ public final class MyClassWithTracing {
doFinalWork();
}
}
-
+
private static void doInitialWork() {
// ...
tracer.getCurrentSpan().addAnnotation("Important.");
// ...
}
-
+
private static void doFinalWork() {
// ...
tracer.getCurrentSpan().addAnnotation("More important.");
@@ -74,7 +74,7 @@ TODO
## Quickstart for Applications
-Besides recording tracing/stats events the application also need to link the implementation,
+Besides recording tracing/stats events the application also need to link the implementation,
setup exporters, and debugging [Z-Pages](https://github.com/census-instrumentation/opencensus-java/tree/master/contrib/zpages).
### Add the dependencies to your project
@@ -105,6 +105,7 @@ runtime 'io.opencensus:opencensus-impl:0.11.1'
### How to setup exporters?
#### Trace exporters
+* [Instana][TraceExporterInstana]
* [Logging][TraceExporterLogging]
* [Stackdriver][TraceExporterStackdriver]
* [Zipkin][TraceExporterZipkin]
@@ -115,7 +116,7 @@ runtime 'io.opencensus:opencensus-impl:0.11.1'
### How to setup debugging Z-Pages?
-If the application owner wants to export in-process tracing and stats data via HTML debugging pages
+If the application owner wants to export in-process tracing and stats data via HTML debugging pages
see this [link](https://github.com/census-instrumentation/opencensus-java/tree/master/contrib/zpages#quickstart).
[travis-image]: https://travis-ci.org/census-instrumentation/opencensus-java.svg?branch=master
@@ -130,6 +131,7 @@ see this [link](https://github.com/census-instrumentation/opencensus-java/tree/m
[gitter-url]: https://gitter.im/census-instrumentation/lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[codecov-image]: https://codecov.io/gh/census-instrumentation/opencensus-java/branch/master/graph/badge.svg
[codecov-url]: https://codecov.io/gh/census-instrumentation/opencensus-java/branch/master/
+[TraceExporterInstana]: https://github.com/census-instrumentation/opencensus-java/tree/master/exporters/trace/instana#quickstart
[TraceExporterLogging]: https://github.com/census-instrumentation/opencensus-java/tree/master/exporters/trace/logging#quickstart
[TraceExporterStackdriver]: https://github.com/census-instrumentation/opencensus-java/tree/master/exporters/trace/stackdriver#quickstart
[TraceExporterZipkin]: https://github.com/census-instrumentation/opencensus-java/tree/master/exporters/trace/zipkin#quickstart
diff --git a/RELEASING.md b/RELEASING.md
index 9067de65..2d42ed0b 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -221,6 +221,7 @@ $ README_FILES=(
contrib/zpages/README.md
exporters/stats/signalfx/README.md
exporters/stats/stackdriver/README.md
+ exporters/trace/instana/README.md
exporters/trace/logging/README.md
exporters/trace/stackdriver/README.md
exporters/trace/zipkin/README.md
diff --git a/build.gradle b/build.gradle
index 4e467377..a305e91a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -335,6 +335,7 @@ subprojects {
'opencensus-contrib-zpages',
'opencensus-exporter-stats-signalfx',
'opencensus-exporter-stats-stackdriver',
+ 'opencensus-exporter-trace-instana',
'opencensus-exporter-trace-logging',
'opencensus-exporter-trace-stackdriver',
'opencensus-exporter-trace-zipkin',
diff --git a/exporters/trace/instana/README.md b/exporters/trace/instana/README.md
new file mode 100644
index 00000000..e75bfb2c
--- /dev/null
+++ b/exporters/trace/instana/README.md
@@ -0,0 +1,73 @@
+# OpenCensus Instana Trace Exporter
+[![Build Status][travis-image]][travis-url]
+[![Windows Build Status][appveyor-image]][appveyor-url]
+[![Maven Central][maven-image]][maven-url]
+
+The *OpenCensus Instana Trace Exporter* is a trace exporter that exports
+data to Instana. [Instana](http://www.instana.com/) Instana is a distributed
+tracing system.
+
+## Quickstart
+
+### Prerequisites
+
+[Instana](http://www.instana.com/) forwards traces exported by applications
+instrumented with Census to its backend using the Instana agent processes as proxy.
+If the agent is used on the same host as Census, please take care to deactivate
+automatic tracing.
+
+
+### Hello Stan
+
+#### Add the dependencies to your project
+
+For Maven add to your `pom.xml`:
+```xml
+<dependencies>
+ <dependency>
+ <groupId>io.opencensus</groupId>
+ <artifactId>opencensus-api</artifactId>
+ <version>0.12.0</version>
+ </dependency>
+ <dependency>
+ <groupId>io.opencensus</groupId>
+ <artifactId>opencensus-exporter-trace-instana</artifactId>
+ <version>0.12.0</version>
+ </dependency>
+ <dependency>
+ <groupId>io.opencensus</groupId>
+ <artifactId>opencensus-impl</artifactId>
+ <version>0.12.0</version>
+ <scope>runtime</scope>
+ </dependency>
+</dependencies>
+```
+
+For Gradle add to your dependencies:
+```groovy
+compile 'io.opencensus:opencensus-api:0.12.0'
+compile 'io.opencensus:opencensus-exporter-trace-instana:0.12.0'
+runtime 'io.opencensus:opencensus-impl:0.12.0'
+```
+
+#### Register the exporter
+
+```java
+public class MyMainClass {
+ public static void main(String[] args) throws Exception {
+ InstanaTraceExporter.createAndRegister("http://localhost:42699/com.instana.plugin.generic.trace");
+ // ...
+ }
+}
+```
+
+#### Java Versions
+
+Java 6 or above is required for using this exporter.
+
+[travis-image]: https://travis-ci.org/census-instrumentation/opencensus-java.svg?branch=master
+[travis-url]: https://travis-ci.org/census-instrumentation/opencensus-java
+[appveyor-image]: https://ci.appveyor.com/api/projects/status/hxthmpkxar4jq4be/branch/master?svg=true
+[appveyor-url]: https://ci.appveyor.com/project/opencensusjavateam/opencensus-java/branch/master
+[maven-image]: https://maven-badges.herokuapp.com/maven-central/io.opencensus/opencensus-exporter-trace-instana/badge.svg
+[maven-url]: https://maven-badges.herokuapp.com/maven-central/io.opencensus/opencensus-exporter-trace-instana
diff --git a/exporters/trace/instana/build.gradle b/exporters/trace/instana/build.gradle
new file mode 100644
index 00000000..edd54fc5
--- /dev/null
+++ b/exporters/trace/instana/build.gradle
@@ -0,0 +1,14 @@
+description = 'OpenCensus Trace Instana Exporter'
+
+[compileJava, compileTestJava].each() {
+ it.sourceCompatibility = 1.6
+ it.targetCompatibility = 1.6
+}
+
+dependencies {
+ compile project(':opencensus-api')
+
+ testCompile project(':opencensus-api')
+
+ signature "org.codehaus.mojo.signature:java16:+@signature"
+}
diff --git a/exporters/trace/instana/src/main/java/io/opencensus/exporter/trace/instana/InstanaExporterHandler.java b/exporters/trace/instana/src/main/java/io/opencensus/exporter/trace/instana/InstanaExporterHandler.java
new file mode 100644
index 00000000..aa770674
--- /dev/null
+++ b/exporters/trace/instana/src/main/java/io/opencensus/exporter/trace/instana/InstanaExporterHandler.java
@@ -0,0 +1,228 @@
+/*
+ * 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.exporter.trace.instana;
+
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import com.google.common.io.BaseEncoding;
+import io.opencensus.common.Duration;
+import io.opencensus.common.Function;
+import io.opencensus.common.Functions;
+import io.opencensus.common.Scope;
+import io.opencensus.common.Timestamp;
+import io.opencensus.trace.AttributeValue;
+import io.opencensus.trace.Sampler;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.Status;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.Tracer;
+import io.opencensus.trace.Tracing;
+import io.opencensus.trace.export.SpanData;
+import io.opencensus.trace.export.SpanExporter;
+import io.opencensus.trace.samplers.Samplers;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Logger;
+
+/*
+ * Exports to an Instana agent acting as proxy to the Instana backend (and handling authentication)
+ * Uses the Trace SDK documented:
+ * https://github.com/instana/instana-java-sdk#instana-trace-webservice
+ *
+ * Currently does a blocking export using HttpUrlConnection.
+ * Also uses a StringBuilder to build JSON.
+ * Both can be improved should 3rd party library usage not be a concern.
+ *
+ * Major TODO is the limitation of Instana to only suport 64bit trace ids, which will be resolved.
+ * Until then it is crossing fingers and treating it as 50% sampler :).
+ */
+final class InstanaExporterHandler extends SpanExporter.Handler {
+
+ private static final Tracer tracer = Tracing.getTracer();
+ private static final Sampler probabilitySpampler = Samplers.probabilitySampler(0.0001);
+ static final Logger logger = Logger.getLogger(InstanaExporterHandler.class.getName());
+
+ private static final Function<Object, String> RETURN_STRING =
+ new Function<Object, String>() {
+ @Override
+ public String apply(Object input) {
+ return input.toString();
+ }
+ };
+
+ private final URL agentEndpoint;
+
+ InstanaExporterHandler(URL agentEndpoint) {
+ this.agentEndpoint = agentEndpoint;
+ }
+
+ private static String encodeTraceId(TraceId traceId) {
+ return BaseEncoding.base16().lowerCase().encode(traceId.getBytes(), 0, 8);
+ }
+
+ private static String encodeSpanId(SpanId spanId) {
+ return BaseEncoding.base16().lowerCase().encode(spanId.getBytes());
+ }
+
+ private static String toSpanName(SpanData spanData) {
+ return spanData.getName();
+ }
+
+ private static String toSpanType(SpanData spanData) {
+ if (spanData.getParentSpanId() == null || Boolean.TRUE.equals(spanData.getHasRemoteParent())) {
+ return "ENTRY";
+ }
+
+ // This is a hack because the v2 API does not have SpanKind. When switch to v2 this will be
+ // fixed.
+ if (spanData.getName().startsWith("Sent.")) {
+ return "EXIT";
+ }
+
+ return "INTERMEDIATE";
+ }
+
+ private static long toMillis(Timestamp timestamp) {
+ return SECONDS.toMillis(timestamp.getSeconds()) + NANOSECONDS.toMillis(timestamp.getNanos());
+ }
+
+ private static long toMillis(Timestamp start, Timestamp end) {
+ Duration duration = end.subtractTimestamp(start);
+ return SECONDS.toMillis(duration.getSeconds()) + NANOSECONDS.toMillis(duration.getNanos());
+ }
+
+ private static String attributeValueToString(AttributeValue attributeValue) {
+ return attributeValue.match(
+ RETURN_STRING, RETURN_STRING, RETURN_STRING, Functions.<String>returnNull());
+ }
+
+ static String convertToJson(Collection<SpanData> spanDataList) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('[');
+ for (final SpanData span : spanDataList) {
+ final SpanContext spanContext = span.getContext();
+ final SpanId parentSpanId = span.getParentSpanId();
+ final Timestamp startTimestamp = span.getStartTimestamp();
+ final Timestamp endTimestamp = span.getEndTimestamp();
+ final Status status = span.getStatus();
+ if (status == null || endTimestamp == null) {
+ continue;
+ }
+ if (sb.length() > 1) {
+ sb.append(',');
+ }
+ sb.append('{');
+ sb.append("\"spanId\":\"").append(encodeSpanId(spanContext.getSpanId())).append("\",");
+ sb.append("\"traceId\":\"").append(encodeTraceId(spanContext.getTraceId())).append("\",");
+ if (parentSpanId != null) {
+ sb.append("\"parentId\":\"").append(encodeSpanId(parentSpanId)).append("\",");
+ }
+ sb.append("\"timestamp\":").append(toMillis(startTimestamp)).append(',');
+ sb.append("\"duration\":").append(toMillis(startTimestamp, endTimestamp)).append(',');
+ sb.append("\"name\":\"").append(toSpanName(span)).append("\",");
+ sb.append("\"type\":\"").append(toSpanType(span)).append('"');
+ if (!status.isOk()) {
+ sb.append(",\"error\":").append("true");
+ }
+ Map<String, AttributeValue> attributeMap = span.getAttributes().getAttributeMap();
+ if (attributeMap.size() > 0) {
+ StringBuilder dataSb = new StringBuilder();
+ dataSb.append('{');
+ for (Entry<String, AttributeValue> entry : attributeMap.entrySet()) {
+ if (dataSb.length() > 1) {
+ dataSb.append(',');
+ }
+ dataSb
+ .append("\"")
+ .append(entry.getKey())
+ .append("\":\"")
+ .append(attributeValueToString(entry.getValue()))
+ .append("\"");
+ }
+ dataSb.append('}');
+
+ sb.append(",\"data\":").append(dataSb);
+ }
+ sb.append('}');
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+ @Override
+ public void export(Collection<SpanData> spanDataList) {
+ // Start a new span with explicit 1/10000 sampling probability to avoid the case when user
+ // sets the default sampler to always sample and we get the gRPC span of the instana
+ // export call always sampled and go to an infinite loop.
+ Scope scope =
+ tracer.spanBuilder("ExportInstanaTraces").setSampler(probabilitySpampler).startScopedSpan();
+ try {
+ String json = convertToJson(spanDataList);
+
+ OutputStream outputStream = null;
+ InputStream inputStream = null;
+ try {
+ HttpURLConnection connection = (HttpURLConnection) agentEndpoint.openConnection();
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(true);
+ outputStream = connection.getOutputStream();
+ outputStream.write(json.getBytes(Charset.defaultCharset()));
+ outputStream.flush();
+ inputStream = connection.getInputStream();
+ if (connection.getResponseCode() != 200) {
+ tracer
+ .getCurrentSpan()
+ .setStatus(
+ Status.UNKNOWN.withDescription("Response " + connection.getResponseCode()));
+ }
+ } catch (IOException e) {
+ tracer
+ .getCurrentSpan()
+ .setStatus(
+ Status.UNKNOWN.withDescription(
+ e.getMessage() == null ? e.getClass().getSimpleName() : e.getMessage()));
+ // dropping span batch
+ } finally {
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ if (outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ } finally {
+ scope.close();
+ }
+ }
+}
diff --git a/exporters/trace/instana/src/main/java/io/opencensus/exporter/trace/instana/InstanaTraceExporter.java b/exporters/trace/instana/src/main/java/io/opencensus/exporter/trace/instana/InstanaTraceExporter.java
new file mode 100644
index 00000000..1f542cdd
--- /dev/null
+++ b/exporters/trace/instana/src/main/java/io/opencensus/exporter/trace/instana/InstanaTraceExporter.java
@@ -0,0 +1,103 @@
+/*
+ * 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.exporter.trace.instana;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.annotations.VisibleForTesting;
+import io.opencensus.trace.Tracing;
+import io.opencensus.trace.export.SpanExporter;
+import io.opencensus.trace.export.SpanExporter.Handler;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * An OpenCensus span exporter implementation which exports data to Instana.
+ *
+ * <p>Example of usage:
+ *
+ * <pre>{@code
+ * public static void main(String[] args) {
+ * InstanaTraceExporter.createAndRegister("http://localhost:42699/com.instana.plugin.generic.trace");
+ * ... // Do work.
+ * }
+ * }</pre>
+ */
+public final class InstanaTraceExporter {
+
+ private static final String REGISTER_NAME = InstanaTraceExporter.class.getName();
+ private static final Object monitor = new Object();
+
+ @GuardedBy("monitor")
+ @Nullable
+ private static Handler handler = null;
+
+ private InstanaTraceExporter() {}
+
+ /**
+ * Creates and registers the Instana Trace exporter to the OpenCensus library. Only one Instana
+ * exporter can be registered at any point.
+ *
+ * @param agentEndpoint Ex http://localhost:42699/com.instana.plugin.generic.trace
+ * @throws MalformedURLException if the agentEndpoint is not a valid http url.
+ * @throws IllegalStateException if a Instana exporter is already registered.
+ */
+ public static void createAndRegister(String agentEndpoint) throws MalformedURLException {
+ synchronized (monitor) {
+ checkState(handler == null, "Instana exporter is already registered.");
+ Handler newHandler = new InstanaExporterHandler(new URL(agentEndpoint));
+ handler = newHandler;
+ register(Tracing.getExportComponent().getSpanExporter(), newHandler);
+ }
+ }
+
+ /**
+ * Registers the {@code InstanaTraceExporter}.
+ *
+ * @param spanExporter the instance of the {@code SpanExporter} where this service is registered.
+ */
+ @VisibleForTesting
+ static void register(SpanExporter spanExporter, Handler handler) {
+ spanExporter.registerHandler(REGISTER_NAME, handler);
+ }
+
+ /**
+ * Unregisters the Instana Trace exporter from the OpenCensus library.
+ *
+ * @throws IllegalStateException if a Instana exporter is not registered.
+ */
+ public static void unregister() {
+ synchronized (monitor) {
+ checkState(handler != null, "Instana exporter is not registered.");
+ unregister(Tracing.getExportComponent().getSpanExporter());
+ handler = null;
+ }
+ }
+
+ /**
+ * Unregisters the {@code InstanaTraceExporter}.
+ *
+ * @param spanExporter the instance of the {@code SpanExporter} from where this service is
+ * unregistered.
+ */
+ @VisibleForTesting
+ static void unregister(SpanExporter spanExporter) {
+ spanExporter.unregisterHandler(REGISTER_NAME);
+ }
+}
diff --git a/exporters/trace/instana/src/test/java/io/opencensus/exporter/trace/instana/InstanaExporterHandlerTest.java b/exporters/trace/instana/src/test/java/io/opencensus/exporter/trace/instana/InstanaExporterHandlerTest.java
new file mode 100644
index 00000000..29d512d5
--- /dev/null
+++ b/exporters/trace/instana/src/test/java/io/opencensus/exporter/trace/instana/InstanaExporterHandlerTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.exporter.trace.instana;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import io.opencensus.common.Timestamp;
+import io.opencensus.trace.Annotation;
+import io.opencensus.trace.AttributeValue;
+import io.opencensus.trace.Link;
+import io.opencensus.trace.NetworkEvent;
+import io.opencensus.trace.NetworkEvent.Type;
+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.export.SpanData;
+import io.opencensus.trace.export.SpanData.Attributes;
+import io.opencensus.trace.export.SpanData.Links;
+import io.opencensus.trace.export.SpanData.TimedEvent;
+import io.opencensus.trace.export.SpanData.TimedEvents;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link InstanaExporterHandler}. */
+@RunWith(JUnit4.class)
+public class InstanaExporterHandlerTest {
+
+ @Test
+ public void generateSpan() {
+ String traceId = "d239036e7d5cec116b562147388b35bf";
+ String spanId = "9cc1e3049173be09";
+ String parentId = "8b03ab423da481c5";
+ Map<String, AttributeValue> attributes =
+ Collections.singletonMap(
+ "http.url", AttributeValue.stringAttributeValue("http://localhost/foo"));
+ List<TimedEvent<Annotation>> annotations = Collections.emptyList();
+ List<TimedEvent<NetworkEvent>> networkEvents =
+ ImmutableList.of(
+ TimedEvent.create(
+ Timestamp.create(1505855799, 433901068),
+ NetworkEvent.builder(Type.RECV, 0).setCompressedMessageSize(7).build()),
+ TimedEvent.create(
+ Timestamp.create(1505855799, 459486280),
+ NetworkEvent.builder(Type.SENT, 0).setCompressedMessageSize(13).build()));
+ SpanData data =
+ SpanData.create(
+ SpanContext.create(
+ TraceId.fromLowerBase16(traceId),
+ SpanId.fromLowerBase16(spanId),
+ TraceOptions.fromBytes(new byte[] {1} /* sampled */)),
+ SpanId.fromLowerBase16(parentId),
+ true, /* hasRemoteParent */
+ "SpanName", /* name */
+ Timestamp.create(1505855794, 194009601) /* startTimestamp */,
+ Attributes.create(attributes, 0 /* droppedAttributesCount */),
+ TimedEvents.create(annotations, 0 /* droppedEventsCount */),
+ TimedEvents.create(networkEvents, 0 /* droppedEventsCount */),
+ Links.create(Collections.<Link>emptyList(), 0 /* droppedLinksCount */),
+ null, /* childSpanCount */
+ Status.OK,
+ Timestamp.create(1505855799, 465726528) /* endTimestamp */);
+
+ assertThat(InstanaExporterHandler.convertToJson(Collections.singletonList(data)))
+ .isEqualTo(
+ "["
+ + "{"
+ + "\"spanId\":\"9cc1e3049173be09\","
+ + "\"traceId\":\"d239036e7d5cec11\","
+ + "\"parentId\":\"8b03ab423da481c5\","
+ + "\"timestamp\":1505855794194,"
+ + "\"duration\":5271,"
+ + "\"name\":\"SpanName\","
+ + "\"type\":\"ENTRY\","
+ + "\"data\":"
+ + "{\"http.url\":\"http://localhost/foo\"}"
+ + "}"
+ + "]");
+ }
+}
diff --git a/exporters/trace/instana/src/test/java/io/opencensus/exporter/trace/instana/InstanaTraceExporterTest.java b/exporters/trace/instana/src/test/java/io/opencensus/exporter/trace/instana/InstanaTraceExporterTest.java
new file mode 100644
index 00000000..a4d03df3
--- /dev/null
+++ b/exporters/trace/instana/src/test/java/io/opencensus/exporter/trace/instana/InstanaTraceExporterTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.exporter.trace.instana;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.verify;
+
+import io.opencensus.trace.export.SpanExporter;
+import io.opencensus.trace.export.SpanExporter.Handler;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link InstanaTraceExporter}. */
+@RunWith(JUnit4.class)
+public class InstanaTraceExporterTest {
+
+ @Mock private SpanExporter spanExporter;
+ @Mock private Handler handler;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void registerUnregisterInstanaExporter() {
+ InstanaTraceExporter.register(spanExporter, handler);
+ verify(spanExporter)
+ .registerHandler(
+ eq("io.opencensus.exporter.trace.instana.InstanaTraceExporter"), same(handler));
+ InstanaTraceExporter.unregister(spanExporter);
+ verify(spanExporter)
+ .unregisterHandler(eq("io.opencensus.exporter.trace.instana.InstanaTraceExporter"));
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index 5456e658..6921de56 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -5,6 +5,7 @@ include ":opencensus-impl-core"
include ":opencensus-impl-lite"
include ":opencensus-impl"
include ":opencensus-testing"
+include ":opencensus-exporter-trace-instana"
include ":opencensus-exporter-trace-logging"
include ":opencensus-exporter-trace-stackdriver"
include ":opencensus-exporter-trace-zipkin"
@@ -24,6 +25,8 @@ project(':opencensus-contrib-agent').projectDir = "$rootDir/contrib/agent" as Fi
project(':opencensus-contrib-grpc-metrics').projectDir = "$rootDir/contrib/grpc_metrics" as File
project(':opencensus-contrib-grpc-util').projectDir = "$rootDir/contrib/grpc_util" as File
project(':opencensus-contrib-http-util').projectDir = "$rootDir/contrib/http_util" as File
+project(':opencensus-exporter-trace-instana').projectDir =
+ "$rootDir/exporters/trace/instana" as File
project(':opencensus-exporter-trace-logging').projectDir =
"$rootDir/exporters/trace/logging" as File
project(':opencensus-exporter-trace-stackdriver').projectDir =