diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Clock.java b/dd-trace/src/main/java/datadog/trace/tracer/Clock.java index 5286e52de2..1aca0cb0db 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/Clock.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/Clock.java @@ -47,6 +47,15 @@ class Clock { return new Timestamp(this); } + /** + * Create new timestamp instance for current time. + * + * @return new timestamp capturing current time. + */ + public Timestamp createTimestampForTime(final long time, final TimeUnit unit) { + return new Timestamp(this, time, unit); + } + /** * Get the current nanos ticks (i.e. System.nanoTime()), this method can't be use for date * accuracy (only duration calculations). diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Timestamp.java b/dd-trace/src/main/java/datadog/trace/tracer/Timestamp.java index 2ef3197655..a619bd8ccb 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/Timestamp.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/Timestamp.java @@ -3,6 +3,7 @@ package datadog.trace.tracer; import static java.lang.Math.max; import com.fasterxml.jackson.annotation.JsonValue; +import java.util.concurrent.TimeUnit; import lombok.EqualsAndHashCode; /** @@ -26,6 +27,20 @@ public class Timestamp { nanoTicks = clock.nanoTicks(); } + /** + * Create timestamp for a given clock and given start time and precision + * + * @param clock clock instance + */ + Timestamp(final Clock clock, final long time, final TimeUnit unit) { + this.clock = clock; + final long currentTime = clock.epochTimeNano(); + final long currentTick = clock.nanoTicks(); + final long desiredTime = unit.toNanos(time); + final long offset = currentTime - desiredTime; + nanoTicks = currentTick - offset; + } + /** @return clock instance used by this timestamp */ Clock getClock() { return clock; diff --git a/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java b/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java index c3e63f394c..9f153b274e 100644 --- a/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java +++ b/dd-trace/src/main/java/datadog/trace/tracer/Tracer.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; import lombok.Builder; import lombok.extern.slf4j.Slf4j; @@ -74,6 +75,14 @@ public class Tracer implements Closeable { return new Clock(this).createCurrentTimestamp(); } + /** + * @return timestamp for given time. Note: this is mainly useful when there is no 'current' trace. + * If there is 'current' trace already then one should use it to get timestamps. + */ + public Timestamp createTimestampForTime(final long time, final TimeUnit unit) { + return new Clock(this).createTimestampForTime(time, unit); + } + /** * Construct a new trace using this tracer's settings and return the root span. * diff --git a/dd-trace/src/test/groovy/datadog/trace/tracer/ClockTest.groovy b/dd-trace/src/test/groovy/datadog/trace/tracer/ClockTest.groovy index 7c4fcf1639..a19472de12 100644 --- a/dd-trace/src/test/groovy/datadog/trace/tracer/ClockTest.groovy +++ b/dd-trace/src/test/groovy/datadog/trace/tracer/ClockTest.groovy @@ -43,6 +43,17 @@ class ClockTest extends Specification { timestamp.getClock() == clock } + def "test timestamp creation with custom time"() { + setup: + def clock = new Clock(tracer) + + when: + def timestamp = clock.createTimestampForTime(1, TimeUnit.SECONDS) + + then: + timestamp.getClock() == clock + } + def "test equals"() { when: EqualsVerifier.forClass(Clock).suppress(Warning.STRICT_INHERITANCE).verify() diff --git a/dd-trace/src/test/groovy/datadog/trace/tracer/TimestampTest.groovy b/dd-trace/src/test/groovy/datadog/trace/tracer/TimestampTest.groovy index dcbad3de3c..4f3838e7d2 100644 --- a/dd-trace/src/test/groovy/datadog/trace/tracer/TimestampTest.groovy +++ b/dd-trace/src/test/groovy/datadog/trace/tracer/TimestampTest.groovy @@ -5,6 +5,9 @@ import nl.jqno.equalsverifier.EqualsVerifier import nl.jqno.equalsverifier.Warning import spock.lang.Specification +import static java.util.concurrent.TimeUnit.MICROSECONDS +import static java.util.concurrent.TimeUnit.NANOSECONDS + class TimestampTest extends Specification { private static final long CLOCK_START_TIME = 100 @@ -36,11 +39,33 @@ class TimestampTest extends Specification { clock.getStartNanoTicks() >> CLOCK_START_NANO_TICKS def timestamp = new Timestamp(clock) + when: + def duration = timestamp.getDuration(FINISH_TIME) + + then: + duration == 400 + 0 * tracer._ + } + + def "test timestamp with custom time"() { + setup: + clock.nanoTicks() >> CLOCK_NANO_TICKS + clock.getStartTimeNano() >> CLOCK_START_TIME + clock.getStartNanoTicks() >> CLOCK_START_NANO_TICKS + def timestamp = new Timestamp(clock, CLOCK_START_TIME + offset, unit) + when: def time = timestamp.getTime() then: - time == 300 + time == expected + + where: + offset | unit | expected + 10 | NANOSECONDS | 410 + -20 | NANOSECONDS | 380 + 3 | MICROSECONDS | 103300 + -4 | MICROSECONDS | 96300 } def "test getDuration with literal finish time"() {