You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2016/04/01 16:08:44 UTC

[3/3] camel git commit: CAMEL-9759: camel-zipkin - Instrument Camel. Work in progress.

CAMEL-9759: camel-zipkin - Instrument Camel. Work in progress.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/7e40ae05
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/7e40ae05
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/7e40ae05

Branch: refs/heads/master
Commit: 7e40ae05f947cdb130564dbe1f761c3fe4367af5
Parents: 197e200
Author: Claus Ibsen <da...@apache.org>
Authored: Fri Apr 1 15:46:17 2016 +0200
Committer: Claus Ibsen <da...@apache.org>
Committed: Fri Apr 1 16:08:34 2016 +0200

----------------------------------------------------------------------
 .../zipkin/ZipkinClientRequestAdapter.java      |   4 +-
 .../zipkin/ZipkinClientResponseAdaptor.java     |   4 +-
 .../camel/zipkin/ZipkinEventNotifier.java       | 684 -------------------
 .../zipkin/ZipkinLoggingSpanCollector.java      |  10 +-
 .../zipkin/ZipkinServerRequestAdapter.java      |   4 +-
 .../zipkin/ZipkinServerResponseAdapter.java     |   4 +-
 .../org/apache/camel/zipkin/ZipkinState.java    |  20 +-
 .../org/apache/camel/zipkin/ZipkinTracer.java   | 660 ++++++++++++++++++
 .../apache/camel/zipkin/ZipkinABCRouteTest.java |  86 +++
 .../camel/zipkin/ZipkinRouteConcurrentTest.java |   4 +-
 .../zipkin/ZipkinSimpleFallbackRouteTest.java   |   4 +-
 .../camel/zipkin/ZipkinSimpleRouteTest.java     |   8 +-
 .../apache/camel/zipkin/ZipkinTwoRouteTest.java |   4 +-
 .../zipkin/scribe/ZipkinABCRouteScribe.java     |  90 +++
 .../scribe/ZipkinAutoConfigureScribe.java       |   6 +-
 .../scribe/ZipkinOneRouteFallbackScribe.java    |   6 +-
 .../zipkin/scribe/ZipkinOneRouteScribe.java     |   6 +-
 .../zipkin/scribe/ZipkinSimpleRouteScribe.java  |   6 +-
 .../zipkin/scribe/ZipkinTimerRouteScribe.java   |   6 +-
 .../zipkin/scribe/ZipkinTwoRouteScribe.java     |   6 +-
 .../src/test/resources/log4j.properties         |   2 +-
 21 files changed, 889 insertions(+), 735 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinClientRequestAdapter.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinClientRequestAdapter.java b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinClientRequestAdapter.java
index 3060f18..a9664a1 100644
--- a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinClientRequestAdapter.java
+++ b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinClientRequestAdapter.java
@@ -33,14 +33,14 @@ import org.apache.camel.util.URISupport;
 
 public final class ZipkinClientRequestAdapter implements ClientRequestAdapter {
 
-    private final ZipkinEventNotifier eventNotifier;
+    private final ZipkinTracer eventNotifier;
     private final String serviceName;
     private final Exchange exchange;
     private final Endpoint endpoint;
     private final String spanName;
     private final String url;
 
-    public ZipkinClientRequestAdapter(ZipkinEventNotifier eventNotifier, String serviceName, Exchange exchange, Endpoint endpoint) {
+    public ZipkinClientRequestAdapter(ZipkinTracer eventNotifier, String serviceName, Exchange exchange, Endpoint endpoint) {
         this.eventNotifier = eventNotifier;
         this.serviceName = serviceName;
         this.exchange = exchange;

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinClientResponseAdaptor.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinClientResponseAdaptor.java b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinClientResponseAdaptor.java
index 2e4fded..04ef33e 100644
--- a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinClientResponseAdaptor.java
+++ b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinClientResponseAdaptor.java
@@ -29,12 +29,12 @@ import org.apache.camel.util.URISupport;
 
 public class ZipkinClientResponseAdaptor implements ClientResponseAdapter {
 
-    private final ZipkinEventNotifier eventNotifier;
+    private final ZipkinTracer eventNotifier;
     private final Exchange exchange;
     private final Endpoint endpoint;
     private final String url;
 
-    public ZipkinClientResponseAdaptor(ZipkinEventNotifier eventNotifier, Exchange exchange, Endpoint endpoint) {
+    public ZipkinClientResponseAdaptor(ZipkinTracer eventNotifier, Exchange exchange, Endpoint endpoint) {
         this.eventNotifier = eventNotifier;
         this.exchange = exchange;
         this.endpoint = endpoint;

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinEventNotifier.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinEventNotifier.java b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinEventNotifier.java
deleted file mode 100644
index 5135557..0000000
--- a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinEventNotifier.java
+++ /dev/null
@@ -1,684 +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
- * <p/>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p/>
- * 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.camel.zipkin;
-
-import java.io.Closeable;
-import java.util.EventObject;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import com.github.kristofa.brave.Brave;
-import com.github.kristofa.brave.ClientSpanThreadBinder;
-import com.github.kristofa.brave.Sampler;
-import com.github.kristofa.brave.ServerSpan;
-import com.github.kristofa.brave.ServerSpanThreadBinder;
-import com.github.kristofa.brave.SpanCollector;
-import com.github.kristofa.brave.scribe.ScribeSpanCollector;
-import com.twitter.zipkin.gen.Span;
-import org.apache.camel.CamelContext;
-import org.apache.camel.CamelContextAware;
-import org.apache.camel.Endpoint;
-import org.apache.camel.Exchange;
-import org.apache.camel.StatefulService;
-import org.apache.camel.api.management.ManagedAttribute;
-import org.apache.camel.api.management.ManagedResource;
-import org.apache.camel.component.properties.ServiceHostPropertiesFunction;
-import org.apache.camel.component.properties.ServicePortPropertiesFunction;
-import org.apache.camel.management.event.ExchangeCompletedEvent;
-import org.apache.camel.management.event.ExchangeCreatedEvent;
-import org.apache.camel.management.event.ExchangeFailedEvent;
-import org.apache.camel.management.event.ExchangeSendingEvent;
-import org.apache.camel.management.event.ExchangeSentEvent;
-import org.apache.camel.support.EventNotifierSupport;
-import org.apache.camel.util.EndpointHelper;
-import org.apache.camel.util.IOHelper;
-import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.ServiceHelper;
-
-import static org.apache.camel.builder.ExpressionBuilder.routeIdExpression;
-
-/**
- * To use zipkin with Camel then setup this {@link org.apache.camel.spi.EventNotifier} in your Camel application.
- * <p/>
- * Events (span) are captured for incoming and outgoing messages being sent to/from Camel.
- * This means you need to configure which which Camel endpoints that maps to zipkin service names.
- * The mapping can be configured using
- * <ul>
- * <li>route id - A Camel route id</li>
- * <li>endpoint url - A Camel endpoint url</li>
- * </ul>
- * For both kinds you can use wildcards and regular expressions to match, which is using the rules from
- * {@link EndpointHelper#matchPattern(String, String)} and {@link EndpointHelper#matchEndpoint(CamelContext, String, String)}
- * <p/>
- * To match all Camel messages you can use <tt>*</tt> in the pattern and configure that to the same service name.
- * <br/>
- * If no mapping has been configured then Camel will fallback and use endpoint uri's as service names.
- * However its recommended to configure service mappings so you can use human logic names instead of Camel
- * endpoint uris in the names.
- * <p/>
- * Camel will auto-configure a {@link ScribeSpanCollector} if no SpanCollector explicit has been configured, and
- * if the hostname and port to the span collector has been configured as environment variables
- * <ul>
- *     <li>ZIPKIN_COLLECTOR_SERVICE_HOST - The hostname</li>
- *     <li>ZIPKIN_COLLECTOR_SERVICE_PORT - The port number</li>
- * </ul>
- */
-@ManagedResource(description = "Managing ZipkinEventNotifier")
-public class ZipkinEventNotifier extends EventNotifierSupport implements StatefulService, CamelContextAware {
-
-    private final Map<String, Brave> braves = new HashMap<>();
-    private transient boolean useFallbackServiceNames;
-
-    private CamelContext camelContext;
-    private String hostName;
-    private int port;
-    private float rate = 1.0f;
-    private SpanCollector spanCollector;
-    private Map<String, String> clientServiceMappings = new HashMap<>();
-    private Map<String, String> serverServiceMappings = new HashMap<>();
-    private Set<String> excludePatterns = new HashSet<>();
-    private boolean includeMessageBody;
-
-    public ZipkinEventNotifier() {
-    }
-
-    public CamelContext getCamelContext() {
-        return camelContext;
-    }
-
-    public void setCamelContext(CamelContext camelContext) {
-        this.camelContext = camelContext;
-    }
-
-    public String getHostName() {
-        return hostName;
-    }
-
-    /**
-     * Sets a hostname for the remote zipkin server to use.
-     */
-    public void setHostName(String hostName) {
-        this.hostName = hostName;
-    }
-
-    public int getPort() {
-        return port;
-    }
-
-    /**
-     * Sets the port number for the remote zipkin server to use.
-     */
-    public void setPort(int port) {
-        this.port = port;
-    }
-
-    public float getRate() {
-        return rate;
-    }
-
-    /**
-     * Configures a rate that decides how many events should be traced by zipkin.
-     * The rate is expressed as a percentage (1.0f = 100%, 0.5f is 50%, 0.1f is 10%).
-     *
-     * @param rate minimum sample rate is 0.0001, or 0.01% of traces
-     */
-    public void setRate(float rate) {
-        this.rate = rate;
-    }
-
-    public SpanCollector getSpanCollector() {
-        return spanCollector;
-    }
-
-    /**
-     * The collector to use for sending zipkin span events to the zipkin server.
-     */
-    public void setSpanCollector(SpanCollector spanCollector) {
-        this.spanCollector = spanCollector;
-    }
-
-    public String getServiceName() {
-        return clientServiceMappings.get("*");
-    }
-
-    /**
-     * To use a global service name that matches all Camel events
-     */
-    public void setServiceName(String serviceName) {
-        clientServiceMappings.put("*", serviceName);
-        serverServiceMappings.put("*", serviceName);
-    }
-
-    public Map<String, String> getClientServiceMappings() {
-        return clientServiceMappings;
-    }
-
-    public void setClientServiceMappings(Map<String, String> clientServiceMappings) {
-        this.clientServiceMappings = clientServiceMappings;
-    }
-
-    /**
-     * Adds a client service mapping that matches Camel events to the given zipkin service name.
-     * See more details at the class javadoc.
-     *
-     * @param pattern  the pattern such as route id, endpoint url
-     * @param serviceName the zipkin service name
-     */
-    public void addClientServiceMapping(String pattern, String serviceName) {
-        clientServiceMappings.put(pattern, serviceName);
-    }
-
-    public Map<String, String> getServerServiceMappings() {
-        return serverServiceMappings;
-    }
-
-    public void setServerServiceMappings(Map<String, String> serverServiceMappings) {
-        this.serverServiceMappings = serverServiceMappings;
-    }
-
-    /**
-     * Adds a server service mapping that matches Camel events to the given zipkin service name.
-     * See more details at the class javadoc.
-     *
-     * @param pattern  the pattern such as route id, endpoint url
-     * @param serviceName the zipkin service name
-     */
-    public void addServerServiceMapping(String pattern, String serviceName) {
-        serverServiceMappings.put(pattern, serviceName);
-    }
-
-    public Set<String> getExcludePatterns() {
-        return excludePatterns;
-    }
-
-    public void setExcludePatterns(Set<String> excludePatterns) {
-        this.excludePatterns = excludePatterns;
-    }
-
-    /**
-     * Adds an exclude pattern that will disable tracing with zipkin for Camel messages that matches the pattern.
-     *
-     * @param pattern  the pattern such as route id, endpoint url
-     */
-    public void addExcludePattern(String pattern) {
-        excludePatterns.add(pattern);
-    }
-
-    @ManagedAttribute(description = "Whether to include the Camel message body in the zipkin traces")
-    public boolean isIncludeMessageBody() {
-        return includeMessageBody;
-    }
-
-    /**
-     * Whether to include the Camel message body in the zipkin traces.
-     * <p/>
-     * This is not recommended for production usage, or when having big payloads. You can limit the size by
-     * configuring the <a href="http://camel.apache.org/how-do-i-set-the-max-chars-when-debug-logging-messages-in-camel.html">max debug log size</a>.
-     */
-    @ManagedAttribute(description = "Whether to include the Camel message body in the zipkin traces")
-    public void setIncludeMessageBody(boolean includeMessageBody) {
-        this.includeMessageBody = includeMessageBody;
-    }
-
-    @Override
-    protected void doStart() throws Exception {
-        super.doStart();
-
-        ObjectHelper.notNull(camelContext, "CamelContext", this);
-
-        if (spanCollector == null) {
-            if (hostName != null && port > 0) {
-                log.info("Configuring Zipkin ScribeSpanCollector using host: {} and port: {}", hostName, port);
-                spanCollector = new ScribeSpanCollector(hostName, port);
-            } else {
-                // is there a zipkin service setup as ENV variable to auto register a scribe span collector
-                String host = new ServiceHostPropertiesFunction().apply("zipkin-collector");
-                String port = new ServicePortPropertiesFunction().apply("zipkin-collector");
-                if (ObjectHelper.isNotEmpty(host) && ObjectHelper.isNotEmpty(port)) {
-                    log.info("Auto-configuring Zipkin ScribeSpanCollector using host: {} and port: {}", host, port);
-                    int num = camelContext.getTypeConverter().mandatoryConvertTo(Integer.class, port);
-                    spanCollector = new ScribeSpanCollector(host, num);
-                }
-            }
-        }
-
-        ObjectHelper.notNull(spanCollector, "SpanCollector", this);
-
-        if (clientServiceMappings.isEmpty()) {
-            log.warn("No service name(s) has been configured. Camel will fallback and use endpoint uris as service names.");
-            useFallbackServiceNames = true;
-        }
-
-        // create braves mapped per service name
-        for (Map.Entry<String, String> entry : clientServiceMappings.entrySet()) {
-            String pattern = entry.getKey();
-            String serviceName = entry.getValue();
-            createBraveForService(pattern, serviceName);
-        }
-        for (Map.Entry<String, String> entry : serverServiceMappings.entrySet()) {
-            String pattern = entry.getKey();
-            String serviceName = entry.getValue();
-            createBraveForService(pattern, serviceName);
-        }
-
-        ServiceHelper.startService(spanCollector);
-    }
-
-    @Override
-    protected void doStop() throws Exception {
-        super.doStop();
-
-        // stop and close collector
-        ServiceHelper.stopAndShutdownService(spanCollector);
-        if (spanCollector instanceof Closeable) {
-            IOHelper.close((Closeable) spanCollector);
-        }
-
-        braves.clear();
-    }
-
-    @Override
-    public boolean isEnabled(EventObject event) {
-        return event instanceof ExchangeSendingEvent
-                || event instanceof ExchangeSentEvent
-                || event instanceof ExchangeCreatedEvent
-                || event instanceof ExchangeCompletedEvent
-                || event instanceof ExchangeFailedEvent;
-    }
-
-    private String getServiceName(EventObject event, Exchange exchange, Endpoint endpoint, boolean server, boolean client) {
-        if (client) {
-            return getServiceName(event, exchange, endpoint, clientServiceMappings, server, client);
-        } else if (server) {
-            return getServiceName(event, exchange, endpoint, serverServiceMappings, server, client);
-        } else {
-            return null;
-        }
-    }
-
-    private String getServiceName(EventObject event, Exchange exchange, Endpoint endpoint, Map<String, String> serviceMappings, boolean server, boolean client) {
-        String answer = null;
-
-        // endpoint takes precedence over route
-        if (endpoint != null) {
-            String url = endpoint.getEndpointUri();
-            if (url != null) {
-                // exclude patterns take precedence
-                for (String pattern : excludePatterns) {
-                    if (EndpointHelper.matchEndpoint(exchange.getContext(), url, pattern)) {
-                        return null;
-                    }
-                }
-                for (Map.Entry<String, String> entry : serviceMappings.entrySet()) {
-                    String pattern = entry.getKey();
-                    if (EndpointHelper.matchEndpoint(exchange.getContext(), url, pattern)) {
-                        answer = entry.getValue();
-                        break;
-                    }
-                }
-            }
-        }
-
-        // special for created event as its a server side to know where it was from
-        if (server && (event instanceof ExchangeCreatedEvent || event instanceof ExchangeCompletedEvent || event instanceof ExchangeFailedEvent)) {
-            if (answer == null && exchange.getFromEndpoint() != null) {
-                String url = exchange.getFromEndpoint().getEndpointUri();
-                if (url != null) {
-                    // exclude patterns take precedence
-                    for (String pattern : excludePatterns) {
-                        if (EndpointHelper.matchEndpoint(exchange.getContext(), url, pattern)) {
-                            return null;
-                        }
-                    }
-                    for (Map.Entry<String, String> entry : serviceMappings.entrySet()) {
-                        String pattern = entry.getKey();
-                        if (EndpointHelper.matchEndpoint(exchange.getContext(), url, pattern)) {
-                            answer = entry.getValue();
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-
-        // route
-        if (answer == null) {
-            String id = routeIdExpression().evaluate(exchange, String.class);
-            if (id != null) {
-                // exclude patterns take precedence
-                for (String pattern : excludePatterns) {
-                    if (EndpointHelper.matchPattern(id, pattern)) {
-                        return null;
-                    }
-                }
-                for (Map.Entry<String, String> entry : serviceMappings.entrySet()) {
-                    String pattern = entry.getKey();
-                    if (EndpointHelper.matchPattern(id, pattern)) {
-                        answer = entry.getValue();
-                        break;
-                    }
-                }
-            }
-        }
-
-        if (answer == null) {
-            String id = exchange.getFromRouteId();
-            if (id != null) {
-                // exclude patterns take precedence
-                for (String pattern : excludePatterns) {
-                    if (EndpointHelper.matchPattern(id, pattern)) {
-                        return null;
-                    }
-                }
-                for (Map.Entry<String, String> entry : serviceMappings.entrySet()) {
-                    String pattern = entry.getKey();
-                    if (EndpointHelper.matchPattern(id, pattern)) {
-                        answer = entry.getValue();
-                        break;
-                    }
-                }
-            }
-        }
-
-        if (answer == null && useFallbackServiceNames) {
-            String key = null;
-            if (endpoint != null) {
-                key = endpoint.getEndpointKey();
-            } else if (exchange.getFromEndpoint() != null) {
-                key = exchange.getFromEndpoint().getEndpointKey();
-            }
-            // exclude patterns take precedence
-            for (String pattern : excludePatterns) {
-                if (EndpointHelper.matchPattern(key, pattern)) {
-                    return null;
-                }
-            }
-            if (log.isTraceEnabled() && key != null) {
-                log.trace("Using serviceName: {} as fallback", key);
-            }
-            return key;
-        } else {
-            if (log.isTraceEnabled() && answer != null) {
-                log.trace("Using serviceName: {}", answer);
-            }
-            return answer;
-        }
-    }
-
-    private void createBraveForService(String pattern, String serviceName) {
-        Brave brave = braves.get(pattern);
-        if (brave == null && !braves.containsKey(serviceName)) {
-            Brave.Builder builder = new Brave.Builder(serviceName);
-            builder = builder.traceSampler(Sampler.create(rate));
-            if (spanCollector != null) {
-                builder = builder.spanCollector(spanCollector);
-            }
-            brave = builder.build();
-            braves.put(serviceName, brave);
-        }
-    }
-
-    private Brave getBrave(String serviceName) {
-        Brave brave = null;
-        if (serviceName != null) {
-            brave = braves.get(serviceName);
-
-            if (brave == null && useFallbackServiceNames) {
-                log.debug("Creating Brave assigned to serviceName: {}", serviceName + " as fallback");
-                Brave.Builder builder = new Brave.Builder(serviceName);
-                builder = builder.traceSampler(Sampler.create(rate));
-                if (spanCollector != null) {
-                    builder = builder.spanCollector(spanCollector);
-                }
-                brave = builder.build();
-                braves.put(serviceName, brave);
-            }
-        }
-
-        return brave;
-    }
-
-    @Override
-    public void notify(EventObject event) throws Exception {
-        // client events
-        if (event instanceof ExchangeSendingEvent) {
-            ExchangeSendingEvent ese = (ExchangeSendingEvent) event;
-            String serviceName = getServiceName(ese, ese.getExchange(), ese.getEndpoint(), false, true);
-            Brave brave = getBrave(serviceName);
-            if (brave != null) {
-                clientRequest(brave, serviceName, ese);
-            }
-        } else if (event instanceof ExchangeSentEvent) {
-            ExchangeSentEvent ese = (ExchangeSentEvent) event;
-            String serviceName = getServiceName(ese, ese.getExchange(), ese.getEndpoint(), false, true);
-            Brave brave = getBrave(serviceName);
-            if (brave != null) {
-                clientResponse(brave, serviceName, ese);
-            }
-        }
-
-        // server received client request
-        if (event instanceof ExchangeCreatedEvent) {
-            ExchangeCreatedEvent ece = (ExchangeCreatedEvent) event;
-            // we should only emit a server request if we have an existing trace id (eg a client has called us)
-            if (hasZipkinTraceId(ece.getExchange())) {
-                String serviceName = getServiceName(ece, ece.getExchange(), null, true, false);
-                Brave brave = getBrave(serviceName);
-                if (brave != null) {
-                    serverRequest(brave, serviceName, ece);
-                }
-            }
-        }
-        // server completed events
-        if (event instanceof ExchangeCompletedEvent) {
-            ExchangeCompletedEvent ece = (ExchangeCompletedEvent) event;
-            String serviceName = getServiceName(ece, ece.getExchange(), null, true, false);
-            Brave brave = getBrave(serviceName);
-            if (brave != null) {
-                serverResponse(brave, serviceName, ece);
-            }
-        } else if (event instanceof ExchangeFailedEvent) {
-            ExchangeFailedEvent efe = (ExchangeFailedEvent) event;
-            String serviceName = getServiceName(efe, efe.getExchange(), null, true, false);
-            Brave brave = getBrave(serviceName);
-            if (brave != null) {
-                serverResponse(brave, serviceName, efe);
-            }
-        }
-    }
-
-    private void clientRequest(Brave brave, String serviceName, ExchangeSendingEvent event) {
-        ClientSpanThreadBinder clientBinder = brave.clientSpanThreadBinder();
-        ServerSpanThreadBinder serverBinder = brave.serverSpanThreadBinder();
-
-        // reuse existing span if we do multiple requests from the same
-        ZipkinState state = event.getExchange().getProperty(ZipkinState.KEY, ZipkinState.class);
-        if (state == null) {
-            state = new ZipkinState();
-            event.getExchange().setProperty(ZipkinState.KEY, state);
-        }
-        // need to store the last span in use whether it was a server or client based span
-        Object last = state.getLast();
-        if (last != null && last instanceof Span) {
-            clientBinder.setCurrentSpan((Span) last);
-        } else if (last != null && last instanceof ServerSpan) {
-            serverBinder.setCurrentSpan((ServerSpan) last);
-        }
-
-        brave.clientRequestInterceptor().handle(new ZipkinClientRequestAdapter(this, serviceName, event.getExchange(), event.getEndpoint()));
-
-        // store span after request
-        Span span = clientBinder.getCurrentClientSpan();
-        state.pushClientSpan(span);
-        // and reset binder
-        clientBinder.setCurrentSpan(null);
-        serverBinder.setCurrentSpan(null);
-
-        if (log.isDebugEnabled()) {
-            String traceId = "<null>";
-            if (span != null) {
-                traceId = "" + span.getTrace_id();
-            }
-            String spanId = "<null>";
-            if (span != null) {
-                spanId = "" + span.getId();
-            }
-            String parentId = "<null>";
-            if (span != null) {
-                parentId = "" + span.getParent_id();
-            }
-            log.debug("clientRequest [service={}, traceId={}, spanId={}, parentId={}]", serviceName, traceId, spanId, parentId);
-        }
-    }
-
-    private void clientResponse(Brave brave, String serviceName, ExchangeSentEvent event) {
-        Span span = null;
-        ZipkinState state = event.getExchange().getProperty(ZipkinState.KEY, ZipkinState.class);
-        if (state != null) {
-            // only process if it was a zipkin client event
-            span = state.popClientSpan();
-        }
-
-        if (span != null) {
-            ClientSpanThreadBinder clientBinder = brave.clientSpanThreadBinder();
-            clientBinder.setCurrentSpan(span);
-            brave.clientResponseInterceptor().handle(new ZipkinClientResponseAdaptor(this, event.getExchange(), event.getEndpoint()));
-            // and reset binder
-            clientBinder.setCurrentSpan(null);
-
-            if (log.isDebugEnabled()) {
-                String traceId = "" + span.getTrace_id();
-                String spanId = "" + span.getId();
-                String parentId = "" + span.getParent_id();
-                log.debug("clientResponse[service={}, traceId={}, spanId={}, parentId={}]", serviceName, traceId, spanId, parentId);
-            }
-        }
-    }
-
-    private void serverRequest(Brave brave, String serviceName, ExchangeCreatedEvent event) {
-        ServerSpanThreadBinder serverBinder = brave.serverSpanThreadBinder();
-
-        // reuse existing span if we do multiple requests from the same
-        ZipkinState state = event.getExchange().getProperty(ZipkinState.KEY, ZipkinState.class);
-        if (state == null) {
-            state = new ZipkinState();
-            event.getExchange().setProperty(ZipkinState.KEY, state);
-        }
-        Object last = state.getLast();
-        if (last != null && last instanceof ServerSpan) {
-            serverBinder.setCurrentSpan((ServerSpan) last);
-        }
-
-        brave.serverRequestInterceptor().handle(new ZipkinServerRequestAdapter(this, event.getExchange()));
-
-        // store span after request
-        ServerSpan span = serverBinder.getCurrentServerSpan();
-        state.pushServerSpan(span);
-        // and reset binder
-        serverBinder.setCurrentSpan(null);
-
-        if (log.isDebugEnabled()) {
-            String traceId = "<null>";
-            if (span.getSpan() != null) {
-                traceId = "" + span.getSpan().getTrace_id();
-            }
-            String spanId = "<null>";
-            if (span.getSpan() != null) {
-                spanId = "" + span.getSpan().getId();
-            }
-            String parentId = "<null>";
-            if (span.getSpan() != null) {
-                parentId = "" + span.getSpan().getParent_id();
-            }
-            log.debug("serverRequest [service={}, traceId={}, spanId={}, parentId={}]", serviceName, traceId, spanId, parentId);
-        }
-    }
-
-    private void serverResponse(Brave brave, String serviceName, ExchangeCompletedEvent event) {
-        ServerSpan span = null;
-        ZipkinState state = event.getExchange().getProperty(ZipkinState.KEY, ZipkinState.class);
-        if (state != null) {
-            // only process if it was a zipkin server event
-            span = state.popServerSpan();
-        }
-
-        if (span != null) {
-            ServerSpanThreadBinder serverBinder = brave.serverSpanThreadBinder();
-            serverBinder.setCurrentSpan(span);
-            brave.serverResponseInterceptor().handle(new ZipkinServerResponseAdapter(this, event.getExchange()));
-            // and reset binder
-            serverBinder.setCurrentSpan(null);
-
-            if (log.isDebugEnabled()) {
-                String traceId = "<null>";
-                if (span.getSpan() != null) {
-                    traceId = "" + span.getSpan().getTrace_id();
-                }
-                String spanId = "<null>";
-                if (span.getSpan() != null) {
-                    spanId = "" + span.getSpan().getId();
-                }
-                String parentId = "<null>";
-                if (span.getSpan() != null) {
-                    parentId = "" + span.getSpan().getParent_id();
-                }
-                log.debug("serverResponse[service={}, traceId={}, spanId={}, parentId={}] [status=exchangeCompleted]", serviceName, traceId, spanId, parentId);
-            }
-        }
-    }
-
-    private void serverResponse(Brave brave, String serviceName, ExchangeFailedEvent event) {
-        ServerSpan span = null;
-        ZipkinState state = event.getExchange().getProperty(ZipkinState.KEY, ZipkinState.class);
-        if (state != null) {
-            // only process if it was a zipkin server event
-            span = state.popServerSpan();
-        }
-
-        if (span != null) {
-            ServerSpanThreadBinder serverBinder = brave.serverSpanThreadBinder();
-            serverBinder.setCurrentSpan(span);
-            brave.serverResponseInterceptor().handle(new ZipkinServerResponseAdapter(this, event.getExchange()));
-            // and reset binder
-            serverBinder.setCurrentSpan(null);
-
-            if (log.isDebugEnabled()) {
-                String traceId = "<null>";
-                if (span.getSpan() != null) {
-                    traceId = "" + span.getSpan().getTrace_id();
-                }
-                String spanId = "<null>";
-                if (span.getSpan() != null) {
-                    spanId = "" + span.getSpan().getId();
-                }
-                String parentId = "<null>";
-                if (span.getSpan() != null) {
-                    parentId = "" + span.getSpan().getParent_id();
-                }
-                log.debug("serverResponse[service={}, traceId={}, spanId={}, parentId={}] [status=exchangeFailed]", serviceName, traceId, spanId, parentId);
-            }
-        }
-    }
-
-    private boolean hasZipkinTraceId(Exchange exchange) {
-        // must have zipkin headers to start a server event
-        return exchange.getIn().getHeader(ZipkinConstants.TRACE_ID) != null;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinLoggingSpanCollector.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinLoggingSpanCollector.java b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinLoggingSpanCollector.java
index 608ef3a..1fccf44 100644
--- a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinLoggingSpanCollector.java
+++ b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinLoggingSpanCollector.java
@@ -41,11 +41,13 @@ public class ZipkinLoggingSpanCollector implements SpanCollector {
 
     @Override
     public void collect(Span span) {
-        if (logger.isInfoEnabled()) {
+        if (logger.isTraceEnabled()) {
+            String name = span.getName();
+            String traceId = "" + span.getTrace_id();
+            String spanId = "" + span.getId();
+            String parentId = "" + span.getParent_id();
             long ms = span.getDuration() != null ? span.getDuration() / 1000 : -1;
-            String id = IdConversion.convertToString(span.getId());
-            String line = String.format("%s(%s) - %s ms", span.getName(), id, ms);
-            logger.info(line);
+            logger.info("Zipkin[name={}, traceId={}, spanId={}, parentId={}, duration={} ms]", name, traceId, spanId, parentId, ms);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinServerRequestAdapter.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinServerRequestAdapter.java b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinServerRequestAdapter.java
index 76eadad..5d3b02f 100644
--- a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinServerRequestAdapter.java
+++ b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinServerRequestAdapter.java
@@ -34,13 +34,13 @@ import static org.apache.camel.zipkin.ZipkinHelper.createSpanId;
 
 public class ZipkinServerRequestAdapter implements ServerRequestAdapter {
 
-    private final ZipkinEventNotifier eventNotifier;
+    private final ZipkinTracer eventNotifier;
     private final Exchange exchange;
     private final Endpoint endpoint;
     private final String spanName;
     private final String url;
 
-    public ZipkinServerRequestAdapter(ZipkinEventNotifier eventNotifier, Exchange exchange) {
+    public ZipkinServerRequestAdapter(ZipkinTracer eventNotifier, Exchange exchange) {
         this.eventNotifier = eventNotifier;
         this.exchange = exchange;
         this.endpoint = exchange.getFromEndpoint();

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinServerResponseAdapter.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinServerResponseAdapter.java b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinServerResponseAdapter.java
index 7fa33d9..83d0e42 100644
--- a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinServerResponseAdapter.java
+++ b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinServerResponseAdapter.java
@@ -29,12 +29,12 @@ import org.apache.camel.util.URISupport;
 
 public class ZipkinServerResponseAdapter implements ServerResponseAdapter {
 
-    private final ZipkinEventNotifier eventNotifier;
+    private final ZipkinTracer eventNotifier;
     private final Exchange exchange;
     private final Endpoint endpoint;
     private final String url;
 
-    public ZipkinServerResponseAdapter(ZipkinEventNotifier eventNotifier, Exchange exchange) {
+    public ZipkinServerResponseAdapter(ZipkinTracer eventNotifier, Exchange exchange) {
         this.eventNotifier = eventNotifier;
         this.exchange = exchange;
         this.endpoint = exchange.getFromEndpoint();

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinState.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinState.java b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinState.java
index 0c9ed83..a679501 100644
--- a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinState.java
+++ b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinState.java
@@ -35,18 +35,14 @@ public final class ZipkinState {
 
     private final Stack<Span> clientSpans = new Stack<>();
     private final Stack<ServerSpan> serverSpans = new Stack<>();
-    private Object last;
 
     public void pushClientSpan(Span span) {
         clientSpans.push(span);
-        last = span;
     }
 
     public Span popClientSpan() {
         if (!clientSpans.empty()) {
-            Span answer = clientSpans.pop();
-            last = answer;
-            return answer;
+            return clientSpans.pop();
         } else {
             return null;
         }
@@ -54,20 +50,22 @@ public final class ZipkinState {
 
     public void pushServerSpan(ServerSpan span) {
         serverSpans.push(span);
-        last = span;
     }
 
     public ServerSpan popServerSpan() {
         if (!serverSpans.empty()) {
-            ServerSpan answer = serverSpans.pop();
-            last = answer;
-            return answer;
+            return serverSpans.pop();
         } else {
             return null;
         }
     }
 
-    public Object getLast() {
-        return last;
+    public ServerSpan peekServerSpan() {
+        if (!serverSpans.empty()) {
+            return serverSpans.peek();
+        } else {
+            return null;
+        }
     }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinTracer.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinTracer.java b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinTracer.java
new file mode 100644
index 0000000..7df3d7d
--- /dev/null
+++ b/components/camel-zipkin/src/main/java/org/apache/camel/zipkin/ZipkinTracer.java
@@ -0,0 +1,660 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.camel.zipkin;
+
+import java.io.Closeable;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.github.kristofa.brave.Brave;
+import com.github.kristofa.brave.ClientSpanThreadBinder;
+import com.github.kristofa.brave.Sampler;
+import com.github.kristofa.brave.ServerSpan;
+import com.github.kristofa.brave.ServerSpanThreadBinder;
+import com.github.kristofa.brave.SpanCollector;
+import com.github.kristofa.brave.scribe.ScribeSpanCollector;
+import com.twitter.zipkin.gen.Span;
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.Route;
+import org.apache.camel.StatefulService;
+import org.apache.camel.api.management.ManagedAttribute;
+import org.apache.camel.api.management.ManagedResource;
+import org.apache.camel.component.properties.ServiceHostPropertiesFunction;
+import org.apache.camel.component.properties.ServicePortPropertiesFunction;
+import org.apache.camel.management.event.ExchangeCompletedEvent;
+import org.apache.camel.management.event.ExchangeCreatedEvent;
+import org.apache.camel.management.event.ExchangeFailedEvent;
+import org.apache.camel.management.event.ExchangeSendingEvent;
+import org.apache.camel.management.event.ExchangeSentEvent;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.spi.RoutePolicy;
+import org.apache.camel.spi.RoutePolicyFactory;
+import org.apache.camel.support.EventNotifierSupport;
+import org.apache.camel.util.EndpointHelper;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.ServiceHelper;
+
+import static org.apache.camel.builder.ExpressionBuilder.routeIdExpression;
+
+/**
+ * To use zipkin with Camel then setup this {@link org.apache.camel.spi.EventNotifier} in your Camel application.
+ * <p/>
+ * Events (span) are captured for incoming and outgoing messages being sent to/from Camel.
+ * This means you need to configure which which Camel endpoints that maps to zipkin service names.
+ * The mapping can be configured using
+ * <ul>
+ * <li>route id - A Camel route id</li>
+ * <li>endpoint url - A Camel endpoint url</li>
+ * </ul>
+ * For both kinds you can use wildcards and regular expressions to match, which is using the rules from
+ * {@link EndpointHelper#matchPattern(String, String)} and {@link EndpointHelper#matchEndpoint(CamelContext, String, String)}
+ * <p/>
+ * To match all Camel messages you can use <tt>*</tt> in the pattern and configure that to the same service name.
+ * <br/>
+ * If no mapping has been configured then Camel will fallback and use endpoint uri's as service names.
+ * However its recommended to configure service mappings so you can use human logic names instead of Camel
+ * endpoint uris in the names.
+ * <p/>
+ * Camel will auto-configure a {@link ScribeSpanCollector} if no SpanCollector explicit has been configured, and
+ * if the hostname and port to the span collector has been configured as environment variables
+ * <ul>
+ *     <li>ZIPKIN_COLLECTOR_SERVICE_HOST - The hostname</li>
+ *     <li>ZIPKIN_COLLECTOR_SERVICE_PORT - The port number</li>
+ * </ul>
+ */
+@ManagedResource(description = "Managing ZipkinTracer")
+public class ZipkinTracer extends EventNotifierSupport implements RoutePolicy, RoutePolicyFactory, StatefulService, CamelContextAware {
+
+    private final Map<String, Brave> braves = new HashMap<>();
+    private transient boolean useFallbackServiceNames;
+
+    private CamelContext camelContext;
+    private String hostName;
+    private int port;
+    private float rate = 1.0f;
+    private SpanCollector spanCollector;
+    private Map<String, String> clientServiceMappings = new HashMap<>();
+    private Map<String, String> serverServiceMappings = new HashMap<>();
+    private Set<String> excludePatterns = new HashSet<>();
+    private boolean includeMessageBody;
+
+    public ZipkinTracer() {
+    }
+
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    public String getHostName() {
+        return hostName;
+    }
+
+    /**
+     * Sets a hostname for the remote zipkin server to use.
+     */
+    public void setHostName(String hostName) {
+        this.hostName = hostName;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    /**
+     * Sets the port number for the remote zipkin server to use.
+     */
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public float getRate() {
+        return rate;
+    }
+
+    /**
+     * Configures a rate that decides how many events should be traced by zipkin.
+     * The rate is expressed as a percentage (1.0f = 100%, 0.5f is 50%, 0.1f is 10%).
+     *
+     * @param rate minimum sample rate is 0.0001, or 0.01% of traces
+     */
+    public void setRate(float rate) {
+        this.rate = rate;
+    }
+
+    public SpanCollector getSpanCollector() {
+        return spanCollector;
+    }
+
+    /**
+     * The collector to use for sending zipkin span events to the zipkin server.
+     */
+    public void setSpanCollector(SpanCollector spanCollector) {
+        this.spanCollector = spanCollector;
+    }
+
+    public String getServiceName() {
+        return clientServiceMappings.get("*");
+    }
+
+    /**
+     * To use a global service name that matches all Camel events
+     */
+    public void setServiceName(String serviceName) {
+        clientServiceMappings.put("*", serviceName);
+        serverServiceMappings.put("*", serviceName);
+    }
+
+    public Map<String, String> getClientServiceMappings() {
+        return clientServiceMappings;
+    }
+
+    public void setClientServiceMappings(Map<String, String> clientServiceMappings) {
+        this.clientServiceMappings = clientServiceMappings;
+    }
+
+    /**
+     * Adds a client service mapping that matches Camel events to the given zipkin service name.
+     * See more details at the class javadoc.
+     *
+     * @param pattern  the pattern such as route id, endpoint url
+     * @param serviceName the zipkin service name
+     */
+    public void addClientServiceMapping(String pattern, String serviceName) {
+        clientServiceMappings.put(pattern, serviceName);
+    }
+
+    public Map<String, String> getServerServiceMappings() {
+        return serverServiceMappings;
+    }
+
+    public void setServerServiceMappings(Map<String, String> serverServiceMappings) {
+        this.serverServiceMappings = serverServiceMappings;
+    }
+
+    /**
+     * Adds a server service mapping that matches Camel events to the given zipkin service name.
+     * See more details at the class javadoc.
+     *
+     * @param pattern  the pattern such as route id, endpoint url
+     * @param serviceName the zipkin service name
+     */
+    public void addServerServiceMapping(String pattern, String serviceName) {
+        serverServiceMappings.put(pattern, serviceName);
+    }
+
+    public Set<String> getExcludePatterns() {
+        return excludePatterns;
+    }
+
+    public void setExcludePatterns(Set<String> excludePatterns) {
+        this.excludePatterns = excludePatterns;
+    }
+
+    /**
+     * Adds an exclude pattern that will disable tracing with zipkin for Camel messages that matches the pattern.
+     *
+     * @param pattern  the pattern such as route id, endpoint url
+     */
+    public void addExcludePattern(String pattern) {
+        excludePatterns.add(pattern);
+    }
+
+    @ManagedAttribute(description = "Whether to include the Camel message body in the zipkin traces")
+    public boolean isIncludeMessageBody() {
+        return includeMessageBody;
+    }
+
+    /**
+     * Whether to include the Camel message body in the zipkin traces.
+     * <p/>
+     * This is not recommended for production usage, or when having big payloads. You can limit the size by
+     * configuring the <a href="http://camel.apache.org/how-do-i-set-the-max-chars-when-debug-logging-messages-in-camel.html">max debug log size</a>.
+     */
+    @ManagedAttribute(description = "Whether to include the Camel message body in the zipkin traces")
+    public void setIncludeMessageBody(boolean includeMessageBody) {
+        this.includeMessageBody = includeMessageBody;
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+
+        ObjectHelper.notNull(camelContext, "CamelContext", this);
+
+        if (spanCollector == null) {
+            if (hostName != null && port > 0) {
+                log.info("Configuring Zipkin ScribeSpanCollector using host: {} and port: {}", hostName, port);
+                spanCollector = new ScribeSpanCollector(hostName, port);
+            } else {
+                // is there a zipkin service setup as ENV variable to auto register a scribe span collector
+                String host = new ServiceHostPropertiesFunction().apply("zipkin-collector");
+                String port = new ServicePortPropertiesFunction().apply("zipkin-collector");
+                if (ObjectHelper.isNotEmpty(host) && ObjectHelper.isNotEmpty(port)) {
+                    log.info("Auto-configuring Zipkin ScribeSpanCollector using host: {} and port: {}", host, port);
+                    int num = camelContext.getTypeConverter().mandatoryConvertTo(Integer.class, port);
+                    spanCollector = new ScribeSpanCollector(host, num);
+                }
+            }
+        }
+
+        ObjectHelper.notNull(spanCollector, "SpanCollector", this);
+
+        if (clientServiceMappings.isEmpty()) {
+            log.warn("No service name(s) has been configured. Camel will fallback and use endpoint uris as service names.");
+            useFallbackServiceNames = true;
+        }
+
+        // create braves mapped per service name
+        for (Map.Entry<String, String> entry : clientServiceMappings.entrySet()) {
+            String pattern = entry.getKey();
+            String serviceName = entry.getValue();
+            createBraveForService(pattern, serviceName);
+        }
+        for (Map.Entry<String, String> entry : serverServiceMappings.entrySet()) {
+            String pattern = entry.getKey();
+            String serviceName = entry.getValue();
+            createBraveForService(pattern, serviceName);
+        }
+
+        ServiceHelper.startService(spanCollector);
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        super.doStop();
+
+        // stop and close collector
+        ServiceHelper.stopAndShutdownService(spanCollector);
+        if (spanCollector instanceof Closeable) {
+            IOHelper.close((Closeable) spanCollector);
+        }
+
+        braves.clear();
+
+        camelContext.getManagementStrategy().removeEventNotifier(this);
+        camelContext.getRoutePolicyFactories().remove(this);
+    }
+
+    @Override
+    public boolean isEnabled(EventObject event) {
+        return event instanceof ExchangeSendingEvent
+                || event instanceof ExchangeSentEvent
+                || event instanceof ExchangeCreatedEvent
+                || event instanceof ExchangeCompletedEvent
+                || event instanceof ExchangeFailedEvent;
+    }
+
+    private String getServiceName(Exchange exchange, Endpoint endpoint, boolean server, boolean client) {
+        if (client) {
+            return getServiceName(exchange, endpoint, clientServiceMappings);
+        } else if (server) {
+            return getServiceName(exchange, endpoint, serverServiceMappings);
+        } else {
+            return null;
+        }
+    }
+
+    private String getServiceName(Exchange exchange, Endpoint endpoint, Map<String, String> serviceMappings) {
+        String answer = null;
+
+        // endpoint takes precedence over route
+        if (endpoint != null) {
+            String url = endpoint.getEndpointUri();
+            if (url != null) {
+                // exclude patterns take precedence
+                for (String pattern : excludePatterns) {
+                    if (EndpointHelper.matchEndpoint(exchange.getContext(), url, pattern)) {
+                        return null;
+                    }
+                }
+                for (Map.Entry<String, String> entry : serviceMappings.entrySet()) {
+                    String pattern = entry.getKey();
+                    if (EndpointHelper.matchEndpoint(exchange.getContext(), url, pattern)) {
+                        answer = entry.getValue();
+                        break;
+                    }
+                }
+            }
+        }
+
+        // route
+        if (answer == null) {
+            String id = routeIdExpression().evaluate(exchange, String.class);
+            if (id != null) {
+                // exclude patterns take precedence
+                for (String pattern : excludePatterns) {
+                    if (EndpointHelper.matchPattern(id, pattern)) {
+                        return null;
+                    }
+                }
+                for (Map.Entry<String, String> entry : serviceMappings.entrySet()) {
+                    String pattern = entry.getKey();
+                    if (EndpointHelper.matchPattern(id, pattern)) {
+                        answer = entry.getValue();
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (answer == null) {
+            String id = exchange.getFromRouteId();
+            if (id != null) {
+                // exclude patterns take precedence
+                for (String pattern : excludePatterns) {
+                    if (EndpointHelper.matchPattern(id, pattern)) {
+                        return null;
+                    }
+                }
+                for (Map.Entry<String, String> entry : serviceMappings.entrySet()) {
+                    String pattern = entry.getKey();
+                    if (EndpointHelper.matchPattern(id, pattern)) {
+                        answer = entry.getValue();
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (answer == null && useFallbackServiceNames) {
+            String key = null;
+            if (endpoint != null) {
+                key = endpoint.getEndpointKey();
+            } else if (exchange.getFromEndpoint() != null) {
+                key = exchange.getFromEndpoint().getEndpointKey();
+            }
+            // exclude patterns take precedence
+            for (String pattern : excludePatterns) {
+                if (EndpointHelper.matchPattern(key, pattern)) {
+                    return null;
+                }
+            }
+            if (log.isTraceEnabled() && key != null) {
+                log.trace("Using serviceName: {} as fallback", key);
+            }
+            return key;
+        } else {
+            if (log.isTraceEnabled() && answer != null) {
+                log.trace("Using serviceName: {}", answer);
+            }
+            return answer;
+        }
+    }
+
+    private void createBraveForService(String pattern, String serviceName) {
+        Brave brave = braves.get(pattern);
+        if (brave == null && !braves.containsKey(serviceName)) {
+            Brave.Builder builder = new Brave.Builder(serviceName);
+            builder = builder.traceSampler(Sampler.create(rate));
+            if (spanCollector != null) {
+                builder = builder.spanCollector(spanCollector);
+            }
+            brave = builder.build();
+            braves.put(serviceName, brave);
+        }
+    }
+
+    private Brave getBrave(String serviceName) {
+        Brave brave = null;
+        if (serviceName != null) {
+            brave = braves.get(serviceName);
+
+            if (brave == null && useFallbackServiceNames) {
+                log.debug("Creating Brave assigned to serviceName: {}", serviceName + " as fallback");
+                Brave.Builder builder = new Brave.Builder(serviceName);
+                builder = builder.traceSampler(Sampler.create(rate));
+                if (spanCollector != null) {
+                    builder = builder.spanCollector(spanCollector);
+                }
+                brave = builder.build();
+                braves.put(serviceName, brave);
+            }
+        }
+
+        return brave;
+    }
+
+    @Override
+    public void notify(EventObject event) throws Exception {
+        // client events
+        if (event instanceof ExchangeSendingEvent) {
+            ExchangeSendingEvent ese = (ExchangeSendingEvent) event;
+            String serviceName = getServiceName(ese.getExchange(), ese.getEndpoint(), false, true);
+            Brave brave = getBrave(serviceName);
+            if (brave != null) {
+                clientRequest(brave, serviceName, ese);
+            }
+        } else if (event instanceof ExchangeSentEvent) {
+            ExchangeSentEvent ese = (ExchangeSentEvent) event;
+            String serviceName = getServiceName(ese.getExchange(), ese.getEndpoint(), false, true);
+            Brave brave = getBrave(serviceName);
+            if (brave != null) {
+                clientResponse(brave, serviceName, ese);
+            }
+        }
+    }
+
+    private void clientRequest(Brave brave, String serviceName, ExchangeSendingEvent event) {
+        ClientSpanThreadBinder clientBinder = brave.clientSpanThreadBinder();
+        ServerSpanThreadBinder serverBinder = brave.serverSpanThreadBinder();
+
+        // reuse existing span if we do multiple requests from the same
+        ZipkinState state = event.getExchange().getProperty(ZipkinState.KEY, ZipkinState.class);
+        if (state == null) {
+            state = new ZipkinState();
+            event.getExchange().setProperty(ZipkinState.KEY, state);
+        }
+        // if we started from a server span then lets reuse that when we call a downstream service
+        ServerSpan last = state.peekServerSpan();
+        if (last != null) {
+            serverBinder.setCurrentSpan(last);
+        }
+
+        brave.clientRequestInterceptor().handle(new ZipkinClientRequestAdapter(this, serviceName, event.getExchange(), event.getEndpoint()));
+
+        // store span after request
+        Span span = clientBinder.getCurrentClientSpan();
+        state.pushClientSpan(span);
+        // and reset binder
+        clientBinder.setCurrentSpan(null);
+        serverBinder.setCurrentSpan(null);
+
+        if (log.isDebugEnabled()) {
+            String traceId = "<null>";
+            if (span != null) {
+                traceId = "" + span.getTrace_id();
+            }
+            String spanId = "<null>";
+            if (span != null) {
+                spanId = "" + span.getId();
+            }
+            String parentId = "<null>";
+            if (span != null) {
+                parentId = "" + span.getParent_id();
+            }
+            log.debug("clientRequest [service={}, traceId={}, spanId={}, parentId={}]", serviceName, traceId, spanId, parentId);
+        }
+    }
+
+    private void clientResponse(Brave brave, String serviceName, ExchangeSentEvent event) {
+        Span span = null;
+        ZipkinState state = event.getExchange().getProperty(ZipkinState.KEY, ZipkinState.class);
+        if (state != null) {
+            // only process if it was a zipkin client event
+            span = state.popClientSpan();
+        }
+
+        if (span != null) {
+            ClientSpanThreadBinder clientBinder = brave.clientSpanThreadBinder();
+            clientBinder.setCurrentSpan(span);
+            brave.clientResponseInterceptor().handle(new ZipkinClientResponseAdaptor(this, event.getExchange(), event.getEndpoint()));
+            // and reset binder
+            clientBinder.setCurrentSpan(null);
+
+            if (log.isDebugEnabled()) {
+                String traceId = "" + span.getTrace_id();
+                String spanId = "" + span.getId();
+                String parentId = "" + span.getParent_id();
+                log.debug("clientResponse[service={}, traceId={}, spanId={}, parentId={}]", serviceName, traceId, spanId, parentId);
+            }
+        }
+    }
+
+    private void serverRequest(Brave brave, String serviceName, Exchange exchange) {
+        ServerSpanThreadBinder serverBinder = brave.serverSpanThreadBinder();
+
+        // reuse existing span if we do multiple requests from the same
+        ZipkinState state = exchange.getProperty(ZipkinState.KEY, ZipkinState.class);
+        if (state == null) {
+            state = new ZipkinState();
+            exchange.setProperty(ZipkinState.KEY, state);
+        }
+        // if we started from a another server span then lets reuse that
+        ServerSpan last = state.peekServerSpan();
+        if (last != null) {
+            serverBinder.setCurrentSpan(last);
+        }
+
+        brave.serverRequestInterceptor().handle(new ZipkinServerRequestAdapter(this, exchange));
+
+        // store span after request
+        ServerSpan span = serverBinder.getCurrentServerSpan();
+        state.pushServerSpan(span);
+        // and reset binder
+        serverBinder.setCurrentSpan(null);
+
+        if (log.isDebugEnabled()) {
+            String traceId = "<null>";
+            if (span.getSpan() != null) {
+                traceId = "" + span.getSpan().getTrace_id();
+            }
+            String spanId = "<null>";
+            if (span.getSpan() != null) {
+                spanId = "" + span.getSpan().getId();
+            }
+            String parentId = "<null>";
+            if (span.getSpan() != null) {
+                parentId = "" + span.getSpan().getParent_id();
+            }
+            log.debug("serverRequest [service={}, traceId={}, spanId={}, parentId={}]", serviceName, traceId, spanId, parentId);
+        }
+    }
+
+    private void serverResponse(Brave brave, String serviceName, Exchange exchange) {
+        ServerSpan span = null;
+        ZipkinState state = exchange.getProperty(ZipkinState.KEY, ZipkinState.class);
+        if (state != null) {
+            // only process if it was a zipkin server event
+            span = state.popServerSpan();
+        }
+
+        if (span != null) {
+            ServerSpanThreadBinder serverBinder = brave.serverSpanThreadBinder();
+            serverBinder.setCurrentSpan(span);
+            brave.serverResponseInterceptor().handle(new ZipkinServerResponseAdapter(this, exchange));
+            // and reset binder
+            serverBinder.setCurrentSpan(null);
+
+            if (log.isDebugEnabled()) {
+                String traceId = "<null>";
+                if (span.getSpan() != null) {
+                    traceId = "" + span.getSpan().getTrace_id();
+                }
+                String spanId = "<null>";
+                if (span.getSpan() != null) {
+                    spanId = "" + span.getSpan().getId();
+                }
+                String parentId = "<null>";
+                if (span.getSpan() != null) {
+                    parentId = "" + span.getSpan().getParent_id();
+                }
+                log.debug("serverResponse[service={}, traceId={}, spanId={}, parentId={}]", serviceName, traceId, spanId, parentId);
+            }
+        }
+    }
+
+    private boolean hasZipkinTraceId(Exchange exchange) {
+        // must have zipkin headers to start a server event
+        return exchange.getIn().getHeader(ZipkinConstants.TRACE_ID) != null;
+    }
+
+    @Override
+    public void onInit(Route route) {
+        // noop
+    }
+
+    @Override
+    public void onRemove(Route route) {
+        // noop
+    }
+
+    @Override
+    public void onStart(Route route) {
+        // noop
+    }
+
+    @Override
+    public void onStop(Route route) {
+        // noop
+    }
+
+    @Override
+    public void onSuspend(Route route) {
+        // noop
+    }
+
+    @Override
+    public void onResume(Route route) {
+        // noop
+    }
+
+    @Override
+    public void onExchangeBegin(Route route, Exchange exchange) {
+        if (hasZipkinTraceId(exchange)) {
+            String serviceName = getServiceName(exchange, route.getEndpoint(), true, false);
+            Brave brave = getBrave(serviceName);
+            if (brave != null) {
+                serverRequest(brave, serviceName, exchange);
+            }
+        }
+    }
+
+    @Override
+    public void onExchangeDone(Route route, Exchange exchange) {
+        String serviceName = getServiceName(exchange, route.getEndpoint(), true, false);
+        Brave brave = getBrave(serviceName);
+        if (brave != null) {
+            serverResponse(brave, serviceName, exchange);
+        }
+    }
+
+    @Override
+    public RoutePolicy createRoutePolicy(CamelContext camelContext, String routeId, RouteDefinition route) {
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinABCRouteTest.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinABCRouteTest.java b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinABCRouteTest.java
new file mode 100644
index 0000000..dacde09
--- /dev/null
+++ b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinABCRouteTest.java
@@ -0,0 +1,86 @@
+/**
+ * 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.camel.zipkin;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+public class ZipkinABCRouteTest extends CamelTestSupport {
+
+    private ZipkinTracer zipkin;
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+
+        zipkin = new ZipkinTracer();
+
+        zipkin.addClientServiceMapping("seda:a", "a");
+        zipkin.addClientServiceMapping("seda:b", "b");
+        zipkin.addClientServiceMapping("seda:c", "c");
+        zipkin.addServerServiceMapping("seda:a", "a");
+        zipkin.addServerServiceMapping("seda:b", "b");
+        zipkin.addServerServiceMapping("seda:c", "c");
+        zipkin.setSpanCollector(new ZipkinLoggingSpanCollector());
+
+        // attaching ourself to CamelContext
+        context.getManagementStrategy().addEventNotifier(zipkin);
+        context.addRoutePolicyFactory(zipkin);
+
+        return context;
+    }
+
+    @Test
+    public void testZipkinRoute() throws Exception {
+        NotifyBuilder notify = new NotifyBuilder(context).whenDone(1).create();
+
+        template.requestBody("direct:start", "Hello World");
+
+        assertTrue(notify.matches(30, TimeUnit.SECONDS));
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").to("seda:a").routeId("start");
+
+                from("seda:a").routeId("a")
+                    .log("routing at ${routeId}")
+                    .to("seda:b")
+                    .delay(2000)
+                    .to("seda:c")
+                    .log("End of routing");
+
+                from("seda:b").routeId("b")
+                        .log("routing at ${routeId}")
+                        .delay(simple("${random(1000,2000)}"));
+
+                from("seda:c").routeId("c")
+                        .log("routing at ${routeId}")
+                        .delay(simple("${random(0,100)}"));
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinRouteConcurrentTest.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinRouteConcurrentTest.java b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinRouteConcurrentTest.java
index 987e188..694f922 100644
--- a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinRouteConcurrentTest.java
+++ b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinRouteConcurrentTest.java
@@ -27,13 +27,13 @@ import org.junit.Test;
 
 public class ZipkinRouteConcurrentTest extends CamelTestSupport {
 
-    private ZipkinEventNotifier zipkin;
+    private ZipkinTracer zipkin;
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
         CamelContext context = super.createCamelContext();
 
-        zipkin = new ZipkinEventNotifier();
+        zipkin = new ZipkinTracer();
         zipkin.addClientServiceMapping("seda:foo", "foo");
         zipkin.addServerServiceMapping("seda:bar", "bar");
         zipkin.setSpanCollector(new ZipkinLoggingSpanCollector());

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinSimpleFallbackRouteTest.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinSimpleFallbackRouteTest.java b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinSimpleFallbackRouteTest.java
index 5fa940a..025f59d 100644
--- a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinSimpleFallbackRouteTest.java
+++ b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinSimpleFallbackRouteTest.java
@@ -27,13 +27,13 @@ import org.junit.Test;
 
 public class ZipkinSimpleFallbackRouteTest extends CamelTestSupport {
 
-    private ZipkinEventNotifier zipkin;
+    private ZipkinTracer zipkin;
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
         CamelContext context = super.createCamelContext();
 
-        zipkin = new ZipkinEventNotifier();
+        zipkin = new ZipkinTracer();
         // no service so should use fallback naming style
         // we do not want to trace any direct endpoints
         zipkin.addExcludePattern("direct:*");

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinSimpleRouteTest.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinSimpleRouteTest.java b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinSimpleRouteTest.java
index 8edabe6..a3840d8 100644
--- a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinSimpleRouteTest.java
+++ b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinSimpleRouteTest.java
@@ -27,16 +27,18 @@ import org.junit.Test;
 
 public class ZipkinSimpleRouteTest extends CamelTestSupport {
 
-    private ZipkinEventNotifier zipkin;
+    private ZipkinTracer zipkin;
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
         CamelContext context = super.createCamelContext();
 
-        zipkin = new ZipkinEventNotifier();
+        zipkin = new ZipkinTracer();
         zipkin.setServiceName("dude");
         zipkin.setSpanCollector(new ZipkinLoggingSpanCollector());
-        context.getManagementStrategy().addEventNotifier(zipkin);
+
+        context.addService(zipkin);
+//        context.getManagementStrategy().addEventNotifier(zipkin);
 
         return context;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinTwoRouteTest.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinTwoRouteTest.java b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinTwoRouteTest.java
index 5819562..124ab91 100644
--- a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinTwoRouteTest.java
+++ b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/ZipkinTwoRouteTest.java
@@ -24,13 +24,13 @@ import org.junit.Test;
 
 public class ZipkinTwoRouteTest extends CamelTestSupport {
 
-    private ZipkinEventNotifier zipkin;
+    private ZipkinTracer zipkin;
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
         CamelContext context = super.createCamelContext();
 
-        zipkin = new ZipkinEventNotifier();
+        zipkin = new ZipkinTracer();
         // we have 2 routes as services
         zipkin.addClientServiceMapping("seda:cat", "cat");
         zipkin.addServerServiceMapping("seda:cat", "cat");

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinABCRouteScribe.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinABCRouteScribe.java b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinABCRouteScribe.java
new file mode 100644
index 0000000..80be8a2
--- /dev/null
+++ b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinABCRouteScribe.java
@@ -0,0 +1,90 @@
+/**
+ * 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.camel.zipkin.scribe;
+
+import java.util.concurrent.TimeUnit;
+
+import com.github.kristofa.brave.scribe.ScribeSpanCollector;
+import org.apache.camel.CamelContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.apache.camel.zipkin.ZipkinLoggingSpanCollector;
+import org.apache.camel.zipkin.ZipkinTracer;
+import org.junit.Test;
+
+public class ZipkinABCRouteScribe extends CamelTestSupport {
+
+    private String ip = "192.168.99.100";
+    private ZipkinTracer zipkin;
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+
+        zipkin = new ZipkinTracer();
+
+        zipkin.addClientServiceMapping("seda:a", "a");
+        zipkin.addClientServiceMapping("seda:b", "b");
+        zipkin.addClientServiceMapping("seda:c", "c");
+        zipkin.addServerServiceMapping("seda:a", "a");
+        zipkin.addServerServiceMapping("seda:b", "b");
+        zipkin.addServerServiceMapping("seda:c", "c");
+        zipkin.setSpanCollector(new ScribeSpanCollector(ip, 9410));
+
+        // attaching ourself to CamelContext
+        context.getManagementStrategy().addEventNotifier(zipkin);
+        context.addRoutePolicyFactory(zipkin);
+
+        return context;
+    }
+
+    @Test
+    public void testZipkinRoute() throws Exception {
+        NotifyBuilder notify = new NotifyBuilder(context).whenDone(1).create();
+
+        template.requestBody("direct:start", "Hello World");
+
+        assertTrue(notify.matches(30, TimeUnit.SECONDS));
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").to("seda:a").routeId("start");
+
+                from("seda:a").routeId("a")
+                    .log("routing at ${routeId}")
+                    .to("seda:b")
+                    .delay(2000)
+                    .to("seda:c")
+                    .log("End of routing");
+
+                from("seda:b").routeId("b")
+                        .log("routing at ${routeId}")
+                        .delay(simple("${random(1000,2000)}"));
+
+                from("seda:c").routeId("c")
+                        .log("routing at ${routeId}")
+                        .delay(simple("${random(0,100)}"));
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinAutoConfigureScribe.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinAutoConfigureScribe.java b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinAutoConfigureScribe.java
index a569af7..16885d6 100644
--- a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinAutoConfigureScribe.java
+++ b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinAutoConfigureScribe.java
@@ -20,7 +20,7 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.test.junit4.CamelTestSupport;
-import org.apache.camel.zipkin.ZipkinEventNotifier;
+import org.apache.camel.zipkin.ZipkinTracer;
 import org.junit.Test;
 
 /**
@@ -33,13 +33,13 @@ import org.junit.Test;
  */
 public class ZipkinAutoConfigureScribe extends CamelTestSupport {
 
-    private ZipkinEventNotifier zipkin;
+    private ZipkinTracer zipkin;
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
         CamelContext context = super.createCamelContext();
 
-        zipkin = new ZipkinEventNotifier();
+        zipkin = new ZipkinTracer();
         // we have one route as service
         zipkin.addClientServiceMapping("seda:cat", "cat");
         zipkin.addServerServiceMapping("seda:cat", "cat");

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinOneRouteFallbackScribe.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinOneRouteFallbackScribe.java b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinOneRouteFallbackScribe.java
index 8f3ed4e..3b34ae8 100644
--- a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinOneRouteFallbackScribe.java
+++ b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinOneRouteFallbackScribe.java
@@ -21,7 +21,7 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.test.junit4.CamelTestSupport;
-import org.apache.camel.zipkin.ZipkinEventNotifier;
+import org.apache.camel.zipkin.ZipkinTracer;
 import org.junit.Test;
 
 /**
@@ -35,13 +35,13 @@ import org.junit.Test;
 public class ZipkinOneRouteFallbackScribe extends CamelTestSupport {
 
     private String ip = "192.168.99.100";
-    private ZipkinEventNotifier zipkin;
+    private ZipkinTracer zipkin;
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
         CamelContext context = super.createCamelContext();
 
-        zipkin = new ZipkinEventNotifier();
+        zipkin = new ZipkinTracer();
         // no service so should use fallback naming style
         // we do not want to trace any direct endpoints
         zipkin.addExcludePattern("direct:*");

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinOneRouteScribe.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinOneRouteScribe.java b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinOneRouteScribe.java
index 2f9fe37..772b332 100644
--- a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinOneRouteScribe.java
+++ b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinOneRouteScribe.java
@@ -21,7 +21,7 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.test.junit4.CamelTestSupport;
-import org.apache.camel.zipkin.ZipkinEventNotifier;
+import org.apache.camel.zipkin.ZipkinTracer;
 import org.junit.Test;
 
 /**
@@ -35,13 +35,13 @@ import org.junit.Test;
 public class ZipkinOneRouteScribe extends CamelTestSupport {
 
     private String ip = "192.168.99.100";
-    private ZipkinEventNotifier zipkin;
+    private ZipkinTracer zipkin;
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
         CamelContext context = super.createCamelContext();
 
-        zipkin = new ZipkinEventNotifier();
+        zipkin = new ZipkinTracer();
         // we have one route as service
         zipkin.addClientServiceMapping("seda:cat", "cat");
         zipkin.addServerServiceMapping("seda:cat", "cat");

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinSimpleRouteScribe.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinSimpleRouteScribe.java b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinSimpleRouteScribe.java
index e3d176d..e1289ee 100644
--- a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinSimpleRouteScribe.java
+++ b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinSimpleRouteScribe.java
@@ -21,7 +21,7 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.test.junit4.CamelTestSupport;
-import org.apache.camel.zipkin.ZipkinEventNotifier;
+import org.apache.camel.zipkin.ZipkinTracer;
 import org.junit.Test;
 
 /**
@@ -35,13 +35,13 @@ import org.junit.Test;
 public class ZipkinSimpleRouteScribe extends CamelTestSupport {
 
     private String ip = "192.168.99.100";
-    private ZipkinEventNotifier zipkin;
+    private ZipkinTracer zipkin;
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
         CamelContext context = super.createCamelContext();
 
-        zipkin = new ZipkinEventNotifier();
+        zipkin = new ZipkinTracer();
         // we have one route as service
         zipkin.addClientServiceMapping("seda:dude", "dude");
         zipkin.addServerServiceMapping("seda:dude", "dude");

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinTimerRouteScribe.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinTimerRouteScribe.java b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinTimerRouteScribe.java
index 7e0ba4b..df4d607 100644
--- a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinTimerRouteScribe.java
+++ b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinTimerRouteScribe.java
@@ -25,7 +25,7 @@ import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.NotifyBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.test.junit4.CamelTestSupport;
-import org.apache.camel.zipkin.ZipkinEventNotifier;
+import org.apache.camel.zipkin.ZipkinTracer;
 import org.junit.Test;
 
 /**
@@ -39,13 +39,13 @@ import org.junit.Test;
 public class ZipkinTimerRouteScribe extends CamelTestSupport {
 
     private String ip = "192.168.99.100";
-    private ZipkinEventNotifier zipkin;
+    private ZipkinTracer zipkin;
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
         CamelContext context = super.createCamelContext();
 
-        zipkin = new ZipkinEventNotifier();
+        zipkin = new ZipkinTracer();
         // we have one route as service
         zipkin.addClientServiceMapping("seda:timer", "timer");
         zipkin.addServerServiceMapping("seda:timer", "timer");

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinTwoRouteScribe.java
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinTwoRouteScribe.java b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinTwoRouteScribe.java
index b5e4c6d..4dbe3a9 100644
--- a/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinTwoRouteScribe.java
+++ b/components/camel-zipkin/src/test/java/org/apache/camel/zipkin/scribe/ZipkinTwoRouteScribe.java
@@ -21,7 +21,7 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.test.junit4.CamelTestSupport;
-import org.apache.camel.zipkin.ZipkinEventNotifier;
+import org.apache.camel.zipkin.ZipkinTracer;
 import org.junit.Test;
 
 /**
@@ -35,13 +35,13 @@ import org.junit.Test;
 public class ZipkinTwoRouteScribe extends CamelTestSupport {
 
     private String ip = "192.168.99.100";
-    private ZipkinEventNotifier zipkin;
+    private ZipkinTracer zipkin;
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
         CamelContext context = super.createCamelContext();
 
-        zipkin = new ZipkinEventNotifier();
+        zipkin = new ZipkinTracer();
         // we have 2 routes as services
         zipkin.addClientServiceMapping("seda:cat", "cat");
         zipkin.addServerServiceMapping("seda:cat", "cat");

http://git-wip-us.apache.org/repos/asf/camel/blob/7e40ae05/components/camel-zipkin/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/components/camel-zipkin/src/test/resources/log4j.properties b/components/camel-zipkin/src/test/resources/log4j.properties
index ab7b29f..538c5bd 100644
--- a/components/camel-zipkin/src/test/resources/log4j.properties
+++ b/components/camel-zipkin/src/test/resources/log4j.properties
@@ -18,7 +18,7 @@
 #
 # The logging properties used
 #
-log4j.rootLogger=INFO, file
+log4j.rootLogger=INFO, out
 
 #log4j.logger.org.apache.camel=DEBUG
 log4j.logger.org.apache.camel.zipkin=DEBUG