From a97c7dac0748732700d3a98ee44bd2fdf847ffbc Mon Sep 17 00:00:00 2001 From: Ivan Topolnjak Date: Mon, 18 Dec 2017 12:50:41 +0100 Subject: move alignment and duration utilities to the companion object --- .../src/test/scala/kamon/util/ClockSpec.scala | 39 ++++++++++++++++++++++ kamon-core/src/main/scala/kamon/trace/Span.scala | 2 +- kamon-core/src/main/scala/kamon/util/Clock.scala | 28 +++++++++++----- 3 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 kamon-core-tests/src/test/scala/kamon/util/ClockSpec.scala diff --git a/kamon-core-tests/src/test/scala/kamon/util/ClockSpec.scala b/kamon-core-tests/src/test/scala/kamon/util/ClockSpec.scala new file mode 100644 index 00000000..5f83d22c --- /dev/null +++ b/kamon-core-tests/src/test/scala/kamon/util/ClockSpec.scala @@ -0,0 +1,39 @@ +package kamon.util + +import java.time.{Duration, Instant} + +import kamon.util +import org.scalatest.{Matchers, WordSpec} + +class ClockSpec extends WordSpec with Matchers { + "the Clock" should { + "generate nanosecond precision Instants" in { + newClock().instant().getNano() % MicrosInSecond shouldNot be(0) + } + + "turn Instants into micros" in { + Clock.toEpochMicros(Instant.parse("2017-12-18T08:39:59.000000000Z")) shouldBe 1513586399000000L + Clock.toEpochMicros(Instant.parse("2017-12-18T08:39:59.000000010Z")) shouldBe 1513586399000000L + Clock.toEpochMicros(Instant.parse("2017-12-18T08:39:59.987654321Z")) shouldBe 1513586399987654L + Clock.toEpochMicros(Instant.parse("2017-12-18T08:39:59.987000000Z")) shouldBe 1513586399987000L + } + + "calculate nanos between two Instants" in { + Clock.nanosBetween(Instant.parse("2017-12-18T08:39:59.987654321Z"), Instant.parse("2017-12-18T08:39:59.987654322Z")) shouldBe 1 + Clock.nanosBetween(Instant.parse("2017-12-18T08:39:59.987654322Z"), Instant.parse("2017-12-18T08:39:59.987654321Z")) shouldBe -1 + Clock.nanosBetween(Instant.parse("2017-12-18T08:39:59.987Z"), Instant.parse("2017-12-18T08:39:59.988Z")) shouldBe 1000000 + Clock.nanosBetween(Instant.parse("2017-12-18T08:39:59.987654Z"), Instant.parse("2017-12-18T08:39:59.987Z")) shouldBe -654000 + } + + "calculate ticks aligned to rounded boundaries" in { + Clock.nextTick(Instant.parse("2017-12-18T08:39:59.999Z"), Duration.ofSeconds(10)).toString shouldBe "2017-12-18T08:40:00Z" + Clock.nextTick(Instant.parse("2017-12-18T08:40:00.000Z"), Duration.ofSeconds(10)).toString shouldBe "2017-12-18T08:40:10Z" + Clock.nextTick(Instant.parse("2017-12-18T08:39:14.906Z"), Duration.ofSeconds(10)).toString shouldBe "2017-12-18T08:39:20Z" + } + } + + val MicrosInSecond = 1000000 + + def newClock(): Clock = + new util.Clock.Default() +} diff --git a/kamon-core/src/main/scala/kamon/trace/Span.scala b/kamon-core/src/main/scala/kamon/trace/Span.scala index d773d46e..690efeb4 100644 --- a/kamon-core/src/main/scala/kamon/trace/Span.scala +++ b/kamon-core/src/main/scala/kamon/trace/Span.scala @@ -201,7 +201,7 @@ object Span { Span.FinishedSpan(spanContext, operationName, from, to, spanTags, marks) private def recordSpanMetrics(to: Instant): Unit = { - val elapsedTime = clock.nanosBetween(from, to) + val elapsedTime = Clock.nanosBetween(from, to) val isErrorText = if(hasError) TagValue.True.text else TagValue.False.text if(scopeSpanMetrics) diff --git a/kamon-core/src/main/scala/kamon/util/Clock.scala b/kamon-core/src/main/scala/kamon/util/Clock.scala index 48a88968..8c00ecc6 100644 --- a/kamon-core/src/main/scala/kamon/util/Clock.scala +++ b/kamon-core/src/main/scala/kamon/util/Clock.scala @@ -15,17 +15,17 @@ package kamon.util -import java.time.{Instant, ZoneId, Clock => JavaClock} +import java.time.{Duration, Instant, ZoneId, Clock => JavaClock} abstract class Clock extends JavaClock { def nanos(): Long - def nanosBetween(left: Instant, right: Instant): Long def toInstant(nanos: Long): Instant } object Clock { private val MillisInSecond = 1000L + private val NanosInMicro = 1000L private val MicrosInSecond = 1000000L private val NanosInSecond = 1000000000L @@ -74,16 +74,28 @@ object Clock { override def instant(): Instant = toInstant(System.nanoTime()) - override def nanosBetween(left: Instant, right: Instant): Long = { - val secsDiff = Math.subtractExact(right.getEpochSecond, left.getEpochSecond) - val totalNanos = Math.multiplyExact(secsDiff, NanosInSecond) - return Math.addExact(totalNanos, right.getNano - left.getNano) - } - override def withZone(zone: ZoneId): JavaClock = systemClock.withZone(zone) override def getZone: ZoneId = systemClock.getZone() } + + def nanosBetween(left: Instant, right: Instant): Long = { + val secsDiff = Math.subtractExact(right.getEpochSecond, left.getEpochSecond) + val totalNanos = Math.multiplyExact(secsDiff, NanosInSecond) + return Math.addExact(totalNanos, right.getNano - left.getNano) + } + + def toEpochMicros(instant: Instant): Long = { + Math.multiplyExact(instant.getEpochSecond, MicrosInSecond) + Math.floorDiv(instant.getNano, NanosInMicro) + } + + def nextTick(from: Instant, expectedDuration: Duration): Instant = { + val fromMillis = from.toEpochMilli() + val intervalCount = Math.floorDiv(fromMillis, expectedDuration.toMillis) + val nextTickMillis = expectedDuration.toMillis * (intervalCount + 1) + + Instant.ofEpochMilli(nextTickMillis) + } } \ No newline at end of file -- cgit v1.2.3