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
+