diff --git a/jaeger-core/src/main/java/com/uber/jaeger/Span.java b/jaeger-core/src/main/java/com/uber/jaeger/Span.java index 1d6677d2d..ed778e64b 100644 --- a/jaeger-core/src/main/java/com/uber/jaeger/Span.java +++ b/jaeger-core/src/main/java/com/uber/jaeger/Span.java @@ -14,7 +14,10 @@ package com.uber.jaeger; +import io.opentracing.log.Fields; import io.opentracing.tag.Tags; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -228,6 +231,9 @@ public Span log(long timestampMicroseconds, Map fields) { return this; } if (context.isSampled()) { + if (tracer.isExpandExceptionLogs()) { + fields = addExceptionLogs(fields); + } if (logs == null) { this.logs = new ArrayList(); } @@ -257,4 +263,36 @@ public Span log(long timestampMicroseconds, String event) { return this; } } + + /** + * Creates logs related to logged exception + * + * @param fields map containing exception logs which are not present in fields + * @return logged fields + */ + private static Map addExceptionLogs(Map fields) { + Object ex = fields.get(Fields.ERROR_OBJECT); + if (!(ex instanceof Throwable)) { + return fields; + } + + Map errorFields = new HashMap(fields); + Throwable loggedException = (Throwable) ex; + + if (fields.get(Fields.ERROR_KIND) == null) { + errorFields.put(Fields.ERROR_KIND, loggedException.getClass().getName()); + } + if (fields.get(Fields.MESSAGE) == null) { + String message = loggedException.getMessage(); + if (message != null) { + errorFields.put(Fields.MESSAGE, message); + } + } + if (fields.get(Fields.STACK) == null) { + StringWriter sw = new StringWriter(); + loggedException.printStackTrace(new PrintWriter(sw)); + errorFields.put(Fields.STACK, sw.toString()); + } + return errorFields; + } } diff --git a/jaeger-core/src/main/java/com/uber/jaeger/Tracer.java b/jaeger-core/src/main/java/com/uber/jaeger/Tracer.java index de63d2277..68d6aaa02 100644 --- a/jaeger-core/src/main/java/com/uber/jaeger/Tracer.java +++ b/jaeger-core/src/main/java/com/uber/jaeger/Tracer.java @@ -69,6 +69,7 @@ public class Tracer implements io.opentracing.Tracer, Closeable { private final boolean zipkinSharedRpcSpan; private final ScopeManager scopeManager; private final BaggageSetter baggageSetter; + private final boolean expandExceptionLogs; private Tracer( String serviceName, @@ -80,7 +81,8 @@ private Tracer( Map tags, boolean zipkinSharedRpcSpan, ScopeManager scopeManager, - BaggageRestrictionManager baggageRestrictionManager) { + BaggageRestrictionManager baggageRestrictionManager, + boolean expandExceptionLogs) { this.serviceName = serviceName; this.reporter = reporter; this.sampler = sampler; @@ -90,6 +92,7 @@ private Tracer( this.zipkinSharedRpcSpan = zipkinSharedRpcSpan; this.scopeManager = scopeManager; this.baggageSetter = new BaggageSetter(baggageRestrictionManager, metrics); + this.expandExceptionLogs = expandExceptionLogs; this.version = loadVersion(); @@ -447,6 +450,7 @@ public static final class Builder { private boolean zipkinSharedRpcSpan; private ScopeManager scopeManager = new ThreadLocalScopeManager(); private BaggageRestrictionManager baggageRestrictionManager = new DefaultBaggageRestrictionManager(); + private boolean expandExceptionLogs; public Builder(String serviceName, Reporter reporter, Sampler sampler) { if (serviceName == null || serviceName.trim().length() == 0) { @@ -495,6 +499,11 @@ public Builder withZipkinSharedRpcSpan() { return this; } + public Builder withExpandExceptionLogs() { + this.expandExceptionLogs = true; + return this; + } + public Builder withMetrics(Metrics metrics) { this.metrics = metrics; return this; @@ -529,7 +538,7 @@ public Builder withBaggageRestrictionManager(BaggageRestrictionManager baggageRe public Tracer build() { return new Tracer(this.serviceName, reporter, sampler, registry, clock, metrics, tags, - zipkinSharedRpcSpan, scopeManager, baggageRestrictionManager); + zipkinSharedRpcSpan, scopeManager, baggageRestrictionManager, expandExceptionLogs); } } @@ -588,4 +597,8 @@ String getHostName() { SpanContext setBaggage(Span span, String key, String value) { return baggageSetter.setBaggage(span, key, value); } + + boolean isExpandExceptionLogs() { + return this.expandExceptionLogs; + } } diff --git a/jaeger-core/src/test/java/com/uber/jaeger/SpanTest.java b/jaeger-core/src/test/java/com/uber/jaeger/SpanTest.java index 9b7103834..8ee54cc63 100644 --- a/jaeger-core/src/test/java/com/uber/jaeger/SpanTest.java +++ b/jaeger-core/src/test/java/com/uber/jaeger/SpanTest.java @@ -31,9 +31,15 @@ import com.uber.jaeger.samplers.ConstSampler; import com.uber.jaeger.utils.Clock; import io.opentracing.References; +import io.opentracing.log.Fields; import io.opentracing.tag.Tags; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Field; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.junit.Before; @@ -59,6 +65,7 @@ public void setUp() throws Exception { .withStatsReporter(metricsReporter) .withClock(clock) .withBaggageRestrictionManager(new DefaultBaggageRestrictionManager()) + .withExpandExceptionLogs() .build(); span = (Span) tracer.buildSpan("some-operation").start(); } @@ -346,4 +353,91 @@ public void testImmutableBaggage() { baggageIter.next(); assertFalse(baggageIter.hasNext()); } + + @Test + public void testExpandExceptionLogs() { + RuntimeException ex = new RuntimeException(new NullPointerException("npe")); + Map logs = new HashMap<>(); + logs.put(Fields.ERROR_OBJECT, ex); + Span span = (Span)tracer.buildSpan("foo").start(); + span.log(logs); + + List logData = span.getLogs(); + assertEquals(1, logData.size()); + assertEquals(4, logData.get(0).getFields().size()); + + assertEquals(ex, logData.get(0).getFields().get(Fields.ERROR_OBJECT)); + assertEquals(ex.getMessage(), logData.get(0).getFields().get(Fields.MESSAGE)); + assertEquals(ex.getClass().getName(), logData.get(0).getFields().get(Fields.ERROR_KIND)); + StringWriter sw = new StringWriter(); + ex.printStackTrace(new PrintWriter(sw)); + assertEquals(sw.toString(), logData.get(0).getFields().get(Fields.STACK)); + } + + @Test + public void testExpandExceptionLogsExpanded() { + RuntimeException ex = new RuntimeException(new NullPointerException("npe")); + Map logs = new HashMap<>(); + logs.put(Fields.ERROR_OBJECT, ex); + logs.put(Fields.MESSAGE, ex.getMessage()); + logs.put(Fields.ERROR_KIND, ex.getClass().getName()); + StringWriter sw = new StringWriter(); + ex.printStackTrace(new PrintWriter(sw)); + logs.put(Fields.STACK, sw.toString()); + Span span = (Span)tracer.buildSpan("foo").start(); + span.log(logs); + + List logData = span.getLogs(); + assertEquals(1, logData.size()); + assertEquals(4, logData.get(0).getFields().size()); + + assertEquals(ex, logData.get(0).getFields().get(Fields.ERROR_OBJECT)); + assertEquals(ex.getMessage(), logData.get(0).getFields().get(Fields.MESSAGE)); + assertEquals(ex.getClass().getName(), logData.get(0).getFields().get(Fields.ERROR_KIND)); + assertEquals(sw.toString(), logData.get(0).getFields().get(Fields.STACK)); + } + + @Test + public void testExpandExceptionLogsLoggedNoException() { + Span span = (Span)tracer.buildSpan("foo").start(); + + Object object = new Object(); + Map logs = new HashMap<>(); + logs.put(Fields.ERROR_OBJECT, object); + span.log(logs); + + List logData = span.getLogs(); + assertEquals(1, logData.size()); + assertEquals(1, logData.get(0).getFields().size()); + assertEquals(object, logData.get(0).getFields().get(Fields.ERROR_OBJECT)); + } + + @Test + public void testNoExpandExceptionLogs() { + Tracer tracer = new Tracer.Builder("fo", reporter, new ConstSampler(true)) + .build(); + + Span span = (Span)tracer.buildSpan("foo").start(); + + RuntimeException ex = new RuntimeException(); + Map logs = new HashMap<>(); + logs.put(Fields.ERROR_OBJECT, ex); + span.log(logs); + + List logData = span.getLogs(); + assertEquals(1, logData.size()); + assertEquals(1, logData.get(0).getFields().size()); + assertEquals(ex, logData.get(0).getFields().get(Fields.ERROR_OBJECT)); + } + + @Test + public void testSpanNotSampled() { + Tracer tracer = new Tracer.Builder("fo", reporter, new ConstSampler(false)) + .build(); + io.opentracing.Span foo = tracer.buildSpan("foo") + .start(); + foo.log(Collections.emptyMap()) + .finish(); + assertEquals(0, reporter.getSpans().size()); + } }