diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ServerReceivedEvent.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ServerReceivedEvent.java new file mode 100644 index 000000000..0663e1706 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ServerReceivedEvent.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.event; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.context.ApplicationEvent; + +/** + * @author Spencer Gibb + */ +@Data +@EqualsAndHashCode(callSuper=false) +@SuppressWarnings("serial") +public class ServerReceivedEvent extends ApplicationEvent { + + private final Span parent; + private final Span span; + + public ServerReceivedEvent(Object source, Span span) { + this(source, null, span); + } + + public ServerReceivedEvent(Object source, Span parent, Span span) { + super(source); + this.parent = parent; + this.span = span; + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ServerSentEvent.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ServerSentEvent.java new file mode 100644 index 000000000..aa59a2742 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ServerSentEvent.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.event; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.context.ApplicationEvent; + +/** + * @author Spencer Gibb + */ +@Data +@EqualsAndHashCode(callSuper=false) +@SuppressWarnings("serial") +public class ServerSentEvent extends ApplicationEvent { + + private final Span parent; + private final Span span; + + public ServerSentEvent(Object source, Span span) { + this(source, null, span); + } + + public ServerSentEvent(Object source, Span parent, Span span) { + super(source); + this.parent = parent; + this.span = span; + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java index a1d662bcb..1fcab34fb 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java @@ -34,9 +34,15 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.cloud.sleuth.MilliSpan; import org.springframework.cloud.sleuth.MilliSpan.MilliSpanBuilder; +import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Trace; import org.springframework.cloud.sleuth.TraceContextHolder; import org.springframework.cloud.sleuth.TraceScope; +import org.springframework.cloud.sleuth.event.ServerReceivedEvent; +import org.springframework.cloud.sleuth.event.ServerSentEvent; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.web.filter.OncePerRequestFilter; @@ -56,7 +62,7 @@ import org.springframework.web.util.UrlPathHelper; * @author Dave Syer */ @Order(Ordered.HIGHEST_PRECEDENCE + 5) -public class TraceFilter extends OncePerRequestFilter { +public class TraceFilter extends OncePerRequestFilter implements ApplicationEventPublisherAware { protected static final String TRACE_REQUEST_ATTR = TraceFilter.class.getName() + ".TRACE"; @@ -68,6 +74,8 @@ public class TraceFilter extends OncePerRequestFilter { private final Pattern skipPattern; private UrlPathHelper urlPathHelper = new UrlPathHelper(); + private ApplicationEventPublisher publisher; + public TraceFilter(Trace trace) { this.trace = trace; this.skipPattern = DEFAULT_SKIP_PATTERN; @@ -78,6 +86,11 @@ public class TraceFilter extends OncePerRequestFilter { this.skipPattern = skipPattern; } + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { + this.publisher = publisher; + } + @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) @@ -117,7 +130,9 @@ public class TraceFilter extends OncePerRequestFilter { span.remote(true); // TODO: trace description? - traceScope = this.trace.startSpan(name, span.build()); + Span parent = span.build(); + traceScope = this.trace.startSpan(name, parent); + publish(new ServerReceivedEvent(this, parent, traceScope.getSpan())); request.setAttribute(TRACE_REQUEST_ATTR, traceScope); // Send new span id back addToResponseIfNotPresent(response, TRACE_ID_NAME, traceScope.getSpan() @@ -144,12 +159,19 @@ public class TraceFilter extends OncePerRequestFilter { } if (traceScope != null) { addResponseAnnotations(response); + publish(new ServerSentEvent(this, traceScope.getSavedSpan(), traceScope.getSpan())); traceScope.close(); } TraceContextHolder.removeCurrentSpan(); } } + private void publish(ApplicationEvent event) { + if (this.publisher!=null) { + this.publisher.publishEvent(event); + } + } + //TODO: move annotation keys to constants protected void addRequestAnnotations(HttpServletRequest request) { String uri = this.urlPathHelper.getPathWithinApplication(request); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java index 421e2d7c1..4f579fa74 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java @@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.context.embedded.FilterRegistrationBean; import org.springframework.cloud.sleuth.Trace; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; @@ -63,10 +64,12 @@ public class TraceWebAutoConfiguration { @Bean @ConditionalOnMissingBean - public FilterRegistrationBean traceFilter() { + public FilterRegistrationBean traceFilter(ApplicationEventPublisher publisher) { Pattern pattern = StringUtils.hasText(this.skipPattern) ? Pattern.compile(this.skipPattern) : TraceFilter.DEFAULT_SKIP_PATTERN; - return new FilterRegistrationBean(new TraceFilter(this.trace, pattern)); + TraceFilter filter = new TraceFilter(this.trace, pattern); + filter.setApplicationEventPublisher(publisher); + return new FilterRegistrationBean(filter); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePostZuulFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePostZuulFilter.java new file mode 100644 index 000000000..eb95e9d72 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePostZuulFilter.java @@ -0,0 +1,70 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.instrument.zuul; + +import static org.springframework.cloud.sleuth.TraceContextHolder.getCurrentSpan; + +import org.springframework.cloud.sleuth.event.ClientReceivedEvent; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; + +import com.netflix.zuul.ZuulFilter; + +/** + * @author Dave Syer + * + */ +public class TracePostZuulFilter extends ZuulFilter implements +ApplicationEventPublisherAware { + + private ApplicationEventPublisher publisher; + + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { + this.publisher = publisher; + } + + @Override + public boolean shouldFilter() { + return true; + } + + @Override + public Object run() { + // TODO: the client sent event should come from the client not the filter! + publish(new ClientReceivedEvent(this, getCurrentSpan())); + return null; + } + + @Override + public String filterType() { + return "post"; + } + + @Override + public int filterOrder() { + return 0; + } + + private void publish(ApplicationEvent event) { + if (this.publisher != null) { + this.publisher.publishEvent(event); + } + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java index 00c08dcbc..18d7ad309 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java @@ -71,6 +71,7 @@ ApplicationEventPublisherAware { setHeader(response, SPAN_NAME_NAME, getCurrentSpan().getName()); setHeader(response, PARENT_ID_NAME, getParentId(getCurrentSpan())); setHeader(response, PROCESS_ID_NAME, getCurrentSpan().getProcessId()); + // TODO: the client sent event should come from the client not the filter! publish(new ClientSentEvent(this, getCurrentSpan())); } catch (Exception ex) { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactory.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactory.java index 21d7b609e..ce4477197 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactory.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactory.java @@ -48,7 +48,7 @@ import com.netflix.niws.client.http.RestClient; * @author Spencer Gibb */ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommandFactory - implements ApplicationEventPublisherAware { +implements ApplicationEventPublisherAware { private ApplicationEventPublisher publisher; @@ -70,7 +70,7 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand return new TraceRestClientRibbonCommand( context.getServiceId(), restClient, getVerb(context.getVerb()), context.getUri(), context.getRetryable(), context.getHeaders(), - context.getParams(), context.getRequestEntity(), publisher); + context.getParams(), context.getRequestEntity(), this.publisher); } class TraceRestClientRibbonCommand extends RestClientRibbonCommand { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceZuulAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceZuulAutoConfiguration.java index 05e1438ca..2b47e4f07 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceZuulAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceZuulAutoConfiguration.java @@ -44,7 +44,7 @@ public class TraceZuulAutoConfiguration { @Bean @ConditionalOnMissingBean - public TracePreZuulFilter traceZuulFilter() { + public TracePreZuulFilter tracePreZuulFilter() { return new TracePreZuulFilter(); } @@ -53,4 +53,10 @@ public class TraceZuulAutoConfiguration { return new TraceRestClientRibbonCommandFactory(factory); } + @Bean + @ConditionalOnMissingBean + public TracePostZuulFilter tracePostZuulFilter() { + return new TracePostZuulFilter(); + } + } diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListener.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListener.java index 6c2ad4bff..426b89815 100644 --- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListener.java +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListener.java @@ -33,6 +33,8 @@ import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.TimelineAnnotation; import org.springframework.cloud.sleuth.event.ClientReceivedEvent; import org.springframework.cloud.sleuth.event.ClientSentEvent; +import org.springframework.cloud.sleuth.event.ServerReceivedEvent; +import org.springframework.cloud.sleuth.event.ServerSentEvent; import org.springframework.cloud.sleuth.event.SpanAcquiredEvent; import org.springframework.cloud.sleuth.event.SpanReleasedEvent; import org.springframework.context.event.EventListener; @@ -65,10 +67,15 @@ public class ZipkinSpanListener { @EventListener @Order(0) public void start(SpanAcquiredEvent event) { + event.getSpan().addTimelineAnnotation("acquire"); + } + + @EventListener + @Order(0) + public void serverReceived(ServerReceivedEvent event) { if (event.getParent() != null && event.getParent().isRemote()) { event.getParent().addTimelineAnnotation(zipkinCoreConstants.SERVER_RECV); } - event.getSpan().addTimelineAnnotation("acquire"); } @EventListener @@ -85,11 +92,16 @@ public class ZipkinSpanListener { @EventListener @Order(0) - public void release(SpanReleasedEvent event) { + public void serverSend(ServerSentEvent event) { if (event.getParent() != null && event.getParent().isRemote()) { event.getParent().addTimelineAnnotation(zipkinCoreConstants.SERVER_SEND); this.spanCollector.collect(convert(event.getParent())); } + } + + @EventListener + @Order(0) + public void release(SpanReleasedEvent event) { event.getSpan().addTimelineAnnotation("release"); this.spanCollector.collect(convert(event.getSpan())); }