diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ArrayListSpanAccumulator.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ArrayListSpanAccumulator.java index d3671e943..1b5d07ef8 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ArrayListSpanAccumulator.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ArrayListSpanAccumulator.java @@ -41,23 +41,6 @@ public class ArrayListSpanAccumulator implements ApplicationListenerargThat( + instanceOf(LocalComponentTraceRunnable.class)), anyLong(), any(TimeUnit.class)); } @Test @@ -58,24 +60,24 @@ public class TraceableScheduledExecutorServiceTest { public void should_schedule_a_local_component_trace_callable() throws Exception { this.traceableScheduledExecutorService.schedule(aCallable(), 1L, TimeUnit.DAYS); - then(this.scheduledExecutorService).should().schedule(any( - LocalComponentTraceCallable.class), anyLong(), any(TimeUnit.class)); + then(this.scheduledExecutorService).should().schedule(BDDMockito.argThat( + instanceOf(LocalComponentTraceCallable.class)), anyLong(), any(TimeUnit.class)); } @Test public void should_schedule_at_fixed_rate_a_local_component_trace_runnable() throws Exception { this.traceableScheduledExecutorService.scheduleAtFixedRate(aRunnable(), 1L, 1L, TimeUnit.DAYS); - then(this.scheduledExecutorService).should().scheduleAtFixedRate(any( - LocalComponentTraceRunnable.class), anyLong(), anyLong(), any(TimeUnit.class)); + then(this.scheduledExecutorService).should().scheduleAtFixedRate(BDDMockito.argThat( + instanceOf(LocalComponentTraceRunnable.class)), anyLong(), anyLong(), any(TimeUnit.class)); } @Test public void should_schedule_with_fixed_delay_a_local_component_trace_runnable() throws Exception { this.traceableScheduledExecutorService.scheduleWithFixedDelay(aRunnable(), 1L, 1L, TimeUnit.DAYS); - then(this.scheduledExecutorService).should().scheduleWithFixedDelay(any( - LocalComponentTraceRunnable.class), anyLong(), anyLong(), any(TimeUnit.class)); + then(this.scheduledExecutorService).should().scheduleWithFixedDelay(BDDMockito.argThat( + instanceOf(LocalComponentTraceRunnable.class)), anyLong(), anyLong(), any(TimeUnit.class)); } Runnable aRunnable() { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceAsyncListenableTaskExecutorTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceAsyncListenableTaskExecutorTest.java new file mode 100644 index 000000000..5466a3de8 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceAsyncListenableTaskExecutorTest.java @@ -0,0 +1,109 @@ +/* + * Copyright 2013-2016 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.web.client; + +import java.util.Random; +import java.util.concurrent.Callable; + +import org.junit.Test; +import org.mockito.BDDMockito; +import org.springframework.cloud.sleuth.DefaultSpanNamer; +import org.springframework.cloud.sleuth.TraceCallable; +import org.springframework.cloud.sleuth.TraceRunnable; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.sampler.AlwaysSampler; +import org.springframework.cloud.sleuth.trace.DefaultTracer; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.core.task.AsyncListenableTaskExecutor; + +import static org.hamcrest.Matchers.instanceOf; +import static org.mockito.BDDMockito.mock; + +/** + * @author Marcin Grzejszczak + */ +public class TraceAsyncListenableTaskExecutorTest { + + AsyncListenableTaskExecutor delegate = mock(AsyncListenableTaskExecutor.class); + ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class); + Tracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), + this.publisher, new DefaultSpanNamer()) { + @Override public boolean isTracing() { + return true; + } + }; + TraceAsyncListenableTaskExecutor traceAsyncListenableTaskExecutor = + new TraceAsyncListenableTaskExecutor(this.delegate, this.tracer); + + @Test + public void should_submit_listenable_trace_runnable() throws Exception { + this.traceAsyncListenableTaskExecutor.submitListenable(aRunnable()); + + BDDMockito.then(this.delegate).should().submitListenable(BDDMockito.argThat( + instanceOf(TraceRunnable.class))); + } + + @Test + public void should_submit_listenable_trace_callable() throws Exception { + this.traceAsyncListenableTaskExecutor.submitListenable(aCallable()); + + BDDMockito.then(this.delegate).should().submitListenable(BDDMockito.argThat( + instanceOf(TraceCallable.class))); + } + + @Test + public void should_execute_a_trace_runnable() throws Exception { + this.traceAsyncListenableTaskExecutor.execute(aRunnable()); + + BDDMockito.then(this.delegate).should().execute(BDDMockito.argThat( + instanceOf(TraceRunnable.class))); + } + + @Test + public void should_execute_with_timeout_a_trace_runnable() throws Exception { + this.traceAsyncListenableTaskExecutor.execute(aRunnable(), 1L); + + BDDMockito.then(this.delegate).should().execute(BDDMockito.argThat( + instanceOf(TraceRunnable.class)), BDDMockito.anyLong()); + } + + @Test + public void should_submit_trace_callable() throws Exception { + this.traceAsyncListenableTaskExecutor.submit(aCallable()); + + BDDMockito.then(this.delegate).should().submit(BDDMockito.argThat( + instanceOf(TraceCallable.class))); + } + + @Test + public void should_submit_trace_runnable() throws Exception { + this.traceAsyncListenableTaskExecutor.submit(aRunnable()); + + BDDMockito.then(this.delegate).should().submit(BDDMockito.argThat( + instanceOf(TraceRunnable.class))); + } + + Runnable aRunnable() { + return () -> { + + }; + } + + Callable aCallable() { + return () -> null; + } +} \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/util/ExceptionUtilsTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/util/ExceptionUtilsTest.java new file mode 100644 index 000000000..7820dd83f --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/util/ExceptionUtilsTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2016 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.util; + +import org.junit.After; +import org.junit.Test; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.junit.Assert.fail; + +/** + * @author Marcin Grzejszczak + */ +public class ExceptionUtilsTest { + + @After + public void clean() { + ExceptionUtils.setFail(true); + } + + @Test + public void should_not_throw_exception_if_flag_is_disabled() throws Exception { + ExceptionUtils.setFail(false); + + ExceptionUtils.warn("warning message"); + } + + @Test + public void should_throw_exception_if_flag_is_disabled() throws Exception { + ExceptionUtils.setFail(true); + + try { + ExceptionUtils.warn("warning message"); + fail("should throw an exception"); + } catch (Exception e) { + then(e).isInstanceOf(IllegalStateException.class); + } + } +} \ No newline at end of file diff --git a/spring-cloud-sleuth-stream/src/main/java/org/springframework/cloud/sleuth/stream/SleuthStreamAutoConfiguration.java b/spring-cloud-sleuth-stream/src/main/java/org/springframework/cloud/sleuth/stream/SleuthStreamAutoConfiguration.java index eb071e0ee..ddc260fe8 100644 --- a/spring-cloud-sleuth-stream/src/main/java/org/springframework/cloud/sleuth/stream/SleuthStreamAutoConfiguration.java +++ b/spring-cloud-sleuth-stream/src/main/java/org/springframework/cloud/sleuth/stream/SleuthStreamAutoConfiguration.java @@ -27,7 +27,6 @@ import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.sleuth.Sampler; -import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.metric.SpanReporterService; import org.springframework.cloud.sleuth.sampler.PercentageBasedSampler; import org.springframework.cloud.sleuth.sampler.SamplerProperties; @@ -37,11 +36,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.integration.config.GlobalChannelInterceptor; -import org.springframework.integration.support.MessageBuilder; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; import org.springframework.messaging.support.ChannelInterceptor; -import org.springframework.messaging.support.ChannelInterceptorAdapter; /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration} @@ -69,30 +64,8 @@ public class SleuthStreamAutoConfiguration { @Bean @GlobalChannelInterceptor(patterns = SleuthSource.OUTPUT, order = Ordered.HIGHEST_PRECEDENCE) - public ChannelInterceptor zipkinChannelInterceptor(final SpanReporterService spanReporterService) { - // don't trace the tracer (suppress spans originating from our own source) - return new ChannelInterceptorAdapter() { - @Override - public Message preSend(Message message, MessageChannel channel) { - return MessageBuilder.fromMessage(message) - .setHeader(Span.NOT_SAMPLED_NAME, "true").build(); - } - - @Override - public void afterSendCompletion(Message message, MessageChannel channel, - boolean sent, Exception ex) { - if (!(message.getPayload() instanceof Spans)) { - return; - } - Spans spans = (Spans) message.getPayload(); - int spanNumber = spans.getSpans().size(); - if (sent) { - spanReporterService.incrementAcceptedSpans(spanNumber); - } else { - spanReporterService.incrementDroppedSpans(spanNumber); - } - } - }; + public ChannelInterceptor zipkinChannelInterceptor(SpanReporterService spanReporterService) { + return new TracerIgnoringChannelInterceptor(spanReporterService); } @Bean diff --git a/spring-cloud-sleuth-stream/src/main/java/org/springframework/cloud/sleuth/stream/TracerIgnoringChannelInterceptor.java b/spring-cloud-sleuth-stream/src/main/java/org/springframework/cloud/sleuth/stream/TracerIgnoringChannelInterceptor.java new file mode 100644 index 000000000..caa14f4a3 --- /dev/null +++ b/spring-cloud-sleuth-stream/src/main/java/org/springframework/cloud/sleuth/stream/TracerIgnoringChannelInterceptor.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2016 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.stream; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.metric.SpanReporterService; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.support.ChannelInterceptorAdapter; + +/** + * {@link org.springframework.messaging.support.ChannelInterceptor} that doesn't + * trace the tracer. + * + * @author Marcin Grzejszczak + */ +class TracerIgnoringChannelInterceptor extends ChannelInterceptorAdapter { + + private final SpanReporterService spanReporterService; + + public TracerIgnoringChannelInterceptor(SpanReporterService spanReporterService) { + this.spanReporterService = spanReporterService; + } + + /** + * Don't trace the tracer (suppress spans originating from our own source) + **/ + @Override + public Message preSend(Message message, MessageChannel channel) { + return MessageBuilder.fromMessage(message) + .setHeader(Span.NOT_SAMPLED_NAME, "true").build(); + } + + @Override + public void afterSendCompletion(Message message, MessageChannel channel, + boolean sent, Exception ex) { + if (!(message.getPayload() instanceof Spans)) { + return; + } + Spans spans = (Spans) message.getPayload(); + int spanNumber = spans.getSpans().size(); + if (sent) { + this.spanReporterService.incrementAcceptedSpans(spanNumber); + } else { + this.spanReporterService.incrementDroppedSpans(spanNumber); + } + } +} diff --git a/spring-cloud-sleuth-stream/src/test/java/org/springframework/cloud/sleuth/stream/TracerIgnoringChannelInterceptorTest.java b/spring-cloud-sleuth-stream/src/test/java/org/springframework/cloud/sleuth/stream/TracerIgnoringChannelInterceptorTest.java new file mode 100644 index 000000000..e678a5af9 --- /dev/null +++ b/spring-cloud-sleuth-stream/src/test/java/org/springframework/cloud/sleuth/stream/TracerIgnoringChannelInterceptorTest.java @@ -0,0 +1,88 @@ +/* + * Copyright 2013-2016 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.stream; + +import java.util.Arrays; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.BDDMockito; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.metric.SpanReporterService; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * @author Marcin Grzejszczak + */ +@RunWith(MockitoJUnitRunner.class) +public class TracerIgnoringChannelInterceptorTest { + + @Mock MessageChannel messageChannel; + @Mock SpanReporterService spanReporterService; + @InjectMocks TracerIgnoringChannelInterceptor tracerIgnoringChannelInterceptor; + + @Test + public void should_attach_not_sampled_header_to_the_message() throws Exception { + Message message = MessageBuilder.withPayload("hello").build(); + + Message interceptedMessage = this.tracerIgnoringChannelInterceptor.preSend(message, this.messageChannel); + + then(interceptedMessage.getHeaders().containsKey( + Span.NOT_SAMPLED_NAME)).isTrue(); + } + + @Test + public void should_ignore_metrics_when_message_payload_does_not_contain_spans() throws Exception { + Message message = MessageBuilder.withPayload("hello").build(); + + this.tracerIgnoringChannelInterceptor.afterSendCompletion(message, this.messageChannel, true, null); + + verifyZeroInteractions(this.spanReporterService); + } + + @Test + public void should_increment_accepted_spans_when_message_sending_was_successful() throws Exception { + Span span1 = Span.builder().build(); + Span span2 = Span.builder().build(); + Message message = MessageBuilder.withPayload(new Spans(null, + Arrays.asList(span1, span2))).build(); + + this.tracerIgnoringChannelInterceptor.afterSendCompletion(message, this.messageChannel, true, null); + + BDDMockito.then(this.spanReporterService).should().incrementAcceptedSpans(2); + } + + @Test + public void should_increment_dropped_spans_when_message_sending_was_successful() throws Exception { + Span span1 = Span.builder().build(); + Span span2 = Span.builder().build(); + Message message = MessageBuilder.withPayload(new Spans(null, + Arrays.asList(span1, span2))).build(); + + this.tracerIgnoringChannelInterceptor.afterSendCompletion(message, this.messageChannel, false, null); + + BDDMockito.then(this.spanReporterService).should().incrementDroppedSpans(2); + } +} \ No newline at end of file