diff --git a/docs/spider-web.jpg b/docs/spider-web.jpg
new file mode 100644
index 000000000..3d9e5fe3d
Binary files /dev/null and b/docs/spider-web.jpg differ
diff --git a/pom.xml b/pom.xml
index a6ca972c4..dc1a19537 100644
--- a/pom.xml
+++ b/pom.xml
@@ -59,6 +59,7 @@
riptide-httpclient
riptide-idempotency
riptide-metrics
+ riptide-opentracing
riptide-problem
riptide-soap
riptide-spring-boot-autoconfigure
@@ -133,6 +134,11 @@
riptide-metrics
${project.version}
+
+ org.zalando
+ riptide-opentracing
+ ${project.version}
+
org.zalando
riptide-problem
diff --git a/riptide-bom/pom.xml b/riptide-bom/pom.xml
index 32a2ab2f1..36d924ea8 100644
--- a/riptide-bom/pom.xml
+++ b/riptide-bom/pom.xml
@@ -68,6 +68,10 @@
riptide-metrics
3.0.0-SNAPSHOT
+
+ org.zalando
+ riptide-opentracing
+
org.zalando
riptide-problem
diff --git a/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/CompositeRetryListener.java b/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/CompositeRetryListener.java
index f7e24d250..39b25ad51 100644
--- a/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/CompositeRetryListener.java
+++ b/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/CompositeRetryListener.java
@@ -10,6 +10,7 @@
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
+// TODO package private?
@API(status = EXPERIMENTAL)
public final class CompositeRetryListener implements RetryListener {
diff --git a/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/FailsafePlugin.java b/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/FailsafePlugin.java
index fae12ffb7..e4db73296 100644
--- a/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/FailsafePlugin.java
+++ b/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/FailsafePlugin.java
@@ -10,6 +10,7 @@
import net.jodah.failsafe.function.CheckedConsumer;
import org.apiguardian.api.API;
import org.springframework.http.client.ClientHttpResponse;
+import org.zalando.riptide.Attribute;
import org.zalando.riptide.Plugin;
import org.zalando.riptide.RequestArguments;
import org.zalando.riptide.RequestExecution;
@@ -27,6 +28,8 @@
@AllArgsConstructor(access = PRIVATE)
public final class FailsafePlugin implements Plugin {
+ public static final Attribute ATTEMPTS = Attribute.generate();
+
private final ImmutableList extends Policy> policies;
private final ScheduledExecutorService scheduler;
private final Predicate predicate;
@@ -56,18 +59,17 @@ public RequestExecution aroundDispatch(final RequestExecution execution) {
return Failsafe.with(select(arguments))
.with(scheduler)
- .getStageAsync(() -> execution.execute(arguments));
+ .getStageAsync(context -> execution
+ .execute(withAttempts(arguments, context.getAttemptCount())));
};
}
-
private Policy[] select(final RequestArguments arguments) {
final Stream> stream = policies.stream()
.filter(skipRetriesIfNeeded(arguments))
.map(withRetryListener(arguments));
- @SuppressWarnings("unchecked")
- final Policy[] policies = stream.toArray(Policy[]::new);
+ @SuppressWarnings("unchecked") final Policy[] policies = stream.toArray(Policy[]::new);
return policies;
}
@@ -84,13 +86,21 @@ private UnaryOperator> withRetryListener(final Reques
if (policy instanceof RetryPolicy) {
final RetryPolicy retryPolicy = (RetryPolicy) policy;
return retryPolicy.copy()
- .onRetry(new RetryListenerAdapter(listener, arguments));
+ .onFailedAttempt(new RetryListenerAdapter(listener, arguments));
} else {
return policy;
}
};
}
+ private RequestArguments withAttempts(final RequestArguments arguments, final int attempts) {
+ if (attempts == 0) {
+ return arguments;
+ }
+
+ return arguments.withAttribute(ATTEMPTS, attempts);
+ }
+
@VisibleForTesting
@AllArgsConstructor
static final class RetryListenerAdapter implements CheckedConsumer> {
diff --git a/riptide-faults/src/test/java/org/zalando/riptide/faults/TransientFaultExceptionTest.java b/riptide-faults/src/test/java/org/zalando/riptide/faults/TransientFaultExceptionTest.java
index 27a3006a0..2f8335d45 100644
--- a/riptide-faults/src/test/java/org/zalando/riptide/faults/TransientFaultExceptionTest.java
+++ b/riptide-faults/src/test/java/org/zalando/riptide/faults/TransientFaultExceptionTest.java
@@ -5,10 +5,10 @@
import java.io.IOException;
import java.util.concurrent.TimeoutException;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItemInArray;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
final class TransientFaultExceptionTest {
diff --git a/riptide-opentracing/README.md b/riptide-opentracing/README.md
new file mode 100644
index 000000000..50dfe8134
--- /dev/null
+++ b/riptide-opentracing/README.md
@@ -0,0 +1,185 @@
+# Riptide: OpenTracing
+
+[![Spider web](../docs/spider-web.jpg)](https://pixabay.com/photos/cobweb-drip-water-mirroring-blue-3725540/)
+
+[![Build Status](https://img.shields.io/travis/zalando/riptide/master.svg)](https://travis-ci.org/zalando/riptide)
+[![Coverage Status](https://img.shields.io/coveralls/zalando/riptide/master.svg)](https://coveralls.io/r/zalando/riptide)
+[![Code Quality](https://img.shields.io/codacy/grade/1fbe3d16ca544c0c8589692632d114de/master.svg)](https://www.codacy.com/app/whiskeysierra/riptide)
+[![Javadoc](https://www.javadoc.io/badge/org.zalando/riptide-metrics.svg)](http://www.javadoc.io/doc/org.zalando/riptide-metrics)
+[![Release](https://img.shields.io/github/release/zalando/riptide.svg)](https://github.com/zalando/riptide/releases)
+[![Maven Central](https://img.shields.io/maven-central/v/org.zalando/riptide-metrics.svg)](https://maven-badges.herokuapp.com/maven-central/org.zalando/riptide-metrics)
+[![OpenTracing](https://img.shields.io/badge/OpenTracing-enabled-blue.svg)](http://opentracing.io)
+[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/zalando/riptide/master/LICENSE)
+
+*Riptide: OpenTracing* adds sophisticated [OpenTracing](https://opentracing.io/) support to *Riptide*.
+
+## Example
+
+```java
+Http.builder()
+ .plugin(new OpenTracingPlugin(tracer))
+ .build();
+```
+
+## Features
+
+- Client span lifecycle management
+- Span context injection into HTTP headers of requests
+- Extensible span decorators for tags and logs
+- Seamless integration with [Riptide: Failsafe](../riptide-failsafe)
+
+## Dependencies
+
+- Java 8
+- Riptide Core
+- [OpenTracing Java API](https://opentracing.io/guides/java/)
+- [Riptide: Failsafe](../riptide-failsafe) (optional)
+
+## Installation
+
+Add the following dependency to your project:
+
+```xml
+
+ org.zalando
+ riptide-opentracing
+ ${riptide.version}
+
+```
+
+## Configuration
+
+```java
+Http.builder()
+ .baseUrl("https://www.example.com")
+ .plugin(new OpenTracingPlugin(tracer))
+ .build();
+```
+
+The following tags/logs are supported out of the box:
+
+| Tag/Log Field | Decorator | Example |
+|----------------------|--------------------------------|-----------------------------------|
+| `component` | `ComponentSpanDecorator` | `Riptide` |
+| `span.kind` | `SpanKindSpanDecorator` | `client` |
+| `peer.hostname` | `PeerSpanDecorator` | `www.github.com` |
+| `peer.port` | `PeerSpanDecorator` | `80` |
+| `http.method` | `HttpMethodSpanDecorator` | `GET` |
+| `http.url` | `HttpUrlSpanDecorator` | `https://www.github.com/users/me` |
+| `http.path` | `HttpPathSpanDecorator` | `/users/{user_id}` |
+| `http.status_code` | `HttpStatusCodeSpanDecorator` | `200` |
+| `error` | `ErrorSpanDecorator` | `false` |
+| `error.kind` (log) | `ErrorSpanDecorator` | `SocketTimeoutException` |
+| `error.object` (log) | `ErrorSpanDecorator` | (exception instance) |
+| `retry` | `RetrySpanDecorator` | `true` |
+| `retry_number` (log) | `RetrySpanDecorator` | `3` |
+| `*` | `CallSiteSpanDecorator` | `admin=true` |
+| `*` | `StaticTagSpanDecorator` | `aws.region=eu-central-1` |
+| `*` | `UriVariablesTagSpanDecorator` | user_id=me |
+
+### Notice
+
+**Be aware**: The `http.url` tag is disabled by default because the full request URI may contain
+sensitive, [*personal data*](https://en.wikipedia.org/wiki/General_Data_Protection_Regulation).
+As an alternative we introduced the `http.path` tag which favors the URI template over the
+already expanded version. That has the additional benefit of a significant lower cardinality
+compared to what `http.url` would provide.
+
+If you still want to enable it, you can do so by just registering the missing span decorator:
+
+```java
+new OpenTracingPlugin(tracer)
+ .withAdditionalSpanDecorators(new HttpUrlSpanDecorator())
+```
+
+### Span Decorators
+
+Span decorators are a simple, yet powerful tool to manipulate the span, i.e. they allow you to
+add tags, logs and baggage to spans. The default set of decorators can be extended by using
+`OpenTracingPlugin#withAdditionalSpanDecorators(..)`:
+
+```java
+new OpenTracingPlugin(tracer)
+ .withAdditionalSpanDecorators(new StaticSpanDecorator(singletonMap(
+ "environment", "local"
+ )))
+```
+
+If the default span decorators are not desired you can replace them completely using
+`OpenTracingPlugin#withSpanDecorators(..)`:
+
+```java
+new OpenTracingPlugin(tracer)
+ .withSpanDecorators(
+ new ComponentSpanDecorator("MSIE"),
+ new SpanKindSpanDecorator(Tags.SPAN_KIND_CONSUMER),
+ new PeerSpanDecorator(),
+ new HttpMethodSpanDecorator(),
+ new HttpPathSpanDecorator(),
+ new HttpUrlSpanDecorator(),
+ new HttpStatusCodeSpanDecorator(),
+ new ErrorSpanDecorator(),
+ new CallSiteSpanDecorator())
+```
+
+## Usage
+
+Typically you won't need to do anything at the call-site regarding OpenTracing, i.e.
+your usages of Riptide should work exactly as before:
+
+```java
+http.get("/users/{id}", userId)
+ .dispatch(series(),
+ on(SUCCESSFUL).call(User.class, this::greet),
+ anySeries().call(problemHandling()))
+```
+
+### Operation Name
+
+By default the HTTP method will be used as the operation name, which might not fit your needs.
+Since deriving a meaningful operation name from request arguments alone is unreliable, you can
+specify the `OpenTracingPlugin.OPERATION_NAME` request attribute to override the default:
+
+```java
+http.get("/users/{id}", userId)
+ .attribute(OpenTracingPlugin.OPERATION_NAME, "get_user")
+ .dispatch(series(),
+ on(SUCCESSFUL).call(User.class, this::greet),
+ anySeries().call(problemHandling()))
+```
+
+### Call-Site Tags
+
+Assuming you have the [`CallSiteSpanDecorator`](#span-decorators) registered (it is by default), you can also
+specify custom tags based on context information which wouldn't be available within the plugin
+anymore:
+
+```java
+http.get("/users/{id}", userId)
+ .attribute(OpenTracingPlugin.TAGS, singletonMap("retry", "true"))
+ .dispatch(series(),
+ on(SUCCESSFUL).call(User.class, this::greet),
+ anySeries().call(problemHandling()))
+```
+
+### URI Variables as Tags
+
+URI templates are not just safer to use (see [Configuration](#notice)), they can also be used to
+generate tags from URI variables. Given you have the `UriVariablesTagSpanDecorator` registered
+then the following will produce a `user_id=123` tag:
+
+```java
+http.get("/users/{user_id}", 123)
+```
+
+The same warning applies as mentioned before regarding [`http.url`](#notice). This feature may
+expose *personal data* and should be used with care.
+
+## Getting Help
+
+If you have questions, concerns, bug reports, etc., please file an issue in this repository's [Issue Tracker](../../../../issues).
+
+## Getting Involved/Contributing
+
+To contribute, simply open a pull request and add a brief description (1-2 sentences) of your addition or change. For
+more details, check the [contribution guidelines](../.github/CONTRIBUTING.md).
diff --git a/riptide-opentracing/pom.xml b/riptide-opentracing/pom.xml
new file mode 100644
index 000000000..e5764b56e
--- /dev/null
+++ b/riptide-opentracing/pom.xml
@@ -0,0 +1,68 @@
+
+
+ 4.0.0
+
+
+ org.zalando
+ riptide-parent
+ 3.0.0-SNAPSHOT
+
+
+ riptide-opentracing
+
+ Riptide: OpenTracing
+ Client side response routing
+
+
+ 0.32.0
+
+
+
+
+ org.zalando
+ riptide-core
+
+
+ io.opentracing
+ opentracing-api
+ ${opentracing.version}
+
+
+ org.zalando
+ riptide-failsafe
+
+ true
+
+
+ io.opentracing
+ opentracing-mock
+ ${opentracing.version}
+ test
+
+
+ com.github.rest-driver
+ rest-client-driver
+
+
+ io.opentracing.contrib
+ opentracing-concurrent
+ 0.2.0
+ test
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.7
+ test
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+
+
diff --git a/riptide-opentracing/src/main/java/org/zalando/riptide/opentracing/ExtensionFields.java b/riptide-opentracing/src/main/java/org/zalando/riptide/opentracing/ExtensionFields.java
new file mode 100644
index 000000000..6249fc534
--- /dev/null
+++ b/riptide-opentracing/src/main/java/org/zalando/riptide/opentracing/ExtensionFields.java
@@ -0,0 +1,14 @@
+package org.zalando.riptide.opentracing;
+
+public final class ExtensionFields {
+
+ /**
+ * In combination with {@link ExtensionTags#RETRY retry tag}, this field holds the number of the retry attempt.
+ */
+ public static final String RETRY_NUMBER = "retry_number";
+
+ private ExtensionFields() {
+
+ }
+
+}
diff --git a/riptide-opentracing/src/main/java/org/zalando/riptide/opentracing/ExtensionTags.java b/riptide-opentracing/src/main/java/org/zalando/riptide/opentracing/ExtensionTags.java
new file mode 100644
index 000000000..5bbeeaa9d
--- /dev/null
+++ b/riptide-opentracing/src/main/java/org/zalando/riptide/opentracing/ExtensionTags.java
@@ -0,0 +1,61 @@
+package org.zalando.riptide.opentracing;
+
+import io.opentracing.tag.BooleanTag;
+import io.opentracing.tag.StringTag;
+import io.opentracing.tag.Tag;
+
+public final class ExtensionTags {
+
+ public static final Tag HTTP_PATH = new StringTag("http.path");
+
+ /**
+ * When present on a client span, they represent a span that wraps a retried RPC. If missing no interpretation can
+ * be made. An explicit value of false would explicitly mean it is a first RPC attempt.
+ */
+ public static final Tag RETRY = new BooleanTag("retry");
+
+ /**
+ * The tag should contain an alias or name that allows users to identify the logical location (infrastructure account)
+ * where the operation took place. This can be the AWS account, or any other cloud provider account.
+ * E.g., {@code account=aws:zalando-zmon}, {@code account=gcp:zalando-foobar}
+ */
+ public static final Tag ACCOUNT = new StringTag("account");
+
+ /**
+ * The tag should contain some information that allows users to associate the physical location of the system where
+ * the operation took place (i.e. the datacenter).
+ * E.g., {@code zone=aws:eu-central-1a}, {@code zone=gcp:europe-west3-b}, {@code zone=dc:gth}.
+ */
+ public static final Tag ZONE = new StringTag("zone");
+
+ /**
+ * Oauth2 client ids have a certain cardinality but are well known or possible to get using different means. It
+ * could be helpful for server spans to identify the client making the call. E.g., {@code client_id=cognac}
+ */
+ public static final Tag CLIENT_ID = new StringTag("client_id");
+
+ /**
+ * The flow_id tag should contain the request flow ID, typically found in the ingress requests HTTP header X-Flow-ID.
+ *
+ * X-Flow-ID Guidelines
+ */
+ public static final Tag FLOW_ID = new StringTag("flow_id");
+
+ /**
+ * The tag should contain the artifact version of the running application generating the spans.
+ * This is, usually, the docker image tag.
+ */
+ public static final Tag ARTIFACT_VERSION = new StringTag("artifact_version");
+
+ /**
+ * The tag should contain the unique identifier of the deployment that resulted in the operation of the running
+ * application generating the spans. This is, usually, the STUPS stack version or the Kubernetes deployment id.
+ * A deployment is the combination of a given artifact_version and the environment, usually its configuration.
+ */
+ public static final Tag DEPLOYMENT_ID = new StringTag("deployment_id");
+
+ private ExtensionTags() {
+
+ }
+
+}
diff --git a/riptide-opentracing/src/main/java/org/zalando/riptide/opentracing/OpenTracingPlugin.java b/riptide-opentracing/src/main/java/org/zalando/riptide/opentracing/OpenTracingPlugin.java
new file mode 100644
index 000000000..ed6beaf05
--- /dev/null
+++ b/riptide-opentracing/src/main/java/org/zalando/riptide/opentracing/OpenTracingPlugin.java
@@ -0,0 +1,175 @@
+package org.zalando.riptide.opentracing;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Multimaps;
+import io.opentracing.Scope;
+import io.opentracing.Span;
+import io.opentracing.SpanContext;
+import io.opentracing.Tracer;
+import io.opentracing.Tracer.SpanBuilder;
+import io.opentracing.propagation.TextMapAdapter;
+import lombok.AllArgsConstructor;
+import org.springframework.http.client.ClientHttpResponse;
+import org.zalando.fauxpas.ThrowingBiConsumer;
+import org.zalando.riptide.Attribute;
+import org.zalando.riptide.AttributeStage;
+import org.zalando.riptide.Plugin;
+import org.zalando.riptide.RequestArguments;
+import org.zalando.riptide.RequestExecution;
+import org.zalando.riptide.opentracing.span.CallSiteSpanDecorator;
+import org.zalando.riptide.opentracing.span.ComponentSpanDecorator;
+import org.zalando.riptide.opentracing.span.ErrorSpanDecorator;
+import org.zalando.riptide.opentracing.span.HttpMethodSpanDecorator;
+import org.zalando.riptide.opentracing.span.HttpPathSpanDecorator;
+import org.zalando.riptide.opentracing.span.HttpStatusCodeSpanDecorator;
+import org.zalando.riptide.opentracing.span.PeerSpanDecorator;
+import org.zalando.riptide.opentracing.span.SpanDecorator;
+import org.zalando.riptide.opentracing.span.SpanKindSpanDecorator;
+
+import javax.annotation.CheckReturnValue;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CompletionException;
+import java.util.function.BiConsumer;
+
+import static io.opentracing.propagation.Format.Builtin.HTTP_HEADERS;
+import static java.util.Objects.nonNull;
+import static lombok.AccessLevel.PRIVATE;
+
+@AllArgsConstructor(access = PRIVATE)
+public final class OpenTracingPlugin implements Plugin {
+
+ /**
+ * Allows to pass a customized {@link Tracer#buildSpan(String) operation name} directly from
+ * a call site. Defaults to the {@link RequestArguments#getMethod() HTTP method}.
+ *
+ * @see AttributeStage#attribute(Attribute, Object)
+ */
+ public static final Attribute OPERATION_NAME = Attribute.generate();
+
+ /**
+ * Allows to pass arbitrary span tags directly from a call site.
+ *
+ * @see AttributeStage#attribute(Attribute, Object)
+ */
+ public static final Attribute
+
+ org.zalando
+ riptide-opentracing
+
+ true
+
+
+ io.opentracing.contrib
+ opentracing-concurrent
+ 0.2.0
+
+ true
+
org.zalando
riptide-soap
@@ -119,7 +132,7 @@
org.zalando
- tracer-spring-boot-starter
+ tracer-spring-boot-autoconfigure
${tracer.version}
true
@@ -225,6 +238,12 @@
2.3.1
test
+
+ io.opentracing
+ opentracing-mock
+ 0.32.0
+ test
+
diff --git a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/DefaultRiptideRegistrar.java b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/DefaultRiptideRegistrar.java
index 1bc28a79c..de0996a80 100644
--- a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/DefaultRiptideRegistrar.java
+++ b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/DefaultRiptideRegistrar.java
@@ -5,6 +5,8 @@
import com.google.common.collect.ImmutableMap;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics;
+import io.opentracing.contrib.concurrent.TracedExecutorService;
+import io.opentracing.contrib.concurrent.TracedScheduledExecutorService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.jodah.failsafe.CircuitBreaker;
@@ -46,11 +48,12 @@
import org.zalando.riptide.httpclient.metrics.HttpConnectionPoolMetrics;
import org.zalando.riptide.idempotency.IdempotencyPredicate;
import org.zalando.riptide.metrics.MetricsPlugin;
+import org.zalando.riptide.opentracing.OpenTracingPlugin;
+import org.zalando.riptide.opentracing.span.SpanDecorator;
import org.zalando.riptide.soap.SOAPFaultHttpMessageConverter;
import org.zalando.riptide.soap.SOAPHttpMessageConverter;
import org.zalando.riptide.stream.Streams;
import org.zalando.riptide.timeout.TimeoutPlugin;
-import org.zalando.tracer.concurrent.TracingExecutors;
import javax.xml.soap.SOAPConstants;
import java.net.SocketTimeoutException;
@@ -100,7 +103,7 @@ private void registerHttp(final String id, final Client client) {
return genericBeanDefinition(HttpFactory.class)
.setFactoryMethod("create")
- .addConstructorArgValue(registerExecutor(id, client))
+ .addConstructorArgReference(registerExecutor(id, client))
.addConstructorArgReference(registerClientHttpRequestFactory(id, client))
.addConstructorArgValue(client.getBaseUrl())
.addConstructorArgValue(client.getUrlResolution())
@@ -118,7 +121,7 @@ private String registerClientHttpRequestFactory(final String id, final Client cl
});
}
- private BeanMetadataElement registerExecutor(final String id, final Client client) {
+ private String registerExecutor(final String id, final Client client) {
final String name = "http-" + id;
final String executorId = registry.registerIfAbsent(id, ExecutorService.class, () -> {
@@ -144,7 +147,14 @@ private BeanMetadataElement registerExecutor(final String id, final Client clien
.addConstructorArgValue(ImmutableList.of(clientId(id))));
}
- return trace(executorId);
+ if (client.getTracing().getEnabled()) {
+ return registry.registerIfAbsent(id, TracedExecutorService.class, () ->
+ genericBeanDefinition(TracedExecutorService.class)
+ .addConstructorArgReference(executorId)
+ .addConstructorArgReference("tracer"));
+ }
+
+ return executorId;
}
private static final class HttpMessageConverters {
@@ -219,9 +229,10 @@ private List registerPlugins(final String id, final Client client
registerChaosPlugin(id, client),
registerMetricsPlugin(id, client),
registerTransientFaultPlugin(id, client),
+ registerOpenTracingPlugin(id, client),
registerFailsafePlugin(id, client),
- registerBackupPlugin(id, client),
registerAuthorizationPlugin(id, client),
+ registerBackupPlugin(id, client),
registerTimeoutPlugin(id, client),
registerOriginalStackTracePlugin(id, client),
registerCustomPlugin(id));
@@ -295,12 +306,13 @@ private Optional registerChaosPlugin(final String id, final Client clien
private Optional registerMetricsPlugin(final String id, final Client client) {
if (client.getMetrics().getEnabled()) {
- log.debug("Client [{}]: Registering [{}]", id, MetricsPlugin.class.getSimpleName());
- final String pluginId = registry.registerIfAbsent(id, MetricsPlugin.class, () ->
- genericBeanDefinition(MetricsPluginFactory.class)
- .setFactoryMethod("createMetricsPlugin")
- .addConstructorArgReference("meterRegistry")
- .addConstructorArgValue(ImmutableList.of(clientId(id))));
+ final String pluginId = registry.registerIfAbsent(id, MetricsPlugin.class, () -> {
+ log.debug("Client [{}]: Registering [{}]", id, MetricsPlugin.class.getSimpleName());
+ return genericBeanDefinition(MetricsPluginFactory.class)
+ .setFactoryMethod("createMetricsPlugin")
+ .addConstructorArgReference("meterRegistry")
+ .addConstructorArgValue(ImmutableList.of(clientId(id)));
+ });
return Optional.of(pluginId);
}
@@ -309,25 +321,55 @@ private Optional registerMetricsPlugin(final String id, final Client cli
private Optional registerTransientFaultPlugin(final String id, final Client client) {
if (client.getTransientFaultDetection().getEnabled()) {
- log.debug("Client [{}]: Registering [{}]", id, TransientFaultPlugin.class.getSimpleName());
- final String pluginId = registry.registerIfAbsent(id, TransientFaultPlugin.class, () ->
- genericBeanDefinition(TransientFaultPlugin.class)
- .addConstructorArgReference(findFaultClassifier(id)));
+ final String pluginId = registry.registerIfAbsent(id, TransientFaultPlugin.class, () -> {
+ log.debug("Client [{}]: Registering [{}]", id, TransientFaultPlugin.class.getSimpleName());
+ return genericBeanDefinition(TransientFaultPlugin.class)
+ .addConstructorArgReference(findFaultClassifier(id));
+ });
return Optional.of(pluginId);
}
return Optional.empty();
}
+ private Optional registerOpenTracingPlugin(final String id, final Client client) {
+ if (client.getTracing().getEnabled()) {
+ registry.registerIfAbsent(id, OpenTracingPlugin.class, () -> {
+ log.debug("Client [{}]: Registering [{}]", id, OpenTracingPlugin.class.getSimpleName());
+
+ final String decorator = generateBeanName(id, SpanDecorator.class);
+ return genericBeanDefinition(OpenTracingPluginFactory.class)
+ .setFactoryMethod("create")
+ .addConstructorArgReference("tracer")
+ .addConstructorArgValue(client)
+ .addConstructorArgValue(registry.isRegistered(decorator) ? ref(decorator) : null);
+ });
+ }
+ return Optional.empty();
+ }
+
private Optional registerFailsafePlugin(final String id, final Client client) {
if (client.getRetry().getEnabled() || client.getCircuitBreaker().getEnabled()) {
- log.debug("Client [{}]: Registering [{}]", id, FailsafePlugin.class.getSimpleName());
- final String pluginId = registry.registerIfAbsent(id, FailsafePlugin.class, () ->
- genericBeanDefinition(FailsafePluginFactory.class)
- .setFactoryMethod("createFailsafePlugin")
- .addConstructorArgValue(registerScheduler(id, client))
- .addConstructorArgValue(registerRetryPolicy(id, client))
- .addConstructorArgValue(registerCircuitBreaker(id, client))
- .addConstructorArgReference(registerRetryListener(id, client)));
+ final String pluginId = registry.registerIfAbsent(id, FailsafePlugin.class, () -> {
+ log.debug("Client [{}]: Registering [{}]", id, FailsafePlugin.class.getSimpleName());
+ return genericBeanDefinition(FailsafePluginFactory.class)
+ .setFactoryMethod("create")
+ .addConstructorArgReference(registerScheduler(id, client))
+ .addConstructorArgValue(registerRetryPolicy(id, client))
+ .addConstructorArgValue(registerCircuitBreaker(id, client))
+ .addConstructorArgReference(registerRetryListener(id, client));
+ });
+ return Optional.of(pluginId);
+ }
+ return Optional.empty();
+ }
+
+ private Optional registerAuthorizationPlugin(final String id, final Client client) {
+ if (client.getOauth().getEnabled()) {
+ final String pluginId = registry.registerIfAbsent(id, AuthorizationPlugin.class, () -> {
+ log.debug("Client [{}]: Registering [{}]", id, AuthorizationPlugin.class.getSimpleName());
+ return genericBeanDefinition(AuthorizationPlugin.class)
+ .addConstructorArgReference(registerAuthorizationProvider(id, client.getOauth()));
+ });
return Optional.of(pluginId);
}
return Optional.empty();
@@ -338,22 +380,11 @@ private Optional registerBackupPlugin(final String id, final Client clie
log.debug("Client [{}]: Registering [{}]", id, BackupRequestPlugin.class.getSimpleName());
final String pluginId = registry.registerIfAbsent(id, BackupRequestPlugin.class, () ->
genericBeanDefinition(BackupRequestPlugin.class)
- .addConstructorArgValue(registerScheduler(id, client))
+ .addConstructorArgReference(registerScheduler(id, client))
.addConstructorArgValue(client.getBackupRequest().getDelay().getAmount())
.addConstructorArgValue(client.getBackupRequest().getDelay().getUnit())
.addConstructorArgValue(new IdempotencyPredicate())
- .addConstructorArgValue(registerExecutor(id, client)));
- return Optional.of(pluginId);
- }
- return Optional.empty();
- }
-
- private Optional registerAuthorizationPlugin(final String id, final Client client) {
- if (client.getOauth().getEnabled()) {
- log.debug("Client [{}]: Registering [{}]", id, AuthorizationPlugin.class.getSimpleName());
- final String pluginId = registry.registerIfAbsent(id, AuthorizationPlugin.class, () ->
- genericBeanDefinition(AuthorizationPlugin.class)
- .addConstructorArgReference(registerAuthorizationProvider(id, client.getOauth())));
+ .addConstructorArgReference(registerExecutor(id, client)));
return Optional.of(pluginId);
}
return Optional.empty();
@@ -361,14 +392,15 @@ private Optional registerAuthorizationPlugin(final String id, final Clie
private Optional registerTimeoutPlugin(final String id, final Client client) {
if (client.getTimeouts().getEnabled()) {
- log.debug("Client [{}]: Registering [{}]", id, TimeoutPlugin.class.getSimpleName());
- final TimeSpan timeout = client.getTimeouts().getGlobal();
- final String pluginId = registry.registerIfAbsent(id, TimeoutPlugin.class, () ->
- genericBeanDefinition(TimeoutPlugin.class)
- .addConstructorArgValue(registerScheduler(id, client))
- .addConstructorArgValue(timeout.getAmount())
- .addConstructorArgValue(timeout.getUnit())
- .addConstructorArgValue(registerExecutor(id, client)));
+ final String pluginId = registry.registerIfAbsent(id, TimeoutPlugin.class, () -> {
+ log.debug("Client [{}]: Registering [{}]", id, TimeoutPlugin.class.getSimpleName());
+ final TimeSpan timeout = client.getTimeouts().getGlobal();
+ return genericBeanDefinition(TimeoutPlugin.class)
+ .addConstructorArgReference(registerScheduler(id, client))
+ .addConstructorArgValue(timeout.getAmount())
+ .addConstructorArgValue(timeout.getUnit())
+ .addConstructorArgReference(registerExecutor(id, client));
+ });
return Optional.of(pluginId);
}
return Optional.empty();
@@ -376,8 +408,10 @@ private Optional registerTimeoutPlugin(final String id, final Client cli
private Optional registerOriginalStackTracePlugin(final String id, final Client client) {
if (client.getStackTracePreservation().getEnabled()) {
- log.debug("Client [{}]: Registering [{}]", id, OriginalStackTracePlugin.class.getSimpleName());
- final String pluginId = registry.registerIfAbsent(id, OriginalStackTracePlugin.class);
+ final String pluginId = registry.registerIfAbsent(id, OriginalStackTracePlugin.class, () -> {
+ log.debug("Client [{}]: Registering [{}]", id, OriginalStackTracePlugin.class.getSimpleName());
+ return genericBeanDefinition(OriginalStackTracePlugin.class);
+ });
return Optional.of(pluginId);
}
return Optional.empty();
@@ -401,7 +435,7 @@ private String findFaultClassifier(final String id) {
}
}
- private BeanMetadataElement registerScheduler(final String id, final Client client) {
+ private String registerScheduler(final String id, final Client client) {
// we allow users to use their own ScheduledExecutorService, but they don't have to configure tracing
final String name = "http-" + id + "-scheduler";
@@ -426,7 +460,14 @@ private BeanMetadataElement registerScheduler(final String id, final Client clie
.addConstructorArgValue(ImmutableList.of(clientId(id))));
}
- return trace(executorId);
+ if (client.getTracing().getEnabled()) {
+ return registry.registerIfAbsent(id, TracedScheduledExecutorService.class, () ->
+ genericBeanDefinition(TracedScheduledExecutorService.class)
+ .addConstructorArgReference(executorId)
+ .addConstructorArgReference("tracer"));
+ }
+
+ return executorId;
}
private BeanMetadataElement registerRetryPolicy(final String id, final Client client) {
@@ -434,7 +475,7 @@ private BeanMetadataElement registerRetryPolicy(final String id, final Client cl
return ref(registry.registerIfAbsent(id, RetryPolicy.class, () ->
genericBeanDefinition(FailsafePluginFactory.class)
.setFactoryMethod("createRetryPolicy")
- .addConstructorArgValue(client.getRetry())));
+ .addConstructorArgValue(client)));
}
return null;
@@ -500,23 +541,6 @@ private String registerAuthorizationProvider(final String id, final OAuth oauth)
.addConstructorArgValue(id));
}
- private BeanMetadataElement trace(final String executor) {
- final Optional result = ifPresent("org.zalando.tracer.concurrent.TracingExecutors",
- () -> {
- if (registry.isRegistered("tracer")) {
- return genericBeanDefinition(TracingExecutors.class)
- .setFactoryMethod("preserve")
- .addConstructorArgReference(executor)
- .addConstructorArgReference("tracer")
- .getBeanDefinition();
- } else {
- return null;
- }
- });
-
- return result.orElseGet(() -> ref(executor));
- }
-
private String registerHttpClient(final String id, final Client client) {
return registry.registerIfAbsent(id, HttpClient.class, () -> {
log.debug("Client [{}]: Registering HttpClient", id);
@@ -552,9 +576,10 @@ private String registerHttpClient(final String id, final Client client) {
private List configureFirstRequestInterceptors(final String id, final Client client) {
final List interceptors = list();
- if (registry.isRegistered("tracerHttpRequestInterceptor")) {
- log.debug("Client [{}]: Registering TracerHttpRequestInterceptor", id);
- interceptors.add(ref("tracerHttpRequestInterceptor"));
+ // TODO theoretically tracing could still be disabled...
+ if (client.getTracing().getPropagateFlowId()) {
+ log.debug("Client [{}]: Registering FlowHttpRequestInterceptor", id);
+ interceptors.add(ref("flowHttpRequestInterceptor"));
}
return interceptors;
diff --git a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/Defaulting.java b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/Defaulting.java
index d35c0b3c5..f595287c5 100644
--- a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/Defaulting.java
+++ b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/Defaulting.java
@@ -18,10 +18,13 @@
import org.zalando.riptide.autoconfigure.RiptideProperties.Soap;
import org.zalando.riptide.autoconfigure.RiptideProperties.StackTracePreservation;
import org.zalando.riptide.autoconfigure.RiptideProperties.Timeouts;
+import org.zalando.riptide.autoconfigure.RiptideProperties.Tracing;
import org.zalando.riptide.autoconfigure.RiptideProperties.TransientFaultDetection;
import javax.annotation.Nullable;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
import java.util.function.BinaryOperator;
@@ -67,6 +70,7 @@ private static Defaults merge(final Defaults defaults) {
defaults.getRequestCompression(),
defaults.getCertificatePinning(),
defaults.getCaching(),
+ defaults.getTracing(),
defaults.getChaos(),
defaults.getSoap()
);
@@ -101,6 +105,7 @@ private static Client merge(final Client base, final Defaults defaults) {
merge(base.getRequestCompression(), defaults.getRequestCompression(), Defaulting::merge),
merge(base.getCertificatePinning(), defaults.getCertificatePinning(), Defaulting::merge),
merge(base.getCaching(), defaults.getCaching(), Defaulting::merge),
+ merge(base.getTracing(), defaults.getTracing(), Defaulting::merge),
merge(base.getChaos(), defaults.getChaos(), Defaulting::merge),
merge(base.getSoap(), defaults.getSoap(), Defaulting::merge)
);
@@ -240,6 +245,24 @@ private static Heuristic merge(final Heuristic base, final Heuristic defaults) {
);
}
+ private static Tracing merge(final Tracing base, final Tracing defaults) {
+ final boolean enabled = either(base.getEnabled(), defaults.getEnabled());
+ final boolean propagateFlowId = either(base.getPropagateFlowId(), defaults.getPropagateFlowId());
+
+ return new Tracing(
+ enabled,
+ merge(base.getTags(), defaults.getTags(), Defaulting::merge),
+ enabled && propagateFlowId
+ );
+ }
+
+ private static Map merge(final Map base, final Map defaults) {
+ final Map map = new HashMap<>();
+ map.putAll(defaults);
+ map.putAll(base);
+ return map;
+ }
+
private static Chaos merge(final Chaos base, final Chaos defaults) {
return new Chaos(
merge(base.getLatency(), defaults.getLatency(), Defaulting::merge),
diff --git a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/FailsafePluginFactory.java b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/FailsafePluginFactory.java
index a6cb2f34f..9ce352407 100644
--- a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/FailsafePluginFactory.java
+++ b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/FailsafePluginFactory.java
@@ -6,6 +6,8 @@
import net.jodah.failsafe.RetryPolicy;
import org.springframework.http.client.ClientHttpResponse;
import org.zalando.riptide.Plugin;
+import org.zalando.riptide.autoconfigure.RiptideProperties.Client;
+import org.zalando.riptide.autoconfigure.RiptideProperties.Retry;
import org.zalando.riptide.autoconfigure.RiptideProperties.Retry.Backoff;
import org.zalando.riptide.failsafe.CircuitBreakerListener;
import org.zalando.riptide.failsafe.CompositeDelayFunction;
@@ -33,7 +35,7 @@ private FailsafePluginFactory() {
}
- public static Plugin createFailsafePlugin(
+ public static Plugin create(
final ScheduledExecutorService scheduler,
@Nullable final RetryPolicy retryPolicy,
@Nullable final CircuitBreaker circuitBreaker,
@@ -53,9 +55,11 @@ public static Plugin createFailsafePlugin(
.withListener(listener);
}
- public static RetryPolicy createRetryPolicy(final RiptideProperties.Retry config) {
+ public static RetryPolicy createRetryPolicy(final Client client) {
final RetryPolicy policy = new RetryPolicy<>();
+ final Retry config = client.getRetry();
+
Optional.ofNullable(config.getFixedDelay())
.ifPresent(delay -> delay.applyTo(policy::withDelay));
@@ -87,7 +91,10 @@ public static RetryPolicy createRetryPolicy(final RiptidePro
Optional.ofNullable(config.getJitter())
.ifPresent(jitter -> jitter.applyTo(policy::withJitter));
- policy.handle(TransientFaultException.class);
+ if (client.getTransientFaultDetection().getEnabled()) {
+ policy.handle(TransientFaultException.class);
+ }
+
policy.handle(RetryException.class);
policy.withDelay(new CompositeDelayFunction<>(Arrays.asList(
@@ -98,7 +105,7 @@ public static RetryPolicy createRetryPolicy(final RiptidePro
return policy;
}
- public static CircuitBreaker createCircuitBreaker(final RiptideProperties.Client client,
+ public static CircuitBreaker createCircuitBreaker(final Client client,
final CircuitBreakerListener listener) {
final CircuitBreaker breaker = new CircuitBreaker<>();
diff --git a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/OpenTracingPluginFactory.java b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/OpenTracingPluginFactory.java
new file mode 100644
index 000000000..77d5605a1
--- /dev/null
+++ b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/OpenTracingPluginFactory.java
@@ -0,0 +1,43 @@
+package org.zalando.riptide.autoconfigure;
+
+import io.opentracing.Tracer;
+import org.zalando.riptide.Plugin;
+import org.zalando.riptide.autoconfigure.RiptideProperties.Client;
+import org.zalando.riptide.opentracing.OpenTracingPlugin;
+import org.zalando.riptide.opentracing.span.RetrySpanDecorator;
+import org.zalando.riptide.opentracing.span.SpanDecorator;
+import org.zalando.riptide.opentracing.span.StaticSpanDecorator;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.zalando.riptide.opentracing.span.SpanDecorator.composite;
+
+@SuppressWarnings("unused")
+final class OpenTracingPluginFactory {
+
+ private OpenTracingPluginFactory() {
+
+ }
+
+ public static Plugin create(final Tracer tracer, final Client client, @Nullable final SpanDecorator decorator) {
+ final List decorators = new ArrayList<>();
+ decorators.add(new StaticSpanDecorator(client.getTracing().getTags()));
+
+ if (client.getRetry().getEnabled()) {
+ decorators.add(new RetrySpanDecorator());
+ }
+
+ return create(tracer, decorator)
+ .withAdditionalSpanDecorators(composite(decorators));
+ }
+
+ private static OpenTracingPlugin create(final Tracer tracer,
+ @Nullable final SpanDecorator decorator) {
+ return decorator == null ?
+ new OpenTracingPlugin(tracer) :
+ new OpenTracingPlugin(tracer).withSpanDecorators(decorator);
+ }
+
+}
diff --git a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/Registry.java b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/Registry.java
index b2186c204..26aec109d 100644
--- a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/Registry.java
+++ b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/Registry.java
@@ -18,7 +18,6 @@
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.LOWER_HYPHEN;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
final class Registry {
@@ -51,10 +50,6 @@ public String registerIfAbsent(final Class type, final Supplier String registerIfAbsent(final String id, final Class type) {
- return registerIfAbsent(id, type, () -> genericBeanDefinition(type));
- }
-
public String registerIfAbsent(final String id, final Class type,
final Supplier factory) {
return registerIfAbsent(id, generateBeanName(id, type), factory);
diff --git a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/RiptideAutoConfiguration.java b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/RiptideAutoConfiguration.java
index 4009d5705..92ba59d1b 100644
--- a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/RiptideAutoConfiguration.java
+++ b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/RiptideAutoConfiguration.java
@@ -28,14 +28,12 @@
@AutoConfigureAfter(name = {
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration",
"org.zalando.logbook.spring.LogbookAutoConfiguration",
- "org.zalando.tracer.spring.TracerAutoConfiguration",
- "org.zalando.tracer.spring.TracerSchedulingAutoConfiguration", // only needed for tracer < 0.12.0,
- "io.micrometer.spring.autoconfigure.CompositeMeterRegistryAutoConfiguration", // Spring Boot 1.x
- "org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration", // Spring Boot 2.x
+ "org.zalando.tracer.autoconfigure.TracerAutoConfiguration",
+ "org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration",
})
@AutoConfigureBefore(name = {
"org.springframework.scheduling.annotation.SchedulingConfiguration",
- "org.zalando.failsafeactuator.config.FailsafeInjectionConfiguration"
+ "org.zalando.actuate.autoconfigure.failsafe.CircuitBreakersEndpointAutoConfiguration"
})
public class RiptideAutoConfiguration {
diff --git a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/RiptideProperties.java b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/RiptideProperties.java
index fada3fdab..90544d21b 100644
--- a/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/RiptideProperties.java
+++ b/riptide-spring-boot-autoconfigure/src/main/java/org/zalando/riptide/autoconfigure/RiptideProperties.java
@@ -24,6 +24,7 @@
import java.util.List;
import java.util.Map;
+import static java.util.Collections.emptyMap;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.apiguardian.api.API.Status.INTERNAL;
@@ -105,6 +106,9 @@ public static final class Defaults {
)
);
+ @NestedConfigurationProperty
+ private Tracing tracing = new Tracing(false, emptyMap(), false);
+
@NestedConfigurationProperty
private Chaos chaos = new Chaos(
new Latency(false, 0.01, TimeSpan.of(1, SECONDS)),
@@ -165,6 +169,9 @@ public static final class Client {
@NestedConfigurationProperty
private Caching caching;
+ @NestedConfigurationProperty
+ private Tracing tracing;
+
@NestedConfigurationProperty
private Chaos chaos;
@@ -337,6 +344,17 @@ public static final class Heuristic {
}
}
+ @Getter
+ @Setter
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static final class Tracing {
+ private Boolean enabled;
+ private Map tags;
+ private Boolean propagateFlowId;
+ }
+
+
@Getter
@Setter
@NoArgsConstructor
diff --git a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/CachingTest.java b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/CachingTest.java
index 3094678c6..6bc2a16c6 100644
--- a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/CachingTest.java
+++ b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/CachingTest.java
@@ -17,7 +17,7 @@
import org.springframework.test.context.ActiveProfiles;
import org.zalando.logbook.spring.LogbookAutoConfiguration;
import org.zalando.riptide.Http;
-import org.zalando.tracer.spring.TracerAutoConfiguration;
+import org.zalando.tracer.autoconfigure.TracerAutoConfiguration;
import static com.github.restdriver.clientdriver.RestClientDriver.giveResponse;
import static com.github.restdriver.clientdriver.RestClientDriver.onRequestTo;
diff --git a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/DefaultTestConfiguration.java b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/DefaultTestConfiguration.java
index 31d05bf00..36c9fc56f 100644
--- a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/DefaultTestConfiguration.java
+++ b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/DefaultTestConfiguration.java
@@ -5,7 +5,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ActiveProfiles;
import org.zalando.logbook.spring.LogbookAutoConfiguration;
-import org.zalando.tracer.spring.TracerAutoConfiguration;
+import org.zalando.tracer.autoconfigure.TracerAutoConfiguration;
@Configuration
@ImportAutoConfiguration({
@@ -13,6 +13,7 @@
JacksonAutoConfiguration.class,
LogbookAutoConfiguration.class,
TracerAutoConfiguration.class,
+ OpenTracingTestAutoConfiguration.class,
MetricsTestAutoConfiguration.class,
})
@ActiveProfiles("default")
diff --git a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/ManualConfiguration.java b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/ManualConfiguration.java
index 5bdabe553..fb1058773 100644
--- a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/ManualConfiguration.java
+++ b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/ManualConfiguration.java
@@ -4,8 +4,15 @@
import com.google.common.collect.ImmutableList;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
+import io.opentracing.Tracer;
+import io.opentracing.contrib.concurrent.TracedExecutorService;
+import io.opentracing.contrib.concurrent.TracedScheduledExecutorService;
import net.jodah.failsafe.CircuitBreaker;
import net.jodah.failsafe.RetryPolicy;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.ssl.SSLContexts;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
@@ -17,6 +24,9 @@
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
+import org.zalando.logbook.Logbook;
+import org.zalando.logbook.httpclient.LogbookHttpRequestInterceptor;
+import org.zalando.logbook.httpclient.LogbookHttpResponseInterceptor;
import org.zalando.logbook.spring.LogbookAutoConfiguration;
import org.zalando.riptide.Http;
import org.zalando.riptide.OriginalStackTracePlugin;
@@ -39,15 +49,18 @@
import org.zalando.riptide.failsafe.metrics.MetricsRetryListener;
import org.zalando.riptide.faults.TransientFaultException;
import org.zalando.riptide.faults.TransientFaultPlugin;
+import org.zalando.riptide.httpclient.ApacheClientHttpRequestFactory;
+import org.zalando.riptide.httpclient.GzipHttpRequestInterceptor;
import org.zalando.riptide.idempotency.IdempotencyPredicate;
import org.zalando.riptide.metrics.MetricsPlugin;
+import org.zalando.riptide.opentracing.OpenTracingPlugin;
import org.zalando.riptide.soap.SOAPFaultHttpMessageConverter;
import org.zalando.riptide.soap.SOAPHttpMessageConverter;
import org.zalando.riptide.stream.Streams;
import org.zalando.riptide.timeout.TimeoutPlugin;
-import org.zalando.tracer.Tracer;
-import org.zalando.tracer.concurrent.TracingExecutors;
-import org.zalando.tracer.spring.TracerAutoConfiguration;
+import org.zalando.tracer.Flow;
+import org.zalando.tracer.autoconfigure.TracerAutoConfiguration;
+import org.zalando.tracer.httpclient.FlowHttpRequestInterceptor;
import java.net.SocketTimeoutException;
import java.time.Clock;
@@ -67,6 +80,7 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
+import static javax.net.ssl.HttpsURLConnection.getDefaultHostnameVerifier;
import static org.zalando.riptide.chaos.FailureInjection.composite;
@Configuration
@@ -97,7 +111,7 @@ public Http exampleHttp(final Executor executor, final ClientHttpRequestFactory
}
@Bean
- public List examplePlugins(final MeterRegistry meterRegistry,
+ public List examplePlugins(final MeterRegistry meterRegistry, final Tracer tracer,
final ScheduledExecutorService scheduler, final Executor executor) {
final CircuitBreakerListener listener = new MetricsCircuitBreakerListener(meterRegistry)
@@ -120,6 +134,7 @@ public List examplePlugins(final MeterRegistry meterRegistry,
new MetricsPlugin(meterRegistry)
.withDefaultTags(Tag.of("clientId", "example")),
new TransientFaultPlugin(),
+ new OpenTracingPlugin(tracer),
new FailsafePlugin(
ImmutableList.of(
new RetryPolicy()
@@ -152,9 +167,35 @@ public List examplePlugins(final MeterRegistry meterRegistry,
new CustomPlugin());
}
+ @Bean
+ public ApacheClientHttpRequestFactory exampleAsyncClientHttpRequestFactory(
+ final Flow flow, final Logbook logbook) throws Exception {
+ return new ApacheClientHttpRequestFactory(
+ HttpClientBuilder.create()
+ .setDefaultRequestConfig(RequestConfig.custom()
+ .setConnectTimeout(5000)
+ .setSocketTimeout(5000)
+ .build())
+ .setConnectionTimeToLive(30, SECONDS)
+ .setMaxConnPerRoute(2)
+ .setMaxConnTotal(20)
+ .addInterceptorFirst(new FlowHttpRequestInterceptor(flow))
+ .addInterceptorLast(new LogbookHttpRequestInterceptor(logbook))
+ .addInterceptorLast(new GzipHttpRequestInterceptor())
+ .addInterceptorLast(new LogbookHttpResponseInterceptor())
+ .setSSLSocketFactory(new SSLConnectionSocketFactory(
+ SSLContexts.custom()
+ .loadTrustMaterial(
+ getClass().getClassLoader().getResource("example.keystore"),
+ "password".toCharArray())
+ .build(),
+ getDefaultHostnameVerifier()))
+ .build());
+ }
+
@Bean(destroyMethod = "shutdown")
public ExecutorService executor(final Tracer tracer) {
- return TracingExecutors.preserve(
+ return new TracedExecutorService(
new ThreadPoolExecutor(
1, 20, 1, MINUTES,
new ArrayBlockingQueue<>(0),
@@ -165,7 +206,7 @@ public ExecutorService executor(final Tracer tracer) {
@Bean(destroyMethod = "shutdown")
public ScheduledExecutorService scheduler(final Tracer tracer) {
- return TracingExecutors.preserve(
+ return new TracedScheduledExecutorService(
Executors.newScheduledThreadPool(
20, // TODO max-connections-total?
new CustomizableThreadFactory("http-example-scheduler-")),
diff --git a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/OpenTracingTestAutoConfiguration.java b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/OpenTracingTestAutoConfiguration.java
new file mode 100644
index 000000000..d79150dd1
--- /dev/null
+++ b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/OpenTracingTestAutoConfiguration.java
@@ -0,0 +1,19 @@
+package org.zalando.riptide.autoconfigure;
+
+import io.opentracing.Tracer;
+import io.opentracing.mock.MockTracer;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.zalando.tracer.autoconfigure.TracerAutoConfiguration;
+
+@Configuration
+@AutoConfigureBefore(TracerAutoConfiguration.class)
+public class OpenTracingTestAutoConfiguration {
+
+ @Bean
+ public Tracer tracer() {
+ return new MockTracer();
+ }
+
+}
diff --git a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/metrics/MetricsTest.java b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/metrics/MetricsTest.java
index 7b587f37f..4911d06be 100644
--- a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/metrics/MetricsTest.java
+++ b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/metrics/MetricsTest.java
@@ -14,9 +14,10 @@
import org.zalando.logbook.spring.LogbookAutoConfiguration;
import org.zalando.riptide.Http;
import org.zalando.riptide.autoconfigure.MetricsTestAutoConfiguration;
+import org.zalando.riptide.autoconfigure.OpenTracingTestAutoConfiguration;
import org.zalando.riptide.autoconfigure.RiptideClientTest;
import org.zalando.riptide.faults.TransientFaultException;
-import org.zalando.tracer.spring.TracerAutoConfiguration;
+import org.zalando.tracer.autoconfigure.TracerAutoConfiguration;
import java.util.List;
import java.util.stream.Collectors;
@@ -43,6 +44,7 @@ final class MetricsTest {
JacksonAutoConfiguration.class,
LogbookAutoConfiguration.class,
TracerAutoConfiguration.class,
+ OpenTracingTestAutoConfiguration.class,
MetricsTestAutoConfiguration.class,
})
static class ContextConfiguration {
diff --git a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/url/UrlResolutionTest.java b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/url/UrlResolutionTest.java
index c992f19ab..83c7752ee 100644
--- a/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/url/UrlResolutionTest.java
+++ b/riptide-spring-boot-autoconfigure/src/test/java/org/zalando/riptide/autoconfigure/url/UrlResolutionTest.java
@@ -5,14 +5,18 @@
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.client.MockRestServiceServer;
import org.zalando.logbook.spring.LogbookAutoConfiguration;
import org.zalando.riptide.Http;
import org.zalando.riptide.autoconfigure.MetricsTestAutoConfiguration;
+import org.zalando.riptide.autoconfigure.OpenTracingTestAutoConfiguration;
import org.zalando.riptide.autoconfigure.RiptideClientTest;
-import org.zalando.tracer.spring.TracerAutoConfiguration;
+import org.zalando.riptide.opentracing.span.HttpUrlSpanDecorator;
+import org.zalando.riptide.opentracing.span.SpanDecorator;
+import org.zalando.tracer.autoconfigure.TracerAutoConfiguration;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
@@ -27,11 +31,17 @@ final class UrlResolutionTest {
JacksonAutoConfiguration.class,
LogbookAutoConfiguration.class,
TracerAutoConfiguration.class,
+ OpenTracingTestAutoConfiguration.class,
MetricsTestAutoConfiguration.class,
})
@ActiveProfiles("default")
static class ContextConfiguration {
+ @Bean
+ public SpanDecorator exampleSpanDecorator() {
+ return new HttpUrlSpanDecorator();
+ }
+
}
@Autowired
diff --git a/riptide-spring-boot-autoconfigure/src/test/resources/application-default.yml b/riptide-spring-boot-autoconfigure/src/test/resources/application-default.yml
index db1f4b293..2b980ce5b 100644
--- a/riptide-spring-boot-autoconfigure/src/test/resources/application-default.yml
+++ b/riptide-spring-boot-autoconfigure/src/test/resources/application-default.yml
@@ -3,6 +3,10 @@ riptide:
transient-fault-detection.enabled: false
stack-trace-preservation.enabled: false
metrics.enabled: true
+ tracing:
+ tags:
+ environment: test
+ propagate-flow-id: false
clients:
example:
base-url: https://example.com/foo
@@ -17,12 +21,19 @@ riptide:
queue-size: 10
stack-trace-preservation.enabled: true
metrics.enabled: true
+ tracing:
+ enabled: true
+ tags:
+ peer.service: example
+ propagate-flow-id: true
ecb:
base-url: http://www.ecb.europa.eu
request-compression.enabled: true
timeouts:
enabled: true
global: 1 seconds
+ tracing:
+ propagate-flow-id: true
chaos:
latency:
enabled: true
@@ -57,8 +68,11 @@ riptide:
retry:
enabled: true
max-retries: 3
+ tracing:
+ enabled: true
bar:
base-url: http://bar
+ transient-fault-detection.enabled: true
retry:
enabled: true
max-retries: 4