diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/MilliSpan.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/MilliSpan.java index d81da445e..11c1defa9 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/MilliSpan.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/MilliSpan.java @@ -22,30 +22,48 @@ import java.util.List; import java.util.Map; import lombok.Builder; +import lombok.Data; import lombok.Singular; -import lombok.Value; -import lombok.experimental.NonFinal; /** * @author Spencer Gibb */ -@Value +@Data @Builder public class MilliSpan implements Span { - private long begin; - @NonFinal + private final long begin; private long end = 0; - private String name; + private final String name; private final String traceId; @Singular private List parents; private final String spanId; - @NonFinal private boolean remote = false; - private Map kVAnnotations = new LinkedHashMap<>(); + private final Map kVAnnotations = new LinkedHashMap<>(); private final String processId; @Singular - private List timelineAnnotations = new ArrayList<>(); + private final List timelineAnnotations = new ArrayList<>(); + + public MilliSpan(long begin, long end, String name, String traceId, List parents, String spanId, boolean remote, String processId) { + this.begin = begin; + this.end = end; + this.name = name; + this.traceId = traceId; + this.parents = parents; + this.spanId = spanId; + this.remote = remote; + this.processId = processId; + } + + //for serialization + private MilliSpan() { + this.begin = 0; + this.name = null; + this.traceId = null; + this.parents = null; + this.spanId = null; + this.processId = null; + } @Override public synchronized void stop() { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TimelineAnnotation.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TimelineAnnotation.java index 3d3e4a8a6..a7d7974a8 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TimelineAnnotation.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TimelineAnnotation.java @@ -17,12 +17,19 @@ package org.springframework.cloud.sleuth; import lombok.Data; +import lombok.RequiredArgsConstructor; /** * @author Spencer Gibb */ @Data +@RequiredArgsConstructor public class TimelineAnnotation { private final long time; private final String msg; + + private TimelineAnnotation() { + this.time = 0; + this.msg = null; + } } 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 8298034ec..4d01d6fb4 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 @@ -37,14 +37,14 @@ import org.springframework.util.StringUtils; * @author Spencer Gibb */ @Configuration -@ConditionalOnProperty(value = "spring.cloud.sleuth.trace.web.enabled", matchIfMissing = true) +@ConditionalOnProperty(value = "spring.sleuth.trace.web.enabled", matchIfMissing = true) @ConditionalOnWebApplication public class TraceWebAutoConfiguration { /** * Pattern for URLs that should be skipped in tracing */ - @Value("${spring.cloud.sleuth.instrument.web.skipPattern:}") + @Value("${spring.sleuth.instrument.web.skipPattern:}") private String skipPattern; @Autowired diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java index c96683317..6104511a1 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java @@ -30,7 +30,7 @@ import org.springframework.web.client.RestTemplate; * @author Spencer Gibb */ @Configuration -@ConditionalOnProperty(value = "spring.cloud.sleuth.trace.web.client.enabled", matchIfMissing = true) +@ConditionalOnProperty(value = "spring.sleuth.trace.web.client.enabled", matchIfMissing = true) @ConditionalOnClass(RestTemplate.class) public class TraceWebClientAutoConfiguration { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/log/JsonLogSpanListener.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/log/JsonLogSpanListener.java new file mode 100644 index 000000000..efc0a32e7 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/log/JsonLogSpanListener.java @@ -0,0 +1,56 @@ +/* + * 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.log; + +import lombok.Data; +import lombok.SneakyThrows; +import lombok.extern.apachecommons.CommonsLog; + +import org.springframework.cloud.sleuth.event.SpanStoppedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * @author Spencer Gibb + */ +@CommonsLog +@Order(Ordered.LOWEST_PRECEDENCE) +@Data +public class JsonLogSpanListener { + + private final String prefix; + private final String suffix; + private final ObjectMapper objectMapper = new ObjectMapper(); + + public JsonLogSpanListener() { + prefix = "[span]"; + suffix = "[endspan]"; + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + @SneakyThrows + @EventListener(SpanStoppedEvent.class) + public void stop(SpanStoppedEvent event) { + log.info(prefix + objectMapper.writeValueAsString(event.getSpan()) + + suffix); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/SleuthSlf4jAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/log/SleuthLogAutoConfiguration.java similarity index 54% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/SleuthSlf4jAutoConfiguration.java rename to spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/log/SleuthLogAutoConfiguration.java index 31fd58e97..74c31f1a2 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/SleuthSlf4jAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/log/SleuthLogAutoConfiguration.java @@ -14,10 +14,12 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.slf4j; +package org.springframework.cloud.sleuth.log; +import org.apache.commons.logging.Log; import org.slf4j.MDC; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -25,12 +27,26 @@ import org.springframework.context.annotation.Configuration; * @author Spencer Gibb */ @Configuration -@ConditionalOnClass(MDC.class) -public class SleuthSlf4jAutoConfiguration { +public class SleuthLogAutoConfiguration { - @Bean - public Slf4jSpanListener slf4jSpanStartedListener() { - return new Slf4jSpanListener(); + @Configuration + @ConditionalOnClass(MDC.class) + protected static class Slf4jConfiguration { + @Bean + @ConditionalOnProperty(value = "spring.sleuth.log.slf4j.enabled", matchIfMissing = true) + public Slf4jSpanListener slf4jSpanStartedListener() { + return new Slf4jSpanListener(); + } + } + + @Configuration + @ConditionalOnClass(Log.class) + protected static class JsonConfiguration { + @Bean + @ConditionalOnProperty("spring.sleuth.log.json.enabled") + public JsonLogSpanListener jsonSlf4jSpanListener() { + return new JsonLogSpanListener(); + } } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/Slf4jSpanListener.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/log/Slf4jSpanListener.java similarity index 97% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/Slf4jSpanListener.java rename to spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/log/Slf4jSpanListener.java index 754f4fce2..019378c85 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/Slf4jSpanListener.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/log/Slf4jSpanListener.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.slf4j; +package org.springframework.cloud.sleuth.log; import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME; import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME; diff --git a/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories index abd858dc9..00d6ba6eb 100644 --- a/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories @@ -1,7 +1,7 @@ # Auto Configuration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration,\ -org.springframework.cloud.sleuth.slf4j.SleuthSlf4jAutoConfiguration,\ +org.springframework.cloud.sleuth.log.SleuthLogAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.scheduling.TraceSchedulingAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.web.client.TraceWebClientAutoConfiguration diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/log/JsonLogSpanListenerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/log/JsonLogSpanListenerTests.java new file mode 100644 index 000000000..80dcfe99c --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/log/JsonLogSpanListenerTests.java @@ -0,0 +1,67 @@ +/* + * 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.log; + +import static org.junit.Assert.*; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.boot.test.OutputCapture; +import org.springframework.cloud.sleuth.MilliSpan; +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.event.SpanStoppedEvent; +import org.springframework.util.StringUtils; + +import java.io.IOException; + +/** + * @author Spencer Gibb + */ +public class JsonLogSpanListenerTests { + @Rule + public final OutputCapture output = new OutputCapture(); + + @Test + public void jsonSpanIsOnOneLine() throws IOException { + JsonLogSpanListener listener = new JsonLogSpanListener(); + Span span = MilliSpan.builder() + .name("testSpan") + .spanId("spanId1") + .parent("parentId1") + .traceId("traceId1") + .begin(1) + .end(10) + .build(); + span.addKVAnnotation("myKey", "myVal"); + span.addTimelineAnnotation("myTimelineAnnotation"); + listener.stop(new SpanStoppedEvent(this, span)); + + String output = this.output.toString().trim(); + assertTrue("output doesn't contain prefix", output.contains(listener.getPrefix())); + assertTrue("output doesn't contain suffix", output.contains(listener.getSuffix())); + + int prefixIndex = output.indexOf(listener.getPrefix()); + int suffixIndex = output.indexOf(listener.getSuffix()); + String json = output.substring(prefixIndex + listener.getPrefix().length(), suffixIndex); + assertTrue("json is empty", StringUtils.hasText(json)); + assertFalse("json contains linefeed", output.contains("\n")); + assertFalse("json contains carriage return", output.contains("\r")); + + MilliSpan read = listener.getObjectMapper().readValue(json, MilliSpan.class); + assertEquals("span not equals", read, span); + } +} diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinAutoConfiguration.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinAutoConfiguration.java index 7e05b81c0..c34f5cf9d 100644 --- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinAutoConfiguration.java +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinAutoConfiguration.java @@ -49,7 +49,7 @@ import com.google.common.base.Optional; @Configuration @EnableConfigurationProperties @ConditionalOnClass(ServerTracerConfig.class) -@ConditionalOnProperty(value = "spring.cloud.sleuth.zipkin.enabled", matchIfMissing = true) +@ConditionalOnProperty(value = "spring.sleuth.zipkin.enabled", matchIfMissing = true) @Import({ AnnotationSubmitterConfig.class, ClientTracerConfig.class, EndPointSubmitterConfig.class, ServerSpanThreadBinderConfig.class, ServerTracerConfig.class }) @@ -78,14 +78,8 @@ public class ZipkinAutoConfiguration { return new TraceFilters(traceFilters); } - // @Bean - // @ConditionalOnProperty(value = "spring.cloud.sleuth.zipkin.braveTracer.enabled", matchIfMissing = true) - // public ZipkinSpanListener zipkinTrace(ServerTracer serverTracer, ClientTracer clientTracer) { - // return new ZipkinSpanListener(serverTracer, clientTracer); - // } - @Bean - // @ConditionalOnProperty(value = "spring.cloud.sleuth.zipkin.braveTracer.enabled", havingValue = "false") + // @ConditionalOnProperty(value = "spring.sleuth.zipkin.braveTracer.enabled", havingValue = "false") public ZipkinSpanListener sleuthTracer(SpanCollector spanCollector) { return new ZipkinSpanListener(spanCollector); } diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/web/ZipkinWebAutoConfiguration.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/web/ZipkinWebAutoConfiguration.java index 78f5915fb..a744e6edb 100644 --- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/web/ZipkinWebAutoConfiguration.java +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/web/ZipkinWebAutoConfiguration.java @@ -36,7 +36,7 @@ import com.github.kristofa.brave.ServerTracerConfig; @Configuration @ConditionalOnClass(ServerTracerConfig.class) @ConditionalOnWebApplication -@ConditionalOnProperty(value = "spring.cloud.sleuth.zipkin.enabled", matchIfMissing = true) +@ConditionalOnProperty(value = "spring.sleuth.zipkin.enabled", matchIfMissing = true) @AutoConfigureAfter(ZipkinAutoConfiguration.class) @AutoConfigureBefore(TraceAutoConfiguration.class) public class ZipkinWebAutoConfiguration {