Add unit to start, end and event timestamp accepting methods in Span. (#1969)

* Add unit to timestamp accepting methods in Span.

* end timestamp

* Merge

* Merge

* Add Instant versions too
This commit is contained in:
Anuraag Agrawal 2020-11-10 09:19:52 +09:00 committed by GitHub
parent 9063c385d9
commit 1883c578bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 310 additions and 74 deletions

View File

@ -10,6 +10,7 @@ import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.internal.Utils;
import io.opentelemetry.context.Context;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
@ -107,7 +108,7 @@ final class DefaultTracer implements Tracer {
}
@Override
public NoopSpanBuilder setStartTimestamp(long startTimestamp) {
public NoopSpanBuilder setStartTimestamp(long startTimestamp, TimeUnit unit) {
Utils.checkArgument(startTimestamp >= 0, "Negative startTimestamp");
return this;
}

View File

@ -7,6 +7,7 @@ package io.opentelemetry.api.trace;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.Immutable;
/**
@ -71,7 +72,7 @@ final class PropagatedSpan implements Span {
}
@Override
public Span addEvent(String name, long timestamp) {
public Span addEvent(String name, long timestamp, TimeUnit unit) {
return this;
}
@ -81,7 +82,7 @@ final class PropagatedSpan implements Span {
}
@Override
public Span addEvent(String name, Attributes attributes, long timestamp) {
public Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) {
return this;
}
@ -114,7 +115,7 @@ final class PropagatedSpan implements Span {
public void end() {}
@Override
public void end(long timestamp) {}
public void end(long timestamp, TimeUnit unit) {}
@Override
public SpanContext getSpanContext() {

View File

@ -5,10 +5,15 @@
package io.opentelemetry.api.trace;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ImplicitContextKeyed;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
@ -199,10 +204,32 @@ public interface Span extends ImplicitContextKeyed {
* occurred.
*
* @param name the name of the event.
* @param timestamp the explicit event timestamp in nanos since epoch.
* @param timestamp the explicit event timestamp since epoch.
* @param unit the unit of the timestamp
* @return this.
*/
Span addEvent(String name, long timestamp);
Span addEvent(String name, long timestamp, TimeUnit unit);
/**
* Adds an event to the {@link Span} with the given {@code timestamp}, as nanos since epoch. Note,
* this {@code timestamp} is not the same as {@link System#nanoTime()} but may be computed using
* it, for example, by taking a difference of readings from {@link System#nanoTime()} and adding
* to the span start time.
*
* <p>When possible, it is preferred to use {@link #addEvent(String)} at the time the event
* occurred.
*
* @param name the name of the event.
* @param timestamp the explicit event timestamp since epoch.
* @return this.
*/
default Span addEvent(String name, Instant timestamp) {
if (timestamp == null) {
return addEvent(name);
}
return addEvent(
name, SECONDS.toNanos(timestamp.getEpochSecond()) + timestamp.getNano(), NANOSECONDS);
}
/**
* Adds an event to the {@link Span} with the given {@link Attributes}. The timestamp of the event
@ -227,10 +254,37 @@ public interface Span extends ImplicitContextKeyed {
* @param name the name of the event.
* @param attributes the attributes that will be added; these are associated with this event, not
* the {@code Span} as for {@code setAttribute()}.
* @param timestamp the explicit event timestamp in nanos since epoch.
* @param timestamp the explicit event timestamp since epoch.
* @param unit the unit of the timestamp
* @return this.
*/
Span addEvent(String name, Attributes attributes, long timestamp);
Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit);
/**
* Adds an event to the {@link Span} with the given {@link Attributes} and {@code timestamp}.
* Note, this {@code timestamp} is not the same as {@link System#nanoTime()} but may be computed
* using it, for example, by taking a difference of readings from {@link System#nanoTime()} and
* adding to the span start time.
*
* <p>When possible, it is preferred to use {@link #addEvent(String)} at the time the event
* occurred.
*
* @param name the name of the event.
* @param attributes the attributes that will be added; these are associated with this event, not
* the {@code Span} as for {@code setAttribute()}.
* @param timestamp the explicit event timestamp since epoch.
* @return this.
*/
default Span addEvent(String name, Attributes attributes, Instant timestamp) {
if (timestamp == null) {
return addEvent(name, attributes);
}
return addEvent(
name,
attributes,
SECONDS.toNanos(timestamp.getEpochSecond()) + timestamp.getNano(),
NANOSECONDS);
}
/**
* Sets the status to the {@code Span}.
@ -312,10 +366,31 @@ public interface Span extends ImplicitContextKeyed {
* <p>Use this method for specifying explicit end options, such as end {@code Timestamp}. When no
* explicit values are required, use {@link #end()}.
*
* @param timestamp the explicit timestamp, as nanos from the epoch, for this {@code Span}. {@code
* 0} indicates current time should be used.
* @param timestamp the explicit timestamp from the epoch, for this {@code Span}. {@code 0}
* indicates current time should be used.
* @param unit the unit of the timestamp
*/
void end(long timestamp);
void end(long timestamp, TimeUnit unit);
/**
* Marks the end of {@code Span} execution with the specified timestamp.
*
* <p>Only the timing of the first end call for a given {@code Span} will be recorded, and
* implementations are free to ignore all further calls.
*
* <p>Use this method for specifying explicit end options, such as end {@code Timestamp}. When no
* explicit values are required, use {@link #end()}.
*
* @param timestamp the explicit timestamp from the epoch, for this {@code Span}. {@code 0}
* indicates current time should be used.
*/
default void end(Instant timestamp) {
if (timestamp == null) {
end();
return;
}
end(SECONDS.toNanos(timestamp.getEpochSecond()) + timestamp.getNano(), NANOSECONDS);
}
/**
* Returns the {@code SpanContext} associated with this {@code Span}.
@ -570,11 +645,32 @@ public interface Span extends ImplicitContextKeyed {
*
* <p>Important this is NOT equivalent with System.nanoTime().
*
* @param startTimestamp the explicit start timestamp of the newly created {@code Span} in nanos
* since epoch.
* @param startTimestamp the explicit start timestamp from the epoch of the newly created {@code
* Span}.
* @param unit the unit of the timestamp.
* @return this.
*/
Builder setStartTimestamp(long startTimestamp);
Builder setStartTimestamp(long startTimestamp, TimeUnit unit);
/**
* Sets an explicit start timestamp for the newly created {@code Span}.
*
* <p>Use this method to specify an explicit start timestamp. If not called, the implementation
* will use the timestamp value at {@link #startSpan()} time, which should be the default case.
*
* <p>Important this is NOT equivalent with System.nanoTime().
*
* @param startTimestamp the explicit start timestamp from the epoch of the newly created {@code
* Span}.
* @return this.
*/
default Builder setStartTimestamp(Instant startTimestamp) {
if (startTimestamp == null) {
return this;
}
return setStartTimestamp(
SECONDS.toNanos(startTimestamp.getEpochSecond()) + startTimestamp.getNano(), NANOSECONDS);
}
/**
* Starts a new {@link Span}.

View File

@ -15,6 +15,8 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.api.common.Attributes;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
class PropagatedSpanTest {
@ -41,15 +43,18 @@ class PropagatedSpanTest {
span.setAttribute(doubleArrayKey("NullArrayDouble"), null);
span.setAttribute((String) null, null);
span.addEvent("event");
span.addEvent("event", 0);
span.addEvent("event", 0, TimeUnit.NANOSECONDS);
span.addEvent("event", Instant.EPOCH);
span.addEvent("event", Attributes.of(booleanKey("MyBooleanAttributeKey"), true));
span.addEvent("event", Attributes.of(booleanKey("MyBooleanAttributeKey"), true), 0);
span.addEvent(
"event", Attributes.of(booleanKey("MyBooleanAttributeKey"), true), 0, TimeUnit.NANOSECONDS);
span.setStatus(StatusCode.OK);
span.setStatus(StatusCode.OK, "null");
span.recordException(new IllegalStateException());
span.recordException(new IllegalStateException(), Attributes.empty());
span.end();
span.end(0);
span.end(0, TimeUnit.NANOSECONDS);
span.end(Instant.EPOCH);
}
@Test

View File

@ -12,6 +12,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span.Kind;
import io.opentelemetry.context.Context;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link Span.Builder}. */
@ -32,7 +34,8 @@ class SpanBuilderTest {
spanBuilder.setAttribute("key", .12345);
spanBuilder.setAttribute("key", true);
spanBuilder.setAttribute(stringKey("key"), "value");
spanBuilder.setStartTimestamp(12345L);
spanBuilder.setStartTimestamp(12345L, TimeUnit.NANOSECONDS);
spanBuilder.setStartTimestamp(Instant.EPOCH);
assertThat(spanBuilder.startSpan().getSpanContext().isValid()).isFalse();
}
@ -47,7 +50,7 @@ class SpanBuilderTest {
Span.Builder spanBuilder = tracer.spanBuilder("MySpanName");
assertThrows(
IllegalArgumentException.class,
() -> spanBuilder.setStartTimestamp(-1),
() -> spanBuilder.setStartTimestamp(-1, TimeUnit.NANOSECONDS),
"Negative startTimestamp");
}
}

View File

@ -82,7 +82,8 @@ class SpanConverter {
.spanBuilder(ocSpanData.getName())
.setStartTimestamp(
TimeUnit.SECONDS.toNanos(ocSpanData.getStartTimestamp().getSeconds())
+ ocSpanData.getStartTimestamp().getNanos());
+ ocSpanData.getStartTimestamp().getNanos(),
TimeUnit.NANOSECONDS);
if (ocSpanData.getKind() != null) {
builder.setSpanKind(mapKind(ocSpanData.getKind()));
}
@ -167,7 +168,8 @@ class SpanConverter {
AttributeKey.longKey(MESSAGE_EVENT_ATTRIBUTE_KEY_SIZE_COMPRESSED),
event.getEvent().getCompressedMessageSize()),
TimeUnit.SECONDS.toNanos(event.getTimestamp().getSeconds())
+ event.getTimestamp().getNanos());
+ event.getTimestamp().getNanos(),
TimeUnit.NANOSECONDS);
}
}
@ -190,7 +192,8 @@ class SpanConverter {
annotation.getEvent().getDescription(),
attributesBuilder.build(),
TimeUnit.SECONDS.toNanos(annotation.getTimestamp().getSeconds())
+ annotation.getTimestamp().getNanos());
+ annotation.getTimestamp().getNanos(),
TimeUnit.NANOSECONDS);
}
}

View File

@ -105,7 +105,8 @@ class SpanConverterTest {
"First annotation!",
Attributes.builder().put("Attribute1", false).put("Attribute2", 123).build(),
TimeUnit.SECONDS.toNanos(annotations.get(0).getTimestamp().getSeconds())
+ annotations.get(0).getTimestamp().getNanos());
+ annotations.get(0).getTimestamp().getNanos(),
TimeUnit.NANOSECONDS);
verify(spanSpy, times(1))
.addEvent(
"Second annotation!",
@ -114,7 +115,8 @@ class SpanConverterTest {
.put("Attribute2", "attributeValue")
.build(),
TimeUnit.SECONDS.toNanos(annotations.get(1).getTimestamp().getSeconds())
+ annotations.get(1).getTimestamp().getNanos());
+ annotations.get(1).getTimestamp().getNanos(),
TimeUnit.NANOSECONDS);
}
@Test
@ -145,7 +147,8 @@ class SpanConverterTest {
.put("message.event.size.compressed", 34)
.build(),
TimeUnit.SECONDS.toNanos(messageEvents.get(0).getTimestamp().getSeconds())
+ messageEvents.get(0).getTimestamp().getNanos());
+ messageEvents.get(0).getTimestamp().getNanos(),
TimeUnit.NANOSECONDS);
verify(spanSpy, times(1))
.addEvent(
"8",
@ -155,7 +158,8 @@ class SpanConverterTest {
.put("message.event.size.compressed", 180)
.build(),
TimeUnit.SECONDS.toNanos(messageEvents.get(1).getTimestamp().getSeconds())
+ messageEvents.get(1).getTimestamp().getNanos());
+ messageEvents.get(1).getTimestamp().getNanos(),
TimeUnit.NANOSECONDS);
}
private static RecordEventsSpanImpl createOpenCensusSpan() {

View File

@ -20,6 +20,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -59,8 +60,9 @@ class TracezZPageHandlerTest {
Span runningSpan = tracer.spanBuilder(RUNNING_SPAN).startSpan();
Span latencySpan = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpan.end(10002);
Span latencySpan =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpan.end(10002, TimeUnit.NANOSECONDS);
Span errorSpan = tracer.spanBuilder(ERROR_SPAN).startSpan();
errorSpan.setStatus(StatusCode.ERROR);
@ -142,32 +144,41 @@ class TracezZPageHandlerTest {
void summaryTable_linkForLatencyBasedSpans_OnePerBoundary() {
OutputStream output = new ByteArrayOutputStream();
// Boundary 0, >1us
Span latencySpanSubtype0 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpanSubtype0.end(1002);
Span latencySpanSubtype0 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpanSubtype0.end(1002, TimeUnit.NANOSECONDS);
// Boundary 1, >10us
Span latencySpanSubtype1 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpanSubtype1.end(10002);
Span latencySpanSubtype1 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpanSubtype1.end(10002, TimeUnit.NANOSECONDS);
// Boundary 2, >100us
Span latencySpanSubtype2 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpanSubtype2.end(100002);
Span latencySpanSubtype2 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpanSubtype2.end(100002, TimeUnit.NANOSECONDS);
// Boundary 3, >1ms
Span latencySpanSubtype3 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpanSubtype3.end(1000002);
Span latencySpanSubtype3 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpanSubtype3.end(1000002, TimeUnit.NANOSECONDS);
// Boundary 4, >10ms
Span latencySpanSubtype4 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpanSubtype4.end(10000002);
Span latencySpanSubtype4 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpanSubtype4.end(10000002, TimeUnit.NANOSECONDS);
// Boundary 5, >100ms
Span latencySpanSubtype5 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpanSubtype5.end(100000002);
Span latencySpanSubtype5 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpanSubtype5.end(100000002, TimeUnit.NANOSECONDS);
// Boundary 6, >1s
Span latencySpanSubtype6 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpanSubtype6.end(1000000002);
Span latencySpanSubtype6 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpanSubtype6.end(1000000002, TimeUnit.NANOSECONDS);
// Boundary 7, >10s
Span latencySpanSubtype7 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpanSubtype7.end(10000000002L);
Span latencySpanSubtype7 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpanSubtype7.end(10000000002L, TimeUnit.NANOSECONDS);
// Boundary 8, >100s
Span latencySpanSubtype8 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpanSubtype8.end(100000000002L);
Span latencySpanSubtype8 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpanSubtype8.end(100000000002L, TimeUnit.NANOSECONDS);
TracezZPageHandler tracezZPageHandler = new TracezZPageHandler(dataAggregator);
tracezZPageHandler.emitHtml(emptyQueryMap, output);
@ -205,14 +216,18 @@ class TracezZPageHandlerTest {
void summaryTable_linkForLatencyBasedSpans_MultipleForOneBoundary() {
OutputStream output = new ByteArrayOutputStream();
// 4 samples in boundary 5, >100ms
Span latencySpan100ms1 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpan100ms1.end(112931232L);
Span latencySpan100ms2 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpan100ms2.end(138694322L);
Span latencySpan100ms3 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpan100ms3.end(154486482L);
Span latencySpan100ms4 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpan100ms4.end(194892582L);
Span latencySpan100ms1 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpan100ms1.end(112931232L, TimeUnit.NANOSECONDS);
Span latencySpan100ms2 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpan100ms2.end(138694322L, TimeUnit.NANOSECONDS);
Span latencySpan100ms3 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpan100ms3.end(154486482L, TimeUnit.NANOSECONDS);
Span latencySpan100ms4 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpan100ms4.end(194892582L, TimeUnit.NANOSECONDS);
TracezZPageHandler tracezZPageHandler = new TracezZPageHandler(dataAggregator);
tracezZPageHandler.emitHtml(emptyQueryMap, output);
@ -270,10 +285,12 @@ class TracezZPageHandlerTest {
@Test
void spanDetails_emitLatencySpanDetailsCorrectly() {
OutputStream output = new ByteArrayOutputStream();
Span latencySpan1 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpan1.end(10002);
Span latencySpan2 = tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L).startSpan();
latencySpan2.end(10002);
Span latencySpan1 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpan1.end(10002, TimeUnit.NANOSECONDS);
Span latencySpan2 =
tracer.spanBuilder(LATENCY_SPAN).setStartTimestamp(1L, TimeUnit.NANOSECONDS).startSpan();
latencySpan2.end(10002, TimeUnit.NANOSECONDS);
Map<String, String> queryMap =
ImmutableMap.of("zspanname", LATENCY_SPAN, "ztype", "1", "zsubtype", "1");

View File

@ -31,6 +31,7 @@ import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
@ -317,11 +318,11 @@ final class RecordEventsReadableSpan implements ReadWriteSpan {
}
@Override
public ReadWriteSpan addEvent(String name, long timestamp) {
public ReadWriteSpan addEvent(String name, long timestamp, TimeUnit unit) {
if (name == null) {
return this;
}
addTimedEvent(Event.create(timestamp, name, Attributes.empty(), 0));
addTimedEvent(Event.create(unit.toNanos(timestamp), name, Attributes.empty(), 0));
return this;
}
@ -341,14 +342,14 @@ final class RecordEventsReadableSpan implements ReadWriteSpan {
}
@Override
public ReadWriteSpan addEvent(String name, Attributes attributes, long timestamp) {
public ReadWriteSpan addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) {
if (name == null) {
return this;
}
int totalAttributeCount = attributes.size();
addTimedEvent(
Event.create(
timestamp,
unit.toNanos(timestamp),
name,
copyAndLimitAttributes(attributes, traceConfig.getMaxNumberOfAttributesPerEvent()),
totalAttributeCount));
@ -408,7 +409,7 @@ final class RecordEventsReadableSpan implements ReadWriteSpan {
if (exception == null) {
return this;
}
long timestamp = clock.now();
long timestampNanos = clock.now();
Attributes.Builder attributes = Attributes.builder();
attributes.put(SemanticAttributes.EXCEPTION_TYPE, exception.getClass().getCanonicalName());
@ -423,7 +424,11 @@ final class RecordEventsReadableSpan implements ReadWriteSpan {
attributes.putAll(additionalAttributes);
}
addEvent(SemanticAttributes.EXCEPTION_EVENT_NAME, attributes.build(), timestamp);
addEvent(
SemanticAttributes.EXCEPTION_EVENT_NAME,
attributes.build(),
timestampNanos,
TimeUnit.NANOSECONDS);
return this;
}
@ -448,8 +453,11 @@ final class RecordEventsReadableSpan implements ReadWriteSpan {
}
@Override
public void end(long timestamp) {
endInternal(timestamp == 0 ? clock.now() : timestamp);
public void end(long timestamp, TimeUnit unit) {
if (unit == null) {
unit = TimeUnit.NANOSECONDS;
}
endInternal(timestamp == 0 ? clock.now() : unit.toNanos(timestamp));
}
private void endInternal(long endEpochNanos) {

View File

@ -34,6 +34,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
/** {@link SpanBuilderSdk} is SDK implementation of {@link Span.Builder}. */
@ -165,9 +166,9 @@ final class SpanBuilderSdk implements Span.Builder {
}
@Override
public Span.Builder setStartTimestamp(long startTimestamp) {
public Span.Builder setStartTimestamp(long startTimestamp, TimeUnit unit) {
Utils.checkArgument(startTimestamp >= 0, "Negative startTimestamp");
startEpochNanos = startTimestamp;
startEpochNanos = unit.toNanos(startTimestamp);
return this;
}

View File

@ -38,6 +38,7 @@ import io.opentelemetry.sdk.trace.data.SpanData.Link;
import io.opentelemetry.sdk.trace.data.SpanData.Status;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@ -481,11 +482,62 @@ class RecordEventsReadableSpanTest {
try {
span.addEvent("event1");
span.addEvent("event2", Attributes.of(stringKey("e1key"), "e1Value"));
span.addEvent("event3", 10, TimeUnit.SECONDS);
span.addEvent("event4", Instant.ofEpochSecond(20));
span.addEvent(
"event5", Attributes.builder().put("foo", "bar").build(), 30, TimeUnit.MILLISECONDS);
span.addEvent(
"event6", Attributes.builder().put("foo", "bar").build(), Instant.ofEpochMilli(1000));
} finally {
span.end();
}
List<Event> events = span.toSpanData().getEvents();
assertThat(events.size()).isEqualTo(2);
assertThat(events).hasSize(6);
assertThat(events.get(0))
.satisfies(
event -> {
assertThat(event.getName()).isEqualTo("event1");
assertThat(event.getAttributes()).isEqualTo(Attributes.empty());
assertThat(event.getEpochNanos()).isEqualTo(START_EPOCH_NANOS);
});
assertThat(events.get(1))
.satisfies(
event -> {
assertThat(event.getName()).isEqualTo("event2");
assertThat(event.getAttributes())
.isEqualTo(Attributes.of(stringKey("e1key"), "e1Value"));
assertThat(event.getEpochNanos()).isEqualTo(START_EPOCH_NANOS);
});
assertThat(events.get(2))
.satisfies(
event -> {
assertThat(event.getName()).isEqualTo("event3");
assertThat(event.getAttributes()).isEqualTo(Attributes.empty());
assertThat(event.getEpochNanos()).isEqualTo(TimeUnit.SECONDS.toNanos(10));
});
assertThat(events.get(3))
.satisfies(
event -> {
assertThat(event.getName()).isEqualTo("event4");
assertThat(event.getAttributes()).isEqualTo(Attributes.empty());
assertThat(event.getEpochNanos()).isEqualTo(TimeUnit.SECONDS.toNanos(20));
});
assertThat(events.get(4))
.satisfies(
event -> {
assertThat(event.getName()).isEqualTo("event5");
assertThat(event.getAttributes())
.isEqualTo(Attributes.builder().put("foo", "bar").build());
assertThat(event.getEpochNanos()).isEqualTo(TimeUnit.MILLISECONDS.toNanos(30));
});
assertThat(events.get(5))
.satisfies(
event -> {
assertThat(event.getName()).isEqualTo("event6");
assertThat(event.getAttributes())
.isEqualTo(Attributes.builder().put("foo", "bar").build());
assertThat(event.getEpochNanos()).isEqualTo(TimeUnit.MILLISECONDS.toNanos(1000));
});
}
@Test
@ -511,6 +563,20 @@ class RecordEventsReadableSpanTest {
assertThat(spanData.getTotalAttributeCount()).isEqualTo(2 * maxNumberOfAttributes);
}
@Test
void endWithTimestamp_numeric() {
RecordEventsReadableSpan span1 = createTestRootSpan();
span1.end(10, TimeUnit.NANOSECONDS);
assertThat(span1.toSpanData().getEndEpochNanos()).isEqualTo(10);
}
@Test
void endWithTimestamp_instant() {
RecordEventsReadableSpan span1 = createTestRootSpan();
span1.end(Instant.ofEpochMilli(10));
assertThat(span1.toSpanData().getEndEpochNanos()).isEqualTo(TimeUnit.MILLISECONDS.toNanos(10));
}
@Test
void droppingAndAddingAttributes() {
final int maxNumberOfAttributes = 8;
@ -690,11 +756,15 @@ class RecordEventsReadableSpanTest {
span.setStatus(null, null);
span.updateName(null);
span.addEvent(null);
span.addEvent(null, 0);
span.addEvent(null, null);
span.addEvent(null, null, 0);
span.addEvent(null, 0, null);
span.addEvent(null, (Attributes) null);
span.addEvent(null, (Instant) null);
span.addEvent(null, null, 0, null);
span.addEvent(null, null, null);
span.recordException(null);
span.end(0);
span.end(0, TimeUnit.NANOSECONDS);
span.end(1, null);
span.end(null);
// Ignored the bad calls
SpanData data = span.toSpanData();

View File

@ -34,9 +34,11 @@ import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.data.SpanData.Link;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.opentelemetry.sdk.trace.samplers.SamplingResult;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -793,11 +795,36 @@ class SpanBuilderSdkTest {
}
}
@Test
void startTimestamp_numeric() {
RecordEventsReadableSpan span =
(RecordEventsReadableSpan)
tracerSdk
.spanBuilder(SPAN_NAME)
.setStartTimestamp(10, TimeUnit.NANOSECONDS)
.startSpan();
span.end();
assertThat(span.toSpanData().getStartEpochNanos()).isEqualTo(10);
}
@Test
void startTimestamp_instant() {
RecordEventsReadableSpan span =
(RecordEventsReadableSpan)
tracerSdk
.spanBuilder(SPAN_NAME)
.setStartTimestamp(Instant.ofEpochMilli(100))
.startSpan();
span.end();
assertThat(span.toSpanData().getStartEpochNanos())
.isEqualTo(TimeUnit.MILLISECONDS.toNanos(100));
}
@Test
void startTimestamp_null() {
assertThrows(
IllegalArgumentException.class,
() -> tracerSdk.spanBuilder(SPAN_NAME).setStartTimestamp(-1),
() -> tracerSdk.spanBuilder(SPAN_NAME).setStartTimestamp(-1, TimeUnit.NANOSECONDS),
"Negative startTimestamp");
}