filter autosetup and zipkin basic integration
diff --git a/README.adoc b/README.adoc
index e14611b..b42ee7f 100644
--- a/README.adoc
+++ b/README.adoc
@@ -2,6 +2,9 @@
Geronimo OpenTracing provides an OpenTracing implementation and a Microprofile OpenTracing implementation at once.
+NOTE: sampling is not yet supported because it leads to push inconsistent spans to the backend, however you can always specialize (CDI)
+your `FinishSpan` observer to implement it if desired.
+
== Configuration
NOTE: if you are using Microprofile Config it is used, otherwise it uses system properties.
@@ -10,6 +13,8 @@
| Key | Description | Default
|geronimo.opentracing.filter.active|Should OpenTracing be activated|true
|geronimo.opentracing.filter.forcedTracing.urls|Urls for which the tracking should be activated automatically. The urls are actually the requets uri minus the context path.|-
+|geronimo.opentracing.filter.skippedTracing.urls|Urls for which the tracking should be skipped.|-
+|geronimo.opentracing.filter.skippedTracing.matcherType|Type of matcher for the url, default is `prefix` which means a `startsWith` is used on the url but you can also use `regex` which compiles the url as a `Pattern`|prefix
|geronimo.opentracing.filter.forcedTracing.matcherType|Type of matcher for the url, default is `prefix` which means a `startsWith` is used on the url but you can also use `regex` which compiles the url as a `Pattern`|prefix
|geronimo.opentracing.filter.forcedTracing.skipDefaultTags|Should `HTTP_METHOD`, `HTTP_URL` not be added to tags|false
|geronimo.opentracing.server.filter.request.skip.<endpoint class>_<endpoint method>|Should server instrumentation be ignored|false
@@ -22,4 +27,7 @@
|geronimo.opentracing.propagation.headers.traceId|Name of the header used to host the traceId value|`X-B3-TraceId`
|geronimo.opentracing.propagation.headers.baggagePrefix|Prefix of headers used to host the baggage values|`baggage-`
|geronimo.opentracing.cdi.executorServices.wrappedNames|Name (as CDI names of the beans) of executor services which will get an interceptor propagating the current scope (span)|-
+|geronimo.opentracing.id.generator|`counter` (to generate longs), `uuid` (to generate random uuids) or `hex` (to use the hexa representation of the uuid generator). Specifies which kind of trace and span id are in use.|hex
+|geronimo.opentracing.span.converter.zipkin.active|Should spans converted to a zipkin representation. True until there is a standard opentracing format.|true
+|geronimo.opentracing.span.converter.zipkin.logger.active|Should a logger named `org.apache.geronimo.opentracing.zipkin` log each span as a Zipkin JSON. This allows to use a logger implementation to push the spans to any backend (like log4j2 kafka appender). It uses JUL as a facade.|true
|===
diff --git a/geronimo-opentracing-impl/pom.xml b/geronimo-opentracing-impl/pom.xml
index 207dba6..08060c9 100644
--- a/geronimo-opentracing-impl/pom.xml
+++ b/geronimo-opentracing-impl/pom.xml
@@ -36,13 +36,6 @@
<dependencies>
<dependency>
- <groupId>org.eclipse.microprofile.config</groupId>
- <artifactId>microprofile-config-api</artifactId>
- <version>1.2</version>
- <scope>provided</scope>
- <optional>true</optional>
- </dependency>
- <dependency>
<groupId>org.apache.geronimo</groupId>
<artifactId>geronimo-microprofile-opentracing-spec</artifactId>
<version>${project.version}</version>
@@ -56,6 +49,21 @@
</dependency>
<dependency>
+ <groupId>org.eclipse.microprofile.config</groupId>
+ <artifactId>microprofile-config-api</artifactId>
+ <version>1.2</version>
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jsonb_1.0_spec</artifactId>
+ <version>1.0</version>
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-impl</artifactId>
<version>2.0.5</version>
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/config/DefaultOpenTracingConfig.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/config/DefaultOpenTracingConfig.java
similarity index 96%
rename from geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/config/DefaultOpenTracingConfig.java
rename to geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/config/DefaultOpenTracingConfig.java
index 646c3a7..3eaf8a3 100644
--- a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/config/DefaultOpenTracingConfig.java
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/config/DefaultOpenTracingConfig.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.geronimo.microprofile.config;
+package org.apache.geronimo.microprofile.opentracing.config;
import java.io.IOException;
import java.io.InputStream;
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/config/GeronimoOpenTracingConfig.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/config/GeronimoOpenTracingConfig.java
similarity index 94%
rename from geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/config/GeronimoOpenTracingConfig.java
rename to geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/config/GeronimoOpenTracingConfig.java
index d9b3677..e86e1d2 100644
--- a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/config/GeronimoOpenTracingConfig.java
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/config/GeronimoOpenTracingConfig.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.geronimo.microprofile.config;
+package org.apache.geronimo.microprofile.opentracing.config;
@FunctionalInterface
public interface GeronimoOpenTracingConfig {
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/config/OpenTracingConfigMpConfigImpl.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/config/OpenTracingConfigMpConfigImpl.java
similarity index 95%
rename from geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/config/OpenTracingConfigMpConfigImpl.java
rename to geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/config/OpenTracingConfigMpConfigImpl.java
index 7c54bd0..a1d735f 100644
--- a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/config/OpenTracingConfigMpConfigImpl.java
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/config/OpenTracingConfigMpConfigImpl.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.geronimo.microprofile.config;
+package org.apache.geronimo.microprofile.opentracing.config;
import javax.enterprise.inject.Vetoed;
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/config/PrefixedConfig.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/config/PrefixedConfig.java
similarity index 95%
rename from geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/config/PrefixedConfig.java
rename to geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/config/PrefixedConfig.java
index 5575786..0253c2e 100644
--- a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/config/PrefixedConfig.java
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/config/PrefixedConfig.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.geronimo.microprofile.config;
+package org.apache.geronimo.microprofile.opentracing.config;
import javax.enterprise.inject.Vetoed;
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/impl/GeronimoTracer.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/impl/GeronimoTracer.java
index ff9d47b..24f8ab0 100644
--- a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/impl/GeronimoTracer.java
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/impl/GeronimoTracer.java
@@ -40,7 +40,7 @@
import io.opentracing.propagation.Format;
import io.opentracing.propagation.TextMap;
-import org.apache.geronimo.microprofile.config.GeronimoOpenTracingConfig;
+import org.apache.geronimo.microprofile.opentracing.config.GeronimoOpenTracingConfig;
@ApplicationScoped
public class GeronimoTracer implements Tracer {
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/impl/IdGenerator.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/impl/IdGenerator.java
index 9a048c0..c73dc38 100644
--- a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/impl/IdGenerator.java
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/impl/IdGenerator.java
@@ -16,14 +16,57 @@
*/
package org.apache.geronimo.microprofile.opentracing.impl;
+import java.nio.charset.StandardCharsets;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
+import org.apache.geronimo.microprofile.opentracing.config.GeronimoOpenTracingConfig;
@ApplicationScoped
public class IdGenerator {
+ private static final char[] HEXA_MAPPING = "0123456789ABCDEF".toCharArray();
+
+ @Inject
+ private GeronimoOpenTracingConfig config;
+
+ private Supplier<Object> delegate;
+
+ @PostConstruct
+ private void createDelegate() {
+ switch (config.read("id.generator", "counter")) {
+ case "counter":
+ delegate = new Supplier<Object>() {
+ private final AtomicLong counter = new AtomicLong();
+
+ @Override
+ public Object get() {
+ return counter.incrementAndGet();
+ }
+ };
+ break;
+ case "uuid":
+ delegate = () -> UUID.randomUUID().toString();
+ break;
+ case "hex":
+ default:
+ delegate = () -> toHex(UUID.randomUUID().toString().replace("-", "").getBytes(StandardCharsets.UTF_8));
+ }
+ }
public Object next() {
- return UUID.randomUUID().toString();
+ return delegate.get();
+ }
+
+ private String toHex(final byte[] uuid) {
+ final StringBuilder representation = new StringBuilder(uuid.length * 2);
+ for (final byte current : uuid) {
+ representation.append(HEXA_MAPPING[(current >> 4) & 0xF]).append(HEXA_MAPPING[(current & 0xF)]);
+ }
+ return representation.toString();
}
}
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/impl/SpanImpl.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/impl/SpanImpl.java
index 3d4a60f..d68acfc 100644
--- a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/impl/SpanImpl.java
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/impl/SpanImpl.java
@@ -189,6 +189,10 @@
return logs;
}
+ public Collection<ReferenceImpl> getReferences() {
+ return references;
+ }
+
public static class Log {
private final long timestampMicros;
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/cdi/OpenTracingExtension.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/cdi/OpenTracingExtension.java
index a36f406..6b08353 100644
--- a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/cdi/OpenTracingExtension.java
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/cdi/OpenTracingExtension.java
@@ -40,8 +40,10 @@
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Path;
-import org.apache.geronimo.microprofile.config.GeronimoOpenTracingConfig;
+import org.apache.geronimo.microprofile.opentracing.config.GeronimoOpenTracingConfig;
import org.apache.geronimo.microprofile.opentracing.microprofile.thread.OpenTracingExecutorService;
+import org.apache.geronimo.microprofile.opentracing.microprofile.zipkin.ZipkinConverter;
+import org.apache.geronimo.microprofile.opentracing.microprofile.zipkin.ZipkinLogger;
import org.eclipse.microprofile.opentracing.Traced;
public class OpenTracingExtension implements Extension {
@@ -50,12 +52,29 @@
private Collection<String> instrumentedExecutorServices;
+ private boolean useZipkin;
+ private boolean useZipkinLogger;
+
void onStart(@Observes final BeforeBeanDiscovery beforeBeanDiscovery) {
config = GeronimoOpenTracingConfig.create();
+ useZipkin = Boolean.parseBoolean(config.read("span.converter.zipkin.active", "true"));
+ useZipkinLogger = useZipkin && Boolean.parseBoolean(config.read("span.converter.zipkin.logger.active", "true"));
instrumentedExecutorServices = ofNullable(config.read("cdi.executorServices.wrappedNames", null))
.map(s -> Stream.of(s.split(",")).map(String::trim).filter(it -> !it.isEmpty()).collect(toSet())).orElse(null);
}
+ void zipkinConverterToggle(@Observes final ProcessAnnotatedType<ZipkinConverter> onZipkinConverter) {
+ if (!useZipkin) {
+ onZipkinConverter.veto();
+ }
+ }
+
+ void zipkinLoggerToggle(@Observes final ProcessAnnotatedType<ZipkinLogger> onZipkinLogger) {
+ if (!useZipkinLogger) {
+ onZipkinLogger.veto();
+ }
+ }
+
<T> void removeTracedFromJaxRsEndpoints(@Observes @WithAnnotations(Traced.class) final ProcessAnnotatedType<T> pat) {
if (isJaxRs(pat.getAnnotatedType())) { // we have filters with more accurate timing
final AnnotatedTypeConfigurator<T> configurator = pat.configureAnnotatedType();
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/client/OpenTracingClientRequestFilter.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/client/OpenTracingClientRequestFilter.java
index f550774..c02b445 100644
--- a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/client/OpenTracingClientRequestFilter.java
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/client/OpenTracingClientRequestFilter.java
@@ -27,7 +27,7 @@
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
-import org.apache.geronimo.microprofile.config.GeronimoOpenTracingConfig;
+import org.apache.geronimo.microprofile.opentracing.config.GeronimoOpenTracingConfig;
import org.apache.geronimo.microprofile.opentracing.impl.JaxRsHeaderTextMap;
import io.opentracing.Scope;
@@ -76,9 +76,9 @@
Tags.HTTP_METHOD.set(span, context.getMethod());
Tags.HTTP_URL.set(span, context.getUri().toASCIIString());
}
- if ("true".equalsIgnoreCase(
- String.valueOf(context.getProperty("org.apache.geronimo.microprofile.opentracing.client.addPeerTags")))) {
- Tags.PEER_HOSTNAME.set(span, context.getUri().getHost());
+ if (!skipPeerTags) {
+ final String host = context.getUri().getHost();
+ Tags.PEER_HOSTNAME.set(span, host);
Tags.PEER_PORT.set(span, context.getUri().getPort());
}
// customization point
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/server/GeronimoOpenTracingFeature.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/server/GeronimoOpenTracingFeature.java
index 821f14e..a0f9d0a 100644
--- a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/server/GeronimoOpenTracingFeature.java
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/server/GeronimoOpenTracingFeature.java
@@ -30,7 +30,7 @@
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;
-import org.apache.geronimo.microprofile.config.GeronimoOpenTracingConfig;
+import org.apache.geronimo.microprofile.opentracing.config.GeronimoOpenTracingConfig;
import org.eclipse.microprofile.opentracing.Traced;
import io.opentracing.Tracer;
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/server/OpenTracingFilter.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/server/OpenTracingFilter.java
index 4fae300..577c12c 100644
--- a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/server/OpenTracingFilter.java
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/server/OpenTracingFilter.java
@@ -6,6 +6,7 @@
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -24,7 +25,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.apache.geronimo.microprofile.config.GeronimoOpenTracingConfig;
+import org.apache.geronimo.microprofile.opentracing.config.GeronimoOpenTracingConfig;
import org.apache.geronimo.microprofile.opentracing.impl.ScopeManagerImpl;
import org.apache.geronimo.microprofile.opentracing.impl.ServletHeaderTextMap;
@@ -47,29 +48,39 @@
private Collection<Predicate<String>> forcedUrls;
+ private List<Predicate<String>> skipUrls;
+
private boolean skipDefaultTags;
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
skipDefaultTags = Boolean.parseBoolean(config.read("filter.forcedTracing.skipDefaultTags", "false"));
- forcedUrls = ofNullable(config.read("filter.forcedTracing.urls", null)).map(String::trim).filter(v -> !v.isEmpty())
- .map(v -> {
- final String matchingType = config.read("filter.forcedTracing.matcherType", "prefix");
- final Function<String, Predicate<String>> matcherFactory;
- switch (matchingType) {
- case "regex":
- matcherFactory = from -> {
- final Pattern compiled = Pattern.compile(from);
- return url -> compiled.matcher(url).matches();
- };
- break;
- case "prefix":
- default:
- matcherFactory = from -> url -> url.startsWith(from);
- }
- return Stream.of(v.split(",")).map(String::trim).filter(it -> !it.isEmpty()).map(matcherFactory)
- .collect(toList());
- }).orElse(null);
+ forcedUrls = ofNullable(config.read("filter.forcedTracing.urls", null))
+ .map(String::trim).filter(v -> !v.isEmpty())
+ .map(v -> toMatchingPredicates(v, "forcedTracing"))
+ .orElse(null);
+ skipUrls = ofNullable(config.read("filter.skippedTracing.urls", null))
+ .map(String::trim).filter(v -> !v.isEmpty())
+ .map(v -> toMatchingPredicates(v, "skippedTracing"))
+ .orElse(null);
+ }
+
+ private List<Predicate<String>> toMatchingPredicates(final String v, final String keyMarker) {
+ final String matchingType = config.read("filter." + keyMarker + ".matcherType", "prefix");
+ final Function<String, Predicate<String>> matcherFactory;
+ switch (matchingType) {
+ case "regex":
+ matcherFactory = from -> {
+ final Pattern compiled = Pattern.compile(from);
+ return url -> compiled.matcher(url).matches();
+ };
+ break;
+ case "prefix":
+ default:
+ matcherFactory = from -> url -> url.startsWith(from);
+ }
+ return Stream.of(v.split(",")).map(String::trim).filter(it -> !it.isEmpty()).map(matcherFactory)
+ .collect(toList());
}
@Override
@@ -103,6 +114,14 @@
request.setAttribute(OpenTracingFilter.class.getName(), scope);
}
}
+ if (skipUrls != null && !skipUrls.isEmpty()) {
+ final HttpServletRequest req = HttpServletRequest.class.cast(request);
+ final String matching = req.getRequestURI().substring(req.getContextPath().length());
+ if (forcedUrls.stream().anyMatch(p -> p.test(matching))) {
+ chain.doFilter(request, response);
+ return;
+ }
+ }
try {
chain.doFilter(request, response);
} catch (final Exception ex) {
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/server/ServletTracingSetup.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/server/ServletTracingSetup.java
index 72281fd..824525c 100644
--- a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/server/ServletTracingSetup.java
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/server/ServletTracingSetup.java
@@ -24,7 +24,7 @@
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
-import org.apache.geronimo.microprofile.config.GeronimoOpenTracingConfig;
+import org.apache.geronimo.microprofile.opentracing.config.GeronimoOpenTracingConfig;
public class ServletTracingSetup implements ServletContainerInitializer {
@Override
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/zipkin/ZipkinConverter.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/zipkin/ZipkinConverter.java
new file mode 100644
index 0000000..d1acc15
--- /dev/null
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/zipkin/ZipkinConverter.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.geronimo.microprofile.opentracing.microprofile.zipkin;
+
+import static java.util.Collections.emptyList;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Event;
+import javax.enterprise.event.Observes;
+import javax.inject.Inject;
+
+import org.apache.geronimo.microprofile.opentracing.config.GeronimoOpenTracingConfig;
+import org.apache.geronimo.microprofile.opentracing.impl.FinishedSpan;
+import org.apache.geronimo.microprofile.opentracing.impl.SpanImpl;
+
+import io.opentracing.Span;
+import io.opentracing.tag.Tags;
+
+// when writing this observer, opentracing has no standard propagation nor exchange format
+// so falling back on zipkin
+@ApplicationScoped
+public class ZipkinConverter {
+
+ @Inject
+ private Event<ZipkinSpan> zipkinSpanEvent;
+
+ @Inject
+ private GeronimoOpenTracingConfig config;
+
+ public void onSpan(@Observes final FinishedSpan finishedSpan) {
+ final Span from = finishedSpan.getSpan();
+ if (!SpanImpl.class.isInstance(from)) {
+ throw new IllegalStateException("Unsupported span type: " + from + ", maybe check your configuration");
+ }
+ zipkinSpanEvent.fire(toZipkin(SpanImpl.class.cast(from)));
+ }
+
+ private ZipkinSpan toZipkin(final SpanImpl span) {
+ final ZipkinSpan zipkin = new ZipkinSpan();
+ zipkin.setParentId(Long.class.cast(span.getParentId()));
+ zipkin.setTraceId(Long.class.cast(span.getTraceId()));
+ zipkin.setId(Long.class.cast(span.getId()));
+ zipkin.setName(span.getName());
+ zipkin.setKind(span.getKind());
+ zipkin.setTimestamp(span.getTimestamp());
+ zipkin.setDuration(span.getDuration());
+ zipkin.setTags(span.getTags().entrySet().stream().filter(e -> !Tags.SPAN_KIND.getKey().equalsIgnoreCase(e.getKey()))
+ .collect(toMap(Map.Entry::getKey, e -> String.valueOf(e.getValue()))));
+
+ final ZipkinSpan.ZipkinEndpoint endpoint = toEndpoint(span);
+ zipkin.setAnnotations(toAnnotations(endpoint, span));
+ zipkin.setBinaryAnnotations(toBinaryAnnotations(endpoint, span.getTags()));
+ return zipkin;
+ }
+
+ private ZipkinSpan.ZipkinEndpoint toEndpoint(final SpanImpl span) {
+ final Map<String, Object> tags = span.getTags();
+ switch (String.valueOf(tags.get(Tags.SPAN_KIND.getKey()))) {
+ case Tags.SPAN_KIND_CLIENT: {
+ String ipv4 = (String) tags.get(Tags.PEER_HOST_IPV4.getKey());
+ String ipv6 = (String) tags.get(Tags.PEER_HOST_IPV6.getKey());
+ if (ipv4 == null && ipv6 == null && tags.containsKey(Tags.PEER_HOSTNAME.getKey())) {
+ try {
+ final String hostAddress = InetAddress.getByName(tags.get(Tags.PEER_HOSTNAME.getKey()).toString())
+ .getHostAddress();
+ if (hostAddress.contains("::")) {
+ ipv6 = hostAddress;
+ } else {
+ ipv4 = hostAddress;
+ }
+ } catch (final UnknownHostException e) {
+ // no-op
+ }
+ }
+
+ final Integer port = (Integer) tags.get(Tags.PEER_PORT.getKey());
+
+ final ZipkinSpan.ZipkinEndpoint endpoint = new ZipkinSpan.ZipkinEndpoint();
+ endpoint.setServiceName(span.getName());
+ endpoint.setIpv4(ipv4);
+ endpoint.setIpv6(ipv6);
+ endpoint.setPort(port == null ? 0 : port);
+
+ return endpoint;
+ }
+ case Tags.SPAN_KIND_SERVER: {
+ final String url = (String) tags.get(Tags.HTTP_URL.getKey());
+ String ipv4 = null;
+ String ipv6 = null;
+ Integer port = null;
+ if (url != null) {
+ try {
+ final URL asUrl = new URL(url);
+ port = asUrl.getPort();
+
+ final String host = asUrl.getHost();
+ final String hostAddress = host.contains(":") ? host : InetAddress.getByName(host).getHostAddress();
+ if (hostAddress.contains("::")) {
+ ipv6 = hostAddress;
+ } else {
+ ipv4 = hostAddress;
+ }
+ } catch (final UnknownHostException | MalformedURLException e) {
+ // no-op
+ }
+ }
+
+ final ZipkinSpan.ZipkinEndpoint endpoint = new ZipkinSpan.ZipkinEndpoint();
+ endpoint.setServiceName(span.getName());
+ endpoint.setIpv4(ipv4);
+ endpoint.setIpv6(ipv6);
+ endpoint.setPort(port == null ? 0 : port);
+
+ return endpoint;
+ }
+ default:
+ return null;
+ }
+ }
+
+ private List<ZipkinSpan.ZipkinAnnotation> toAnnotations(final ZipkinSpan.ZipkinEndpoint endpoint, final SpanImpl span) {
+ final Map<String, Object> tags = span.getTags();
+ final List<ZipkinSpan.ZipkinAnnotation> annotations = new ArrayList<>(2);
+ switch (String.valueOf(tags.get(Tags.SPAN_KIND.getKey()))) {
+ case Tags.SPAN_KIND_CLIENT: {
+ {
+ final ZipkinSpan.ZipkinAnnotation clientSend = new ZipkinSpan.ZipkinAnnotation();
+ clientSend.setValue("cs");
+ clientSend.setTimestamp(span.getTimestamp());
+ clientSend.setEndpoint(endpoint);
+ annotations.add(clientSend);
+ }
+ {
+ final ZipkinSpan.ZipkinAnnotation clientReceived = new ZipkinSpan.ZipkinAnnotation();
+ clientReceived.setValue("cr");
+ clientReceived.setTimestamp(span.getTimestamp() + span.getDuration());
+ clientReceived.setEndpoint(endpoint);
+ annotations.add(clientReceived);
+ }
+ return annotations;
+ }
+ case Tags.SPAN_KIND_SERVER: {
+ {
+ final ZipkinSpan.ZipkinAnnotation serverReceived = new ZipkinSpan.ZipkinAnnotation();
+ serverReceived.setValue("sr");
+ serverReceived.setTimestamp(span.getTimestamp());
+ serverReceived.setEndpoint(endpoint);
+ annotations.add(serverReceived);
+ }
+ {
+
+ final ZipkinSpan.ZipkinAnnotation serverSend = new ZipkinSpan.ZipkinAnnotation();
+ serverSend.setValue("ss");
+ serverSend.setTimestamp(span.getTimestamp() + span.getDuration());
+ serverSend.setEndpoint(endpoint);
+ annotations.add(serverSend);
+ }
+ return annotations;
+ }
+ default:
+ return emptyList();
+ }
+ }
+
+ private List<ZipkinSpan.ZipkinBinaryAnnotation> toBinaryAnnotations(final ZipkinSpan.ZipkinEndpoint endpoint,
+ final Map<String, Object> tags) {
+ return tags.entrySet().stream().map(tag -> {
+ final ZipkinSpan.ZipkinBinaryAnnotation annotations = new ZipkinSpan.ZipkinBinaryAnnotation();
+ annotations.setType(findAnnotationType(tag.getValue()));
+ annotations.setKey(tag.getKey());
+ annotations.setValue(tag.getValue());
+ annotations.setEndpoint(endpoint);
+ return annotations;
+ }).collect(toList());
+ }
+
+ private int findAnnotationType(final Object value) {
+ if (String.class.isInstance(value)) {
+ return 6;
+ }
+ if (Double.class.isInstance(value)) {
+ return 5;
+ }
+ if (Long.class.isInstance(value)) {
+ return 4;
+ }
+ if (Integer.class.isInstance(value)) {
+ return 3;
+ }
+ if (Short.class.isInstance(value)) {
+ return 2;
+ }
+ if (Byte.class.isInstance(value)) {
+ return 1;
+ }
+ return 6; // todo?
+ }
+}
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/zipkin/ZipkinLogger.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/zipkin/ZipkinLogger.java
new file mode 100644
index 0000000..06acd3e
--- /dev/null
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/zipkin/ZipkinLogger.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.geronimo.microprofile.opentracing.microprofile.zipkin;
+
+import java.util.logging.Logger;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Observes;
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+
+// this allows to integrate with any backend using appenders.
+@ApplicationScoped
+public class ZipkinLogger {
+
+ private final Logger logger = Logger.getLogger(ZipkinLogger.class.getName());
+
+ private Jsonb jsonb;
+
+ @PostConstruct
+ private void init() {
+ jsonb = JsonbBuilder.create();
+ }
+
+ @PreDestroy
+ private void destroy() {
+ try {
+ jsonb.close();
+ } catch (final Exception e) {
+ // no-op
+ }
+ }
+
+ public void onZipkinSpan(@Observes final ZipkinSpan zipkinSpan) {
+ logger.info(jsonb.toJson(zipkinSpan));
+ }
+}
diff --git a/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/zipkin/ZipkinSpan.java b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/zipkin/ZipkinSpan.java
new file mode 100644
index 0000000..52314de
--- /dev/null
+++ b/geronimo-opentracing-impl/src/main/java/org/apache/geronimo/microprofile/opentracing/microprofile/zipkin/ZipkinSpan.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.geronimo.microprofile.opentracing.microprofile.zipkin;
+
+import java.util.List;
+import java.util.Map;
+
+public class ZipkinSpan {
+ private long traceId;
+ private Long parentId;
+ private long id;
+ private String name;
+ private String kind;
+ private Long timestamp;
+ private Long duration;
+ private ZipkinEndpoint localEndpoint;
+ private ZipkinEndpoint remoteEndpoint;
+ private List<ZipkinAnnotation> annotations;
+ private List<ZipkinBinaryAnnotation> binaryAnnotations;
+ private Map<String, String> tags;
+ private Boolean debug;
+ private Boolean shared;
+
+ public long getTraceId() {
+ return traceId;
+ }
+
+ public void setTraceId(final long traceId) {
+ this.traceId = traceId;
+ }
+
+ public Long getParentId() {
+ return parentId;
+ }
+
+ public void setParentId(final Long parentId) {
+ this.parentId = parentId;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(final long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ public Long getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(final Long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public Long getDuration() {
+ return duration;
+ }
+
+ public void setDuration(final Long duration) {
+ this.duration = duration;
+ }
+
+ public List<ZipkinAnnotation> getAnnotations() {
+ return annotations;
+ }
+
+ public void setAnnotations(final List<ZipkinAnnotation> annotations) {
+ this.annotations = annotations;
+ }
+
+ public List<ZipkinBinaryAnnotation> getBinaryAnnotations() {
+ return binaryAnnotations;
+ }
+
+ public void setBinaryAnnotations(final List<ZipkinBinaryAnnotation> binaryAnnotations) {
+ this.binaryAnnotations = binaryAnnotations;
+ }
+
+ public Boolean getDebug() {
+ return debug;
+ }
+
+ public void setDebug(final Boolean debug) {
+ this.debug = debug;
+ }
+
+ public String getKind() {
+ return kind;
+ }
+
+ public void setKind(final String kind) {
+ this.kind = kind;
+ }
+
+ public ZipkinEndpoint getLocalEndpoint() {
+ return localEndpoint;
+ }
+
+ public void setLocalEndpoint(final ZipkinEndpoint localEndpoint) {
+ this.localEndpoint = localEndpoint;
+ }
+
+ public ZipkinEndpoint getRemoteEndpoint() {
+ return remoteEndpoint;
+ }
+
+ public void setRemoteEndpoint(final ZipkinEndpoint remoteEndpoint) {
+ this.remoteEndpoint = remoteEndpoint;
+ }
+
+ public Map<String, String> getTags() {
+ return tags;
+ }
+
+ public void setTags(final Map<String, String> tags) {
+ this.tags = tags;
+ }
+
+ public Boolean getShared() {
+ return shared;
+ }
+
+ public void setShared(final Boolean shared) {
+ this.shared = shared;
+ }
+
+ public static class ZipkinAnnotation {
+ private long timestamp;
+ private String value;
+ private ZipkinEndpoint endpoint;
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(final long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(final String value) {
+ this.value = value;
+ }
+
+ public ZipkinEndpoint getEndpoint() {
+ return endpoint;
+ }
+
+ public void setEndpoint(final ZipkinEndpoint endpoint) {
+ this.endpoint = endpoint;
+ }
+ }
+
+ public static class ZipkinBinaryAnnotation {
+ private String key;
+ private int type;
+ private Object value;
+ private ZipkinEndpoint endpoint;
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(final String key) {
+ this.key = key;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(final int type) {
+ this.type = type;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setValue(final Object value) {
+ this.value = value;
+ }
+
+ public ZipkinEndpoint getEndpoint() {
+ return endpoint;
+ }
+
+ public void setEndpoint(final ZipkinEndpoint endpoint) {
+ this.endpoint = endpoint;
+ }
+ }
+
+ public static class ZipkinEndpoint {
+ private String serviceName;
+ private String ipv4;
+ private String ipv6;
+ private int port;
+
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ public void setServiceName(final String serviceName) {
+ this.serviceName = serviceName;
+ }
+
+ public String getIpv4() {
+ return ipv4;
+ }
+
+ public void setIpv4(final String ipv4) {
+ this.ipv4 = ipv4;
+ }
+
+ public String getIpv6() {
+ return ipv6;
+ }
+
+ public void setIpv6(final String ipv6) {
+ this.ipv6 = ipv6;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(final int port) {
+ this.port = port;
+ }
+ }
+}
diff --git a/geronimo-opentracing-impl/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/LongIdGenerator.java b/geronimo-opentracing-impl/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/LongIdGenerator.java
deleted file mode 100644
index d4e56c8..0000000
--- a/geronimo-opentracing-impl/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/LongIdGenerator.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.geronimo.microprofile.opentracing.tck.setup;
-
-import java.util.concurrent.atomic.AtomicLong;
-
-import javax.enterprise.context.ApplicationScoped;
-import javax.enterprise.inject.Specializes;
-
-import org.apache.geronimo.microprofile.opentracing.impl.IdGenerator;
-
-@Specializes
-@ApplicationScoped
-public class LongIdGenerator extends IdGenerator {
-
- private final AtomicLong id = new AtomicLong();
-
- public Object next() {
- return id.incrementAndGet();
- }
-}
diff --git a/geronimo-opentracing-impl/src/test/resources/META-INF/geronimo/microprofile/opentracing.properties b/geronimo-opentracing-impl/src/test/resources/META-INF/geronimo/microprofile/opentracing.properties
new file mode 100644
index 0000000..fed2729
--- /dev/null
+++ b/geronimo-opentracing-impl/src/test/resources/META-INF/geronimo/microprofile/opentracing.properties
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+geronimo.opentracing.id.generator = counter
+geronimo.opentracing.span.converter.zipkin.active = false
+