diff --git a/CHANGELOG.md b/CHANGELOG.md index d05c5c9397..30a96eae0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,8 @@ break behaviors for applications that rely on this to be always enabled. - Provide a `Deadline` option to Stackdriver Stats exporter. Default value is 10 seconds. Also provide a `MetricServiceStub` option so that advanced users can use a custom Stackdriver Monitoring client to make RPCs. -- Use `JaegerExporterConfiguration` for creating `JaegerTraceExporter`. Provide a `Deadline` option -with default value 10 seconds. -- Use `ZipkinExporterConfiguration` for creating `ZipkinTraceExporter`. Provide a `Deadline` option -with default value 10 seconds. +- Use `Configuration` builder pattern for creating `JaegerTraceExporter`, `ZipkinTraceExporter` and +`InstanaTraceExporter`. Provide a `Deadline` option with default value 10 seconds. - Provide a `Deadline` option to Datadog and Elasticsearch exporter. Default value is 10 seconds. - Extract the common timeout logic of Trace exporters to `opencensus-exporter-trace-util`. diff --git a/exporters/trace/instana/build.gradle b/exporters/trace/instana/build.gradle index 74d9ee07ad..8d5c08b2c0 100644 --- a/exporters/trace/instana/build.gradle +++ b/exporters/trace/instana/build.gradle @@ -6,6 +6,8 @@ description = 'OpenCensus Trace Instana Exporter' } dependencies { + compileOnly libraries.auto_value + compile project(':opencensus-api'), project(':opencensus-exporter-trace-util'), libraries.guava diff --git a/exporters/trace/instana/src/main/java/io/opencensus/exporter/trace/instana/InstanaExporterConfiguration.java b/exporters/trace/instana/src/main/java/io/opencensus/exporter/trace/instana/InstanaExporterConfiguration.java new file mode 100644 index 0000000000..8336828d6d --- /dev/null +++ b/exporters/trace/instana/src/main/java/io/opencensus/exporter/trace/instana/InstanaExporterConfiguration.java @@ -0,0 +1,111 @@ +/* + * Copyright 2019, 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 com.google.auto.value.AutoValue; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import io.opencensus.common.Duration; +import javax.annotation.concurrent.Immutable; + +/** + * Configuration for {@link InstanaTraceExporter}. + * + * @since 0.22 + */ +@AutoValue +@Immutable +public abstract class InstanaExporterConfiguration { + + @VisibleForTesting static final Duration DEFAULT_DEADLINE = Duration.create(10, 0); + @VisibleForTesting static final Duration ZERO = Duration.fromMillis(0); + + InstanaExporterConfiguration() {} + + /** + * Returns the endpoint of the Instana agent. + * + * @return the endpoint of the Instana agent. + * @since 0.22 + */ + public abstract String getAgentEndpoint(); + + /** + * Returns the deadline for exporting to Instana. + * + *

Default value is 10 seconds. + * + * @return the export deadline. + * @since 0.22 + */ + public abstract Duration getDeadline(); + + /** + * Return a new {@link Builder}. + * + * @return a {@code Builder} + * @since 0.22 + */ + public static Builder builder() { + return new AutoValue_InstanaExporterConfiguration.Builder().setDeadline(DEFAULT_DEADLINE); + } + + /** + * Builder for {@link InstanaExporterConfiguration}. + * + * @since 0.22 + */ + @AutoValue.Builder + public abstract static class Builder { + + Builder() {} + + /** + * Sets the endpoint of Instana agent to send traces to. E.g + * http://localhost:42699/com.instana.plugin.generic.trace + * + * @param agentEndpoint the endpoint of the agent. + * @return this. + * @since 0.22 + */ + public abstract Builder setAgentEndpoint(String agentEndpoint); + + /** + * Sets the deadline for exporting to Instana. + * + * @param deadline the export deadline. + * @return this + * @since 0.22 + */ + public abstract Builder setDeadline(Duration deadline); + + abstract Duration getDeadline(); + + abstract InstanaExporterConfiguration autoBuild(); + + /** + * Builds a {@link InstanaExporterConfiguration}. + * + * @return a {@code InstanaExporterConfiguration}. + * @since 0.22 + */ + public InstanaExporterConfiguration build() { + Preconditions.checkArgument(getDeadline().compareTo(ZERO) > 0, "Deadline must be positive."); + return autoBuild(); + } + } +} 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 index 649a026fa2..3c54efb7d5 100644 --- 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 @@ -23,20 +23,16 @@ 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.exporter.trace.util.TimeLimitedHandler; import io.opencensus.trace.AttributeValue; -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.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.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -63,13 +59,13 @@ * 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 { +final class InstanaExporterHandler extends TimeLimitedHandler { - private static final Tracer tracer = Tracing.getTracer(); - private static final Sampler probabilitySpampler = Samplers.probabilitySampler(0.0001); + private static final String EXPORT_SPAN_NAME = "ExportInstanaTraces"; private final URL agentEndpoint; - InstanaExporterHandler(URL agentEndpoint) { + InstanaExporterHandler(URL agentEndpoint, Duration deadline) { + super(deadline, EXPORT_SPAN_NAME); this.agentEndpoint = agentEndpoint; } @@ -180,56 +176,36 @@ static String convertToJson(Collection spanDataList) { } @Override - public void export(Collection 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(); + public void timeLimitedExport(Collection spanDataList) throws Exception { + String json = convertToJson(spanDataList); + + OutputStream outputStream = null; + InputStream inputStream = null; try { - String json = convertToJson(spanDataList); + 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) { + throw new Exception("Response " + connection.getResponseCode()); + } + } finally { + closeStream(inputStream); + closeStream(outputStream); + } + } - OutputStream outputStream = null; - InputStream inputStream = null; + // Closes an input or output stream and ignores potential IOException. + private static void closeStream(@javax.annotation.Nullable Closeable stream) { + if (stream != 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())); - } + stream.close(); } 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 - } - } + // 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 index da2ce35402..dcc46d3031 100644 --- 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 @@ -34,7 +34,9 @@ * *

{@code
  * public static void main(String[] args) {
- *   InstanaTraceExporter.createAndRegister("http://localhost:42699/com.instana.plugin.generic.trace");
+ *   String agentEndpoint = "http://localhost:42699/com.instana.plugin.generic.trace";
+ *   InstanaTraceExporter.createAndRegister(
+ *     InstanaExporterConfiguration.builder().setAgentEndpoint(agentEndpoint).build());
  *   ... // Do work.
  * }
  * }
@@ -56,20 +58,39 @@ 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 + * @param configuration Configuration for InstanaTraceExporter. * @throws MalformedURLException if the agentEndpoint is not a valid http url. * @throws IllegalStateException if a Instana exporter is already registered. - * @since 0.12 + * @since 0.22 */ - public static void createAndRegister(String agentEndpoint) throws MalformedURLException { + public static void createAndRegister(InstanaExporterConfiguration configuration) + throws MalformedURLException { synchronized (monitor) { checkState(handler == null, "Instana exporter is already registered."); - Handler newHandler = new InstanaExporterHandler(new URL(agentEndpoint)); + Handler newHandler = + new InstanaExporterHandler( + new URL(configuration.getAgentEndpoint()), configuration.getDeadline()); handler = newHandler; register(Tracing.getExportComponent().getSpanExporter(), newHandler); } } + /** + * 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. + * @since 0.12 + * @deprecated in favor of {@link #createAndRegister(InstanaExporterConfiguration)}. + */ + @Deprecated + public static void createAndRegister(String agentEndpoint) throws MalformedURLException { + createAndRegister( + InstanaExporterConfiguration.builder().setAgentEndpoint(agentEndpoint).build()); + } + /** * Registers the {@code InstanaTraceExporter}. *