diff --git a/README.md b/README.md index 0418cf8..5833959 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,62 @@ This project provides API extensions to the core OpenTracing APIs. -## Observer +## APIs -TODO: Provide some code snippets for use of Observer API. +The APIs can be included using the following: + +```xml + + io.opentracing.contrib + opentracing-api-extensions + + +``` + +### Observer + +The Observer API can be used to monitor Span related activity and perform additional +tasks. + +There are two types of Observer, stateful and stateless. + +* A stateful observer will use +identity information supplied with the `SpanData`, to maintain state information about a particular +`Span` instance - and at the appropriate time perform some action using the accumulated information. + +* A stateless observer will not maintain any local state information about the `Span` instances, and +instead perform tasks directly in the callback that provides the event/information of interest (e.g. recording +metrics `onFinish` or logging events when `onLog` is called). + +The benefit of a stateless approach is that the same observer instance (i.e. singleton) can be used for +all `Span` instances. Whereas the stateful approach will require an observer instance to be instantiated +for each call to `TracerObserver.onStart()`. + + +## Registering API extensions + +There are two ways an extension API can be registered for use with a `Tracer`. + +1) Native support within the `Tracer` implementation + +Some `Tracer` implementations may decide to implement support for the extension APIs directly, in which +case an implementation specific mechanism will be provided for registering the APIs. + +2) Using the extension `Tracer` wrapper + +```xml + + io.opentracing.contrib + opentracing-api-extensions-tracer + + +``` + +The `io.opentracing.contrib.api.tracer.APIExtensionsTracer` provides a single constructor which is supplied +the `Tracer` instance to be wrapped. + +This class also provides `addTracerObserver` and `removeTracerObserver` methods to enable a `TracerObserver` +instance to be registered with the tracer wrapper, and perform relevant tasks when new spans are started. ## Release diff --git a/opentracing-api-extensions-tracer/pom.xml b/opentracing-api-extensions-tracer/pom.xml new file mode 100644 index 0000000..80399a0 --- /dev/null +++ b/opentracing-api-extensions-tracer/pom.xml @@ -0,0 +1,46 @@ + + + + 4.0.0 + + opentracing-api-extensions-parent + io.opentracing.contrib + 0.0.2-SNAPSHOT + + + opentracing-api-extensions-tracer + + + + io.opentracing.contrib + opentracing-api-extensions + + + + org.mockito + mockito-all + test + + + junit + junit + ${version.junit} + test + + + + diff --git a/opentracing-api-extensions-tracer/src/main/java/io/opentracing/contrib/api/tracer/APIExtensionsSpan.java b/opentracing-api-extensions-tracer/src/main/java/io/opentracing/contrib/api/tracer/APIExtensionsSpan.java new file mode 100644 index 0000000..dec956e --- /dev/null +++ b/opentracing-api-extensions-tracer/src/main/java/io/opentracing/contrib/api/tracer/APIExtensionsSpan.java @@ -0,0 +1,270 @@ +/** + * Copyright 2017 The OpenTracing 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 io.opentracing.contrib.api.tracer; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; + +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.contrib.api.SpanData; +import io.opentracing.contrib.api.SpanObserver; + +public class APIExtensionsSpan implements Span, SpanData { + + private final Span wrappedSpan; + + private String operationName; + private final long startTimestampMicro; + private long finishTimestampMicro; + private final long startTimeNano; + private long finishTimeNano; + private final Map tags; + + private final List observers = new CopyOnWriteArrayList(); + + private final UUID correlationId = UUID.randomUUID(); + + /** + * This is the constructor for the extensions API span wrapper. + * + * @param span The span being wrapped + * @param operationName The operation name + * @param startTimestampMicro The start timestamp (microseconds) + * @param startTimeNano The start nano time, or 0 if the start timestamp was explicitly provided by the app + * @param tags The initial tags + */ + APIExtensionsSpan(Span span, String operationName, + long startTimestampMicro, long startTimeNano, Map tags) { + this.wrappedSpan = span; + this.operationName = operationName; + this.startTimestampMicro = startTimestampMicro; + this.startTimeNano = startTimeNano; + this.tags = tags; + } + + /** + * This method adds a new {@link SpanObserver}. + * + * @param observer The observer + */ + public void addSpanObserver(SpanObserver observer) { + if (observer != null) { + observers.add(observer); + } + } + + /** + * This method removes a {@link SpanObserver}. + * + * @param observer The observer + */ + public void removeSpanObserver(SpanObserver observer) { + if (observer != null) { + observers.remove(observer); + } + } + + @Override + public SpanContext context() { + return wrappedSpan.context(); + } + + @Override + public Object getCorrelationId() { + return correlationId; + } + + @Override + public long getStartTime() { + return startTimestampMicro; + } + + @Override + public long getFinishTime() { + return finishTimestampMicro; + } + + @Override + public Span setOperationName(String operationName) { + wrappedSpan.setOperationName(operationName); + this.operationName = operationName; + for (SpanObserver observer : observers) { + observer.onSetOperationName(this, operationName); + } + return this; + } + + @Override + public String getOperationName() { + return operationName; + } + + @Override + public String getBaggageItem(String name) { + return wrappedSpan.getBaggageItem(name); + } + + @Override + public Span setBaggageItem(String name, String value) { + wrappedSpan.setBaggageItem(name, value); + for (SpanObserver observer : observers) { + observer.onSetBaggageItem(this, name, value); + } + return this; + } + + @Override + public Span log(Map fields) { + wrappedSpan.log(fields); + return handleLog(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()), fields); + } + + @Override + public Span log(long timestampMicroseconds, Map fields) { + wrappedSpan.log(timestampMicroseconds, fields); + return handleLog(timestampMicroseconds, fields); + } + + private Span handleLog(long timestampMicroseconds, Map fields) { + for (SpanObserver observer : observers) { + observer.onLog(this, timestampMicroseconds, fields); + } + return this; + } + + @Override + public Span log(String event) { + wrappedSpan.log(event); + return handleLog(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()), event); + } + + @Override + public Span log(long timestampMicroseconds, String event) { + wrappedSpan.log(timestampMicroseconds, event); + return handleLog(timestampMicroseconds, event); + } + + private Span handleLog(long timestampMicroseconds, String event) { + for (SpanObserver observer : observers) { + observer.onLog(this, timestampMicroseconds, event); + } + return this; + } + + @Override + public Span setTag(String key, String value) { + wrappedSpan.setTag(key, value); + return handleSetTag(key, value); + } + + @Override + public Span setTag(String key, boolean value) { + wrappedSpan.setTag(key, value); + return handleSetTag(key, value); + } + + @Override + public Span setTag(String key, Number value) { + wrappedSpan.setTag(key, value); + return handleSetTag(key, value); + } + + private Span handleSetTag(String key, Object value) { + tags.put(key, value); + for (SpanObserver observer : observers) { + observer.onSetTag(this, key, value); + } + return this; + } + + @Override + public Map getTags() { + return Collections.unmodifiableMap(tags); + } + + @Override + public String getStringTag(String key) { + Object value = tags.get(key); + if (value instanceof String) { + return (String) value; + } + return null; + } + + @Override + public Number getNumberTag(String key) { + Object value = tags.get(key); + if (value instanceof Number) { + return (Number) value; + } + return null; + } + + @Override + public Boolean getBooleanTag(String key) { + Object value = tags.get(key); + if (value instanceof Boolean) { + return (Boolean) value; + } + return null; + } + + @Override + public void finish() { + wrappedSpan.finish(); + // Only set the finish nano time if not explicitly providing a timestamp + finishTimeNano = System.nanoTime(); + handleFinish(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())); + } + + @Override + public void finish(long finishMicros) { + wrappedSpan.finish(finishMicros); + handleFinish(finishMicros); + } + + private void handleFinish(long finishMicros) { + finishTimestampMicro = finishMicros; + for (SpanObserver observer : observers) { + observer.onFinish(this, finishMicros); + } + } + + @Override + public long getDuration() { + // If start or finish nano times are not available, then use timestamps + if (startTimeNano == 0 || finishTimeNano == 0) { + return finishTimestampMicro == 0 ? 0 : finishTimestampMicro - startTimestampMicro; + } + return TimeUnit.NANOSECONDS.toMicros(finishTimeNano - startTimeNano); + } + + @SuppressWarnings("deprecation") + @Override + public Span log(String eventName, Object payload) { + return wrappedSpan.log(eventName, payload); + } + + @SuppressWarnings("deprecation") + @Override + public Span log(long timestampMicroseconds, String eventName, Object payload) { + return wrappedSpan.log(timestampMicroseconds, eventName, payload); + } + +} diff --git a/opentracing-api-extensions-tracer/src/main/java/io/opentracing/contrib/api/tracer/APIExtensionsSpanBuilder.java b/opentracing-api-extensions-tracer/src/main/java/io/opentracing/contrib/api/tracer/APIExtensionsSpanBuilder.java new file mode 100644 index 0000000..f1ca441 --- /dev/null +++ b/opentracing-api-extensions-tracer/src/main/java/io/opentracing/contrib/api/tracer/APIExtensionsSpanBuilder.java @@ -0,0 +1,130 @@ +/** + * Copyright 2017 The OpenTracing 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 io.opentracing.contrib.api.tracer; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +import io.opentracing.ActiveSpan; +import io.opentracing.BaseSpan; +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.Tracer.SpanBuilder; +import io.opentracing.contrib.api.TracerObserver; + +public class APIExtensionsSpanBuilder implements SpanBuilder { + + private final Tracer tracer; + private final List observers; + + private final String operationName; + private final SpanBuilder wrappedBuilder; + private long startTimestampMicro = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); + private long startTimeNano = System.nanoTime(); + private final Map tags = new ConcurrentHashMap(); + + APIExtensionsSpanBuilder(Tracer tracer, List observers, + String operationName, SpanBuilder builder) { + this.tracer = tracer; + this.observers = observers; + this.operationName = operationName; + this.wrappedBuilder = builder; + } + + @Override + public SpanBuilder asChildOf(SpanContext parent) { + wrappedBuilder.asChildOf(parent); + return this; + } + + @Override + public SpanBuilder asChildOf(BaseSpan parent) { + wrappedBuilder.asChildOf(parent); + return this; + } + + @Override + public SpanBuilder addReference(String referenceType, SpanContext referencedContext) { + wrappedBuilder.addReference(referenceType, referencedContext); + return this; + } + + @Override + public SpanBuilder ignoreActiveSpan() { + wrappedBuilder.ignoreActiveSpan(); + return this; + } + + @Override + public SpanBuilder withTag(String key, String value) { + tags.put(key, value); + wrappedBuilder.withTag(key, value); + return this; + } + + @Override + public SpanBuilder withTag(String key, boolean value) { + tags.put(key, value); + wrappedBuilder.withTag(key, value); + return this; + } + + @Override + public SpanBuilder withTag(String key, Number value) { + tags.put(key, value); + wrappedBuilder.withTag(key, value); + return this; + } + + @Override + public SpanBuilder withStartTimestamp(long microseconds) { + wrappedBuilder.withStartTimestamp(microseconds); + // Reset the nano start time, so that duration will be calculated based on explicitly + // provided timestamps + this.startTimeNano = 0; + return this; + } + + @Override + public ActiveSpan startActive() { + return tracer.makeActive(startManual()); + } + + @Override + public Span startManual() { + APIExtensionsSpan span = new APIExtensionsSpan(wrappedBuilder.startManual(), + operationName, startTimestampMicro, startTimeNano, tags); + for (TracerObserver observer : observers) { + span.addSpanObserver(observer.onStart(span)); + } + return span; + } + + @Override + public Span start() { + return startManual(); + } + + Map tags() { + return tags; + } + + long startTimeNano() { + return startTimeNano; + } + +} diff --git a/opentracing-api-extensions-tracer/src/main/java/io/opentracing/contrib/api/tracer/APIExtensionsTracer.java b/opentracing-api-extensions-tracer/src/main/java/io/opentracing/contrib/api/tracer/APIExtensionsTracer.java new file mode 100644 index 0000000..000551b --- /dev/null +++ b/opentracing-api-extensions-tracer/src/main/java/io/opentracing/contrib/api/tracer/APIExtensionsTracer.java @@ -0,0 +1,71 @@ +/** + * Copyright 2017 The OpenTracing 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 io.opentracing.contrib.api.tracer; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import io.opentracing.ActiveSpan; +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.contrib.api.TracerObserver; +import io.opentracing.propagation.Format; + +public class APIExtensionsTracer implements Tracer { + + private final Tracer wrappedTracer; + private final List observers = new CopyOnWriteArrayList(); + + public APIExtensionsTracer(Tracer tracer) { + this.wrappedTracer = tracer; + } + + public void addTracerObserver(TracerObserver observer) { + if (observer != null) { + observers.add(observer); + } + } + + public void removeTracerObserver(TracerObserver observer) { + if (observer != null) { + observers.remove(observer); + } + } + + @Override + public ActiveSpan activeSpan() { + return wrappedTracer.activeSpan(); + } + + @Override + public ActiveSpan makeActive(Span span) { + return wrappedTracer.makeActive(span); + } + + @Override + public SpanBuilder buildSpan(String operation) { + return new APIExtensionsSpanBuilder(wrappedTracer, observers, operation, wrappedTracer.buildSpan(operation)); + } + + @Override + public SpanContext extract(Format format, C carrier) { + return wrappedTracer.extract(format, carrier); + } + + @Override + public void inject(SpanContext context, Format format, C carrier) { + wrappedTracer.inject(context, format, carrier); + } + +} diff --git a/opentracing-api-extensions-tracer/src/test/java/io/opentracing/contrib/api/tracer/APIExtensionsSpanBuilderTest.java b/opentracing-api-extensions-tracer/src/test/java/io/opentracing/contrib/api/tracer/APIExtensionsSpanBuilderTest.java new file mode 100644 index 0000000..98164f4 --- /dev/null +++ b/opentracing-api-extensions-tracer/src/test/java/io/opentracing/contrib/api/tracer/APIExtensionsSpanBuilderTest.java @@ -0,0 +1,165 @@ +/** + * Copyright 2017 The OpenTracing 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 io.opentracing.contrib.api.tracer; + +import static org.junit.Assert.*; + +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import io.opentracing.ActiveSpan; +import io.opentracing.References; +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.Tracer.SpanBuilder; +import io.opentracing.contrib.api.TracerObserver; + +public class APIExtensionsSpanBuilderTest { + + @Captor + private ArgumentCaptor spanCaptor; + + @Before + public void init(){ + MockitoAnnotations.initMocks(this); + } + + @Test + public void testAsChildOfSpanContext() { + TestResources res = new TestResources(); + + res.extSpanBuilder.asChildOf(res.spanContext); + Mockito.verify(res.spanBuilder).asChildOf(res.spanContext); + } + + @Test + public void testAsChildOfSpan() { + TestResources res = new TestResources(); + + res.extSpanBuilder.asChildOf(res.span); + Mockito.verify(res.spanBuilder).asChildOf(res.span); + } + + @Test + public void testAddReference() { + TestResources res = new TestResources(); + + res.extSpanBuilder.addReference(References.FOLLOWS_FROM, res.spanContext); + Mockito.verify(res.spanBuilder).addReference(References.FOLLOWS_FROM, res.spanContext); + } + + @Test + public void testIgnoreActiveSpan() { + TestResources res = new TestResources(); + + res.extSpanBuilder.ignoreActiveSpan(); + Mockito.verify(res.spanBuilder).ignoreActiveSpan(); + } + + @Test + public void testWithTagString() { + TestResources res = new TestResources(); + + res.extSpanBuilder.withTag("tagName", "tagValue"); + Mockito.verify(res.spanBuilder).withTag("tagName", "tagValue"); + assertEquals("tagValue", res.extSpanBuilder.tags().get("tagName")); + } + + @Test + public void testWithTagNumber() { + TestResources res = new TestResources(); + + res.extSpanBuilder.withTag("tagName", 5); + Mockito.verify(res.spanBuilder).withTag("tagName", 5); + assertEquals(5, res.extSpanBuilder.tags().get("tagName")); + } + + @Test + public void testWithTagBoolean() { + TestResources res = new TestResources(); + + res.extSpanBuilder.withTag("tagName", Boolean.TRUE); + Mockito.verify(res.spanBuilder).withTag("tagName", Boolean.TRUE); + assertEquals(Boolean.TRUE, res.extSpanBuilder.tags().get("tagName")); + } + + @Test + public void testWithStartTimestamp() { + TestResources res = new TestResources(); + + // Initially should have value, but will be reset to 0 when timestamp explicitly set + assertNotEquals(0, res.extSpanBuilder.startTimeNano()); + + long ts = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); + res.extSpanBuilder.withStartTimestamp(ts); + Mockito.verify(res.spanBuilder).withStartTimestamp(ts); + + assertEquals(0, res.extSpanBuilder.startTimeNano()); + } + + @Test + public void testStartManual() { + TestResources res = new TestResources(); + + Span manualSpan = res.extSpanBuilder.startManual(); + assertTrue(manualSpan instanceof APIExtensionsSpan); + + APIExtensionsSpan extSpan = (APIExtensionsSpan)manualSpan; + + Mockito.verify(res.observer).onStart(extSpan); + Mockito.verify(res.spanBuilder).startManual(); + assertEquals("op", extSpan.getOperationName()); + } + + @Test + public void testStartActive() { + TestResources res = new TestResources(); + + Mockito.when(res.tracer.makeActive(spanCaptor.capture())).thenReturn(res.activeSpan); + + ActiveSpan extActiveSpan = res.extSpanBuilder.startActive(); + + assertEquals(res.activeSpan, extActiveSpan); + assertTrue(spanCaptor.getValue() instanceof APIExtensionsSpan); + } + + public class TestResources { + public Tracer tracer; + public SpanBuilder spanBuilder; + public TracerObserver observer; + public APIExtensionsSpanBuilder extSpanBuilder; + public SpanContext spanContext; + public Span span; + public ActiveSpan activeSpan; + + public TestResources() { + tracer = Mockito.mock(Tracer.class); + spanBuilder = Mockito.mock(SpanBuilder.class); + observer = Mockito.mock(TracerObserver.class); + extSpanBuilder = new APIExtensionsSpanBuilder(tracer, Collections.singletonList(observer), + "op", spanBuilder); + spanContext = Mockito.mock(SpanContext.class); + span = Mockito.mock(Span.class); + activeSpan = Mockito.mock(ActiveSpan.class); + } + } +} diff --git a/opentracing-api-extensions-tracer/src/test/java/io/opentracing/contrib/api/tracer/APIExtensionsSpanTest.java b/opentracing-api-extensions-tracer/src/test/java/io/opentracing/contrib/api/tracer/APIExtensionsSpanTest.java new file mode 100644 index 0000000..68b29d8 --- /dev/null +++ b/opentracing-api-extensions-tracer/src/test/java/io/opentracing/contrib/api/tracer/APIExtensionsSpanTest.java @@ -0,0 +1,231 @@ +/** + * Copyright 2017 The OpenTracing 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 io.opentracing.contrib.api.tracer; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.verify; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import io.opentracing.Span; +import io.opentracing.contrib.api.SpanObserver; + +public class APIExtensionsSpanTest { + + @Captor + private ArgumentCaptor longCaptor; + + @Before + public void init(){ + MockitoAnnotations.initMocks(this); + } + + @Test + public void testGetDurationFromTimestampsNotFinished() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(null, null, + TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()), 0, null); + synchronized(this) { + wait(100); + } + assertEquals(0, span.getDuration()); + } + + @Test + public void testGetDurationFromTimestampsFinished() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()), 0, null); + synchronized(this) { + wait(100); + } + span.finish(); + assertNotEquals(0, span.getDuration()); + } + + @Test + public void testGetDurationFromNanosNotFinished() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(null, null, + TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()), System.nanoTime(), null); + synchronized(this) { + wait(100); + } + assertEquals(0, span.getDuration()); + } + + @Test + public void testGetDurationFromNanosFinished() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()), System.nanoTime(), null); + synchronized(this) { + wait(100); + } + span.finish(); + assertNotEquals(0, span.getDuration()); + } + + @Test + public void testGetDurationNanosAccuracy() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()), System.nanoTime(), null); + synchronized(this) { + wait(100); + } + span.finish(); + assertNotEquals(0, span.getDuration() % 1000); + } + + @Test + public void testGetDurationExplicitStartMillisAccuracy() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()), 0, null); + synchronized(this) { + wait(100); + } + span.finish(); + assertEquals(0, span.getDuration() % 1000); + } + + @Test + public void testGetDurationExplicitFinishMillisAccuracy() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()), System.nanoTime(), null); + synchronized(this) { + wait(100); + } + span.finish(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())); + assertEquals(0, span.getDuration() % 1000); + } + + @Test + public void testOnSetOperationName() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + 0, 0, null); + SpanObserver observer = Mockito.mock(SpanObserver.class); + span.addSpanObserver(observer); + + span.setOperationName("testop"); + + verify(observer).onSetOperationName(span, "testop"); + } + + @Test + public void testOnSetTagString() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + 0, 0, new HashMap()); + SpanObserver observer = Mockito.mock(SpanObserver.class); + span.addSpanObserver(observer); + + span.setTag("testkey", "testvalue"); + + verify(observer).onSetTag(span, "testkey", "testvalue"); + } + + @Test + public void testOnSetTagNumber() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + 0, 0, new HashMap()); + SpanObserver observer = Mockito.mock(SpanObserver.class); + span.addSpanObserver(observer); + + span.setTag("testkey", 5); + + verify(observer).onSetTag(span, "testkey", 5); + } + + @Test + public void testOnSetTagBoolean() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + 0, 0, new HashMap()); + SpanObserver observer = Mockito.mock(SpanObserver.class); + span.addSpanObserver(observer); + + span.setTag("testkey", Boolean.TRUE); + + verify(observer).onSetTag(span, "testkey", Boolean.TRUE); + } + + @Test + public void testOnSetBaggageItem() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + 0, 0, null); + SpanObserver observer = Mockito.mock(SpanObserver.class); + span.addSpanObserver(observer); + + span.setBaggageItem("testkey", "testvalue"); + + verify(observer).onSetBaggageItem(span, "testkey", "testvalue"); + } + + @Test + public void testOnLogFields() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + 0, 0, null); + SpanObserver observer = Mockito.mock(SpanObserver.class); + span.addSpanObserver(observer); + + long ts = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); + Map fields = new HashMap(); + span.log(ts, fields); + + verify(observer).onLog(span, ts, fields); + } + + @Test + public void testOnLogEvent() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + 0, 0, null); + SpanObserver observer = Mockito.mock(SpanObserver.class); + span.addSpanObserver(observer); + + long ts = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); + span.log(ts, "testevent"); + + verify(observer).onLog(span, ts, "testevent"); + } + + @Test + public void testOnFinish() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + 0, 0, null); + SpanObserver observer = Mockito.mock(SpanObserver.class); + span.addSpanObserver(observer); + + span.finish(); + + verify(observer).onFinish(Mockito.eq(span), longCaptor.capture()); + assertNotEquals(0, longCaptor.getValue().longValue()); + } + + @Test + public void testOnFinishWithTimestamp() throws InterruptedException { + APIExtensionsSpan span = new APIExtensionsSpan(Mockito.mock(Span.class), null, + 0, 0, null); + SpanObserver observer = Mockito.mock(SpanObserver.class); + span.addSpanObserver(observer); + + long ts = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); + span.finish(ts); + + verify(observer).onFinish(span, ts); + } + +} diff --git a/opentracing-api-extensions-tracer/src/test/java/io/opentracing/contrib/api/tracer/APIExtensionsTracerTest.java b/opentracing-api-extensions-tracer/src/test/java/io/opentracing/contrib/api/tracer/APIExtensionsTracerTest.java new file mode 100644 index 0000000..b6766fb --- /dev/null +++ b/opentracing-api-extensions-tracer/src/test/java/io/opentracing/contrib/api/tracer/APIExtensionsTracerTest.java @@ -0,0 +1,59 @@ +/** + * Copyright 2017 The OpenTracing 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 io.opentracing.contrib.api.tracer; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.mockito.Mockito; + +import io.opentracing.ActiveSpan; +import io.opentracing.Span; +import io.opentracing.Tracer; + +public class APIExtensionsTracerTest { + + @Test + public void testActiveSpan() { + Tracer tracer = Mockito.mock(Tracer.class); + ActiveSpan activeSpan = Mockito.mock(ActiveSpan.class); + Mockito.when(tracer.activeSpan()).thenReturn(activeSpan); + + APIExtensionsTracer extTracer = new APIExtensionsTracer(tracer); + assertEquals(activeSpan, extTracer.activeSpan()); + } + + @Test + public void testMakeActive() { + Tracer tracer = Mockito.mock(Tracer.class); + ActiveSpan activeSpan = Mockito.mock(ActiveSpan.class); + Span span = Mockito.mock(Span.class); + Mockito.when(tracer.makeActive(span)).thenReturn(activeSpan); + + APIExtensionsTracer extTracer = new APIExtensionsTracer(tracer); + assertEquals(activeSpan, extTracer.makeActive(span)); + } + + @Test + public void testBuild() { + Tracer tracer = Mockito.mock(Tracer.class); + ActiveSpan activeSpan = Mockito.mock(ActiveSpan.class); + Span span = Mockito.mock(Span.class); + Mockito.when(tracer.makeActive(span)).thenReturn(activeSpan); + + APIExtensionsTracer extTracer = new APIExtensionsTracer(tracer); + extTracer.buildSpan("testop"); + } + +} diff --git a/pom.xml b/pom.xml index ccca178..fd0aa1e 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,7 @@ opentracing-api-extensions + opentracing-api-extensions-tracer @@ -67,6 +68,7 @@ 0.30.0 4.12 + 1.10.19 0.3.4 0.1.0 @@ -84,6 +86,19 @@ opentracing-api ${version.io.opentracing} + + + io.opentracing.contrib + opentracing-api-extensions + ${project.version} + + + + + org.mockito + mockito-all + ${version.org.mockito-mockito-all} +