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 6f7f77c52..11aff3b4a 100644 --- a/jaeger-core/src/main/java/com/uber/jaeger/Span.java +++ b/jaeger-core/src/main/java/com/uber/jaeger/Span.java @@ -22,6 +22,7 @@ package com.uber.jaeger; +import com.uber.jaeger.baggage.BaggageSetter; import io.opentracing.tag.Tags; import java.util.ArrayList; import java.util.Collections; @@ -116,22 +117,13 @@ public List getLogs() { @Override public Span setBaggageItem(String key, String value) { + if (key == null || value == null) { + return this; + } synchronized (this) { - // TODO emit a metric whenever baggage is updated - String prevItem = this.getBaggageItem(key); - this.context = this.context.withBaggageItem(key, value); - if (context.isSampled()) { - Map fields = new HashMap(); - fields.put("event", "baggage"); - fields.put("key", key); - fields.put("value", value); - if (prevItem != null) { - fields.put("override", "true"); - } - return this.log(fields); - } + context = tracer.setBaggage(this, key, value); + return this; } - return this; } @Override 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 eedde5994..8022cc1db 100644 --- a/jaeger-core/src/main/java/com/uber/jaeger/Tracer.java +++ b/jaeger-core/src/main/java/com/uber/jaeger/Tracer.java @@ -22,7 +22,9 @@ package com.uber.jaeger; -import com.uber.jaeger.Constants; +import com.uber.jaeger.baggage.BaggageRestrictionManager; +import com.uber.jaeger.baggage.BaggageSetter; +import com.uber.jaeger.baggage.DefaultBaggageRestrictionManager; import com.uber.jaeger.exceptions.UnsupportedFormatException; import com.uber.jaeger.metrics.Metrics; import com.uber.jaeger.metrics.NullStatsReporter; @@ -74,6 +76,7 @@ public class Tracer implements io.opentracing.Tracer { private final Map tags; private final boolean zipkinSharedRpcSpan; private final ActiveSpanSource activeSpanSource; + private final BaggageSetter baggageSetter; private Tracer( String serviceName, @@ -84,7 +87,8 @@ private Tracer( Metrics metrics, Map tags, boolean zipkinSharedRpcSpan, - ActiveSpanSource activeSpanSource) { + ActiveSpanSource activeSpanSource, + BaggageRestrictionManager baggageRestrictionManager) { this.serviceName = serviceName; this.reporter = reporter; this.sampler = sampler; @@ -93,6 +97,7 @@ private Tracer( this.metrics = metrics; this.zipkinSharedRpcSpan = zipkinSharedRpcSpan; this.activeSpanSource = activeSpanSource; + this.baggageSetter = new BaggageSetter(baggageRestrictionManager, metrics); this.version = loadVersion(); @@ -442,12 +447,13 @@ public static final class Builder { private final Sampler sampler; private final Reporter reporter; private final PropagationRegistry registry = new PropagationRegistry(); - private Metrics metrics; + private Metrics metrics = new Metrics(new StatsFactoryImpl(new NullStatsReporter())); private String serviceName; private Clock clock = new SystemClock(); private Map tags = new HashMap(); private boolean zipkinSharedRpcSpan; private ActiveSpanSource activeSpanSource = new ThreadLocalActiveSpanSource(); + private BaggageRestrictionManager baggageRestrictionManager = new DefaultBaggageRestrictionManager(); public Builder(String serviceName, Reporter reporter, Sampler sampler) { if (serviceName == null || serviceName.trim().length() == 0) { @@ -456,7 +462,6 @@ public Builder(String serviceName, Reporter reporter, Sampler sampler) { this.serviceName = serviceName; this.reporter = reporter; this.sampler = sampler; - this.metrics = new Metrics(new StatsFactoryImpl(new NullStatsReporter())); TextMapCodec textMapCodec = new TextMapCodec(false); this.registerInjector(Format.Builtin.TEXT_MAP, textMapCodec); @@ -524,9 +529,14 @@ public Builder withTags(Map tags) { return this; } + public Builder withBaggageRestrictionManager(BaggageRestrictionManager baggageRestrictionManager) { + this.baggageRestrictionManager = baggageRestrictionManager; + return this; + } + public Tracer build() { return new Tracer(this.serviceName, reporter, sampler, registry, clock, metrics, tags, - zipkinSharedRpcSpan, activeSpanSource); + zipkinSharedRpcSpan, activeSpanSource, baggageRestrictionManager); } } @@ -582,4 +592,7 @@ String getHostName() { } } + SpanContext setBaggage(Span span, String key, String value) { + return baggageSetter.setBaggage(span, key, value); + } } diff --git a/jaeger-core/src/main/java/com/uber/jaeger/baggage/BaggageRestrictionManager.java b/jaeger-core/src/main/java/com/uber/jaeger/baggage/BaggageRestrictionManager.java new file mode 100644 index 000000000..58549addc --- /dev/null +++ b/jaeger-core/src/main/java/com/uber/jaeger/baggage/BaggageRestrictionManager.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017, Uber Technologies, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.jaeger.baggage; + +/** + * BaggageRestrictionManager is an interface for a class that manages baggage + * restrictions for baggage keys. The manager will return a {@link Restriction} + * for a specific baggage key which will determine whether the baggage key is + * allowed for the current service and any other applicable restrictions on the + * baggage value. + */ +public interface BaggageRestrictionManager { + int DEFAULT_MAX_VALUE_LENGTH = 2048; + + Restriction getRestriction(String service, String key); +} diff --git a/jaeger-core/src/main/java/com/uber/jaeger/baggage/BaggageSetter.java b/jaeger-core/src/main/java/com/uber/jaeger/baggage/BaggageSetter.java new file mode 100644 index 000000000..2405f3654 --- /dev/null +++ b/jaeger-core/src/main/java/com/uber/jaeger/baggage/BaggageSetter.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017, Uber Technologies, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.jaeger.baggage; + +import com.uber.jaeger.Span; +import com.uber.jaeger.SpanContext; +import com.uber.jaeger.metrics.Metric; +import com.uber.jaeger.metrics.Metrics; + +import java.util.HashMap; +import java.util.Map; + +/** + * BaggageSetter is a class that sets baggage and the logs associated + * with the baggage on a given {@link Span}. + */ +public class BaggageSetter { + + private final BaggageRestrictionManager restrictionManager; + private final Metrics metrics; + + public BaggageSetter(BaggageRestrictionManager restrictionManager, Metrics metrics) { + this.restrictionManager = restrictionManager; + this.metrics = metrics; + } + + /** + * Sets the baggage key:value on the {@link Span} and the corresponding + * logs. Whether the baggage is set on the span depends on if the key + * is allowed to be set by this service. + *

+ * A {@link SpanContext} is returned with the new baggage key:value set + * if key is valid, else returns the existing {@link SpanContext} + * on the {@link Span}. + * + * @param span the span to set the baggage on + * @param value the baggage value to set + * @return the SpanContext with the baggage set + */ + public SpanContext setBaggage(Span span, String key, String value) { + Restriction restriction = restrictionManager.getRestriction(span.getTracer().getServiceName(), key); + boolean truncated = false; + String prevItem = null; + if (!restriction.isKeyAllowed()) { + metrics.baggageUpdateFailure.inc(1); + logFields(span, key, value, prevItem, truncated, restriction.isKeyAllowed()); + return span.context(); + } + if (value.length() > restriction.getMaxValueLength()) { + truncated = true; + value = value.substring(0, restriction.getMaxValueLength()); + metrics.baggageTruncate.inc(1); + } + prevItem = span.getBaggageItem(key); + logFields(span, key, value, prevItem, truncated, restriction.isKeyAllowed()); + metrics.baggageUpdateSuccess.inc(1); + return span.context().withBaggageItem(key, value); + } + + private void logFields(Span span, String key, String value, String prevItem, boolean truncated, boolean valid) { + if (!span.context().isSampled()) { + return; + } + Map fields = new HashMap(); + fields.put("event", "baggage"); + fields.put("key", key); + fields.put("value", value); + if (prevItem != null) { + fields.put("override", "true"); + } + if (truncated) { + fields.put("truncated", "true"); + } + if (!valid) { + fields.put("invalid", "true"); + } + span.log(fields); + } +} diff --git a/jaeger-core/src/main/java/com/uber/jaeger/baggage/DefaultBaggageRestrictionManager.java b/jaeger-core/src/main/java/com/uber/jaeger/baggage/DefaultBaggageRestrictionManager.java new file mode 100644 index 000000000..64c379c81 --- /dev/null +++ b/jaeger-core/src/main/java/com/uber/jaeger/baggage/DefaultBaggageRestrictionManager.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, Uber Technologies, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.jaeger.baggage; + +/** + * DefaultBaggageRestrictionManager is a manager that returns a {@link Restriction} + * that allows all baggage. + */ +public class DefaultBaggageRestrictionManager implements BaggageRestrictionManager { + private final Restriction restriction; + + public DefaultBaggageRestrictionManager() { + this(DEFAULT_MAX_VALUE_LENGTH); + } + + DefaultBaggageRestrictionManager(int maxValueLength) { + this.restriction = Restriction.of(true, maxValueLength); + } + + @Override + public Restriction getRestriction(String service, String key) { + return restriction; + } +} diff --git a/jaeger-core/src/main/java/com/uber/jaeger/baggage/Restriction.java b/jaeger-core/src/main/java/com/uber/jaeger/baggage/Restriction.java new file mode 100644 index 000000000..b217f3601 --- /dev/null +++ b/jaeger-core/src/main/java/com/uber/jaeger/baggage/Restriction.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017, Uber Technologies, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.jaeger.baggage; + +import lombok.Value; + +/** + * Restriction determines whether a baggage key is allowed and contains any + * restrictions on the baggage value. + */ +@Value(staticConstructor = "of") +public class Restriction { + boolean keyAllowed; + int maxValueLength; +} diff --git a/jaeger-core/src/main/java/com/uber/jaeger/metrics/Metrics.java b/jaeger-core/src/main/java/com/uber/jaeger/metrics/Metrics.java index a279dfcef..44542656d 100644 --- a/jaeger-core/src/main/java/com/uber/jaeger/metrics/Metrics.java +++ b/jaeger-core/src/main/java/com/uber/jaeger/metrics/Metrics.java @@ -211,4 +211,22 @@ public static Metrics fromStatsReporter(StatsReporter reporter) { ) // Number of times the Sampler failed to parse retrieved sampling strategy public Counter samplerParsingFailure; + + @Metric( + name = "baggage-update", + tags = {@Tag(key = "result", value = "ok")} + ) + // Number of times baggage was successfully written or updated on spans + public Counter baggageUpdateSuccess; + + @Metric( + name = "baggage-update", + tags = {@Tag(key = "result", value = "err")} + ) + // Number of times baggage failed to write or update on spans + public Counter baggageUpdateFailure; + + @Metric(name = "baggage-truncate") + // Number of times baggage was truncated as per baggage restrictions + public Counter baggageTruncate; } 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 9f8edfb97..1324eeb45 100644 --- a/jaeger-core/src/test/java/com/uber/jaeger/SpanTest.java +++ b/jaeger-core/src/test/java/com/uber/jaeger/SpanTest.java @@ -26,9 +26,16 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.uber.jaeger.baggage.BaggageRestrictionManager; +import com.uber.jaeger.baggage.BaggageSetter; +import com.uber.jaeger.baggage.DefaultBaggageRestrictionManager; +import com.uber.jaeger.baggage.Restriction; import com.uber.jaeger.metrics.InMemoryStatsReporter; +import com.uber.jaeger.metrics.Metrics; +import com.uber.jaeger.metrics.StatsFactoryImpl; import com.uber.jaeger.reporters.InMemoryReporter; import com.uber.jaeger.samplers.ConstSampler; import com.uber.jaeger.utils.Clock; @@ -41,6 +48,8 @@ import java.util.Map.Entry; import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; public class SpanTest { private Clock clock; @@ -48,16 +57,19 @@ public class SpanTest { private Tracer tracer; private Span span; private InMemoryStatsReporter metricsReporter; + private Metrics metrics; @Before public void setUp() throws Exception { metricsReporter = new InMemoryStatsReporter(); reporter = new InMemoryReporter(); clock = mock(Clock.class); + metrics = new Metrics(new StatsFactoryImpl(metricsReporter)); tracer = new Tracer.Builder("SamplerTest", reporter, new ConstSampler(true)) .withStatsReporter(metricsReporter) .withClock(clock) + .withBaggageRestrictionManager(new DefaultBaggageRestrictionManager()) .build(); span = (Span) tracer.buildSpan("some-operation").startManual(); } @@ -74,13 +86,21 @@ public void testSpanMetrics() { @Test public void testSetAndGetBaggageItem() { - String expected = "expected"; - String key = "some.BAGGAGE"; - span.setBaggageItem(key, expected); - assertEquals(expected, span.getBaggageItem(key)); + final String service = "SamplerTest"; + final BaggageRestrictionManager mgr = Mockito.mock(DefaultBaggageRestrictionManager.class); + tracer = + new Tracer.Builder(service, reporter, new ConstSampler(true)) + .withClock(clock) + .withBaggageRestrictionManager(mgr) + .build(); + span = (Span) tracer.buildSpan("some-operation").startManual(); - // Ensure the baggage was logged - this.assertBaggageLogs(span, key, expected, false); + final String key = "key"; + final String value = "value"; + when(mgr.getRestriction(service, key)).thenReturn(Restriction.of(true, 10)); + span.setBaggageItem(key, "value"); + verify(mgr).getRestriction(service, key); + assertEquals(value, span.getBaggageItem(key)); } @Test @@ -295,14 +315,12 @@ public void testSpanDetectsSamplingPriorityLessThanZero() { public void testBaggageOneReference() { io.opentracing.Span parent = tracer.buildSpan("foo").startManual(); parent.setBaggageItem("foo", "bar"); - this.assertBaggageLogs(parent, "foo", "bar", false); io.opentracing.Span child = tracer.buildSpan("foo") .asChildOf(parent) .startManual(); child.setBaggageItem("a", "a"); - this.assertBaggageLogs(child, "a", "a", false); assertNull(parent.getBaggageItem("a")); assertEquals("a", child.getBaggageItem("a")); @@ -313,10 +331,8 @@ public void testBaggageOneReference() { public void testBaggageMultipleReferences() { io.opentracing.Span parent1 = tracer.buildSpan("foo").startManual(); parent1.setBaggageItem("foo", "bar"); - this.assertBaggageLogs(parent1, "foo", "bar", false); io.opentracing.Span parent2 = tracer.buildSpan("foo").startManual(); parent2.setBaggageItem("foo2", "bar"); - this.assertBaggageLogs(parent2, "foo2", "bar", false); io.opentracing.Span child = tracer.buildSpan("foo") .asChildOf(parent1) @@ -324,9 +340,7 @@ public void testBaggageMultipleReferences() { .startManual(); child.setBaggageItem("a", "a"); - this.assertBaggageLogs(child, "a", "a", false); child.setBaggageItem("foo2", "b"); - this.assertBaggageLogs(child, "foo2", "b", true); assertNull(parent1.getBaggageItem("a")); assertNull(parent2.getBaggageItem("a")); @@ -349,18 +363,4 @@ public void testImmutableBaggage() { baggageIter.next(); assertFalse(baggageIter.hasNext()); } - - private void assertBaggageLogs(io.opentracing.Span span, String key, String value, boolean override) { - Span sp = (Span)span; - List logs = sp.getLogs(); - assertEquals(false, logs.isEmpty()); - Map fields = logs.get(logs.size() - 1).getFields(); - assertEquals(override ? 4 : 3, fields.size()); - assertEquals("baggage", fields.get("event")); - assertEquals(key, fields.get("key")); - assertEquals(value, fields.get("value")); - if (override) { - assertEquals("true", fields.get("override")); - } - } } diff --git a/jaeger-core/src/test/java/com/uber/jaeger/TracerTest.java b/jaeger-core/src/test/java/com/uber/jaeger/TracerTest.java index 4e3766209..c18247e47 100644 --- a/jaeger-core/src/test/java/com/uber/jaeger/TracerTest.java +++ b/jaeger-core/src/test/java/com/uber/jaeger/TracerTest.java @@ -30,6 +30,8 @@ import static org.mockito.Mockito.verify; import com.uber.jaeger.metrics.InMemoryStatsReporter; +import com.uber.jaeger.metrics.Metrics; +import com.uber.jaeger.metrics.StatsFactoryImpl; import com.uber.jaeger.propagation.Injector; import com.uber.jaeger.reporters.InMemoryReporter; import com.uber.jaeger.reporters.Reporter; @@ -41,7 +43,6 @@ import org.junit.Before; import org.junit.Test; - public class TracerTest { Tracer tracer; @@ -120,6 +121,22 @@ public void testBuilderIsNotServerRpc() { assertFalse(spanBuilder.isRpcServer()); } + @Test + public void testWithBaggageRestrictionManager() { + metricsReporter = new InMemoryStatsReporter(); + Metrics metrics = new Metrics(new StatsFactoryImpl(metricsReporter)); + tracer = + new Tracer.Builder("TracerTestService", new InMemoryReporter(), new ConstSampler(true)) + .withMetrics(metrics) + .build(); + Span span = (Span) tracer.buildSpan("some-operation").startManual(); + final String key = "key"; + tracer.setBaggage(span, key, "value"); + + assertEquals( + 1L, metricsReporter.counters.get("jaeger.baggage-update.result=ok").longValue()); + } + @Test public void testClose() { Reporter reporter = mock(Reporter.class); diff --git a/jaeger-core/src/test/java/com/uber/jaeger/baggage/BaggageSetterTest.java b/jaeger-core/src/test/java/com/uber/jaeger/baggage/BaggageSetterTest.java new file mode 100644 index 000000000..4a1a4f130 --- /dev/null +++ b/jaeger-core/src/test/java/com/uber/jaeger/baggage/BaggageSetterTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2017, Uber Technologies, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.jaeger.baggage; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.uber.jaeger.LogData; +import com.uber.jaeger.Span; +import com.uber.jaeger.SpanContext; +import com.uber.jaeger.Tracer; +import com.uber.jaeger.metrics.InMemoryStatsReporter; +import com.uber.jaeger.metrics.Metrics; +import com.uber.jaeger.metrics.StatsFactoryImpl; +import com.uber.jaeger.reporters.InMemoryReporter; +import com.uber.jaeger.samplers.ConstSampler; + +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +public class BaggageSetterTest { + + private InMemoryReporter reporter; + private Tracer tracer; + private Span span; + private InMemoryStatsReporter metricsReporter; + private Metrics metrics; + private BaggageRestrictionManager mgr; + private BaggageSetter setter; + + private static final String KEY = "key"; + private static final String SERVICE = "SamplerTest"; + + @Before + public void setUp() throws Exception { + metricsReporter = new InMemoryStatsReporter(); + reporter = new InMemoryReporter(); + metrics = new Metrics(new StatsFactoryImpl(metricsReporter)); + mgr = mock(DefaultBaggageRestrictionManager.class); + setter = new BaggageSetter(mgr, metrics); + tracer = + new Tracer.Builder(SERVICE, reporter, new ConstSampler(true)) + .withStatsReporter(metricsReporter) + .build(); + span = (Span) tracer.buildSpan("some-operation").startManual(); + } + + @Test + public void testInvalidBaggage() { + when(mgr.getRestriction(SERVICE, KEY)).thenReturn(Restriction.of(false, 0)); + + final String value = "value"; + SpanContext ctx = setter.setBaggage(span, KEY, value); + + assertBaggageLogs(span, KEY, value, false, false, true); + assertNull(ctx.getBaggageItem(KEY)); + + assertEquals( + 1L, metricsReporter.counters.get("jaeger.baggage-update.result=err").longValue()); + } + + @Test + public void testTruncatedBaggage() { + when(mgr.getRestriction(SERVICE, KEY)).thenReturn(Restriction.of(true, 5)); + final String value = "0123456789"; + final String expected = "01234"; + SpanContext ctx = setter.setBaggage(span, KEY, value); + + assertBaggageLogs(span, KEY, expected, true, false, false); + assertEquals(expected, ctx.getBaggageItem(KEY)); + + assertEquals( + 1L, metricsReporter.counters.get("jaeger.baggage-truncate").longValue()); + assertEquals( + 1L, metricsReporter.counters.get("jaeger.baggage-update.result=ok").longValue()); + } + + @Test + public void testOverrideBaggage() { + when(mgr.getRestriction(SERVICE, KEY)).thenReturn(Restriction.of(true, 5)); + final String value = "value"; + SpanContext ctx = setter.setBaggage(span, KEY, value); + Span child = (Span) tracer.buildSpan("some-operation").asChildOf(ctx).startManual(); + ctx = setter.setBaggage(child, KEY, value); + + assertBaggageLogs(child, KEY, value, false, true, false); + assertEquals(value, ctx.getBaggageItem(KEY)); + + assertEquals( + 2L, metricsReporter.counters.get("jaeger.baggage-update.result=ok").longValue()); + } + + @Test + public void testUnsampledSpan() { + tracer = + new Tracer.Builder("SamplerTest", reporter, new ConstSampler(false)) + .withStatsReporter(metricsReporter) + .build(); + span = (Span) tracer.buildSpan("some-operation").startManual(); + + when(mgr.getRestriction(SERVICE, KEY)).thenReturn(Restriction.of(true, 5)); + final String value = "value"; + SpanContext ctx = setter.setBaggage(span, KEY, value); + + assertEquals(value, ctx.getBaggageItem(KEY)); + // No logs should be written if the span is not sampled + assertNull(span.getLogs()); + } + + private void assertBaggageLogs( + Span span, + String key, + String value, + boolean truncate, + boolean override, + boolean invalid + ) { + List logs = span.getLogs(); + assertEquals(false, logs.isEmpty()); + Map fields = logs.get(logs.size() - 1).getFields(); + assertEquals("baggage", fields.get("event")); + assertEquals(key, fields.get("key")); + assertEquals(value, fields.get("value")); + if (truncate) { + assertEquals("true", fields.get("truncated")); + } + if (override) { + assertEquals("true", fields.get("override")); + } + if (invalid) { + assertEquals("true", fields.get("invalid")); + } + } +} diff --git a/jaeger-core/src/test/java/com/uber/jaeger/baggage/DefaultBaggageRestrictionManagerTest.java b/jaeger-core/src/test/java/com/uber/jaeger/baggage/DefaultBaggageRestrictionManagerTest.java new file mode 100644 index 000000000..decda42aa --- /dev/null +++ b/jaeger-core/src/test/java/com/uber/jaeger/baggage/DefaultBaggageRestrictionManagerTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017, Uber Technologies, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.uber.jaeger.baggage; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +import com.uber.jaeger.metrics.Metrics; +import com.uber.jaeger.metrics.NullStatsReporter; +import com.uber.jaeger.metrics.StatsFactoryImpl; +import org.junit.Test; + +public class DefaultBaggageRestrictionManagerTest { + + @Test + public void testGetRestriction() { + final DefaultBaggageRestrictionManager undertest = new DefaultBaggageRestrictionManager(); + + final String key = "key"; + Restriction actual = undertest.getRestriction("", key); + Restriction expected = Restriction.of(true, 2048); + assertEquals(expected, actual); + + expected = actual; + actual = undertest.getRestriction("", key); + assertSame(actual, expected); + } +}