diff --git a/project/build.properties b/project/build.properties index 817bc38..c0bab04 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.9 +sbt.version=1.2.8 diff --git a/project/plugins.sbt b/project/plugins.sbt index a5890a2..6988829 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,5 @@ -lazy val root: Project = project.in(file(".")).dependsOn(latestSbtUmbrella) -lazy val latestSbtUmbrella = uri("git://github.com/kamon-io/kamon-sbt-umbrella.git") +lazy val umbrella: ProjectRef = ProjectRef(uri("git://github.com/kamon-io/kamon-sbt-umbrella.git"), "kamon-sbt-umbrella") +lazy val root: Project = project.in(file(".")).dependsOn(umbrella) addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5") + diff --git a/src/main/scala/kamon/apm/package.scala b/src/main/scala/kamon/apm/package.scala index c8adf70..73f3250 100644 --- a/src/main/scala/kamon/apm/package.scala +++ b/src/main/scala/kamon/apm/package.scala @@ -69,4 +69,40 @@ package object apm { def tracingRoute = s"$ingestionApi/tracing/ingest" } + + + /* + * Internal HDR Histogram state required to convert index to values and get bucket size information. These values + * correspond to a histogram configured to have 2 significant value digits prevision and a smallest discernible value + * of 1. + */ + def countsArrayIndex(value: Long): Int = { + + val SubBucketHalfCountMagnitude = 7 + val SubBucketHalfCount = 128 + val UnitMagnitude = 0 + val SubBucketCount = Math.pow(2, SubBucketHalfCountMagnitude + 1).toInt + val LeadingZeroCountBase = 64 - UnitMagnitude - SubBucketHalfCountMagnitude - 1 + val SubBucketMask = (SubBucketCount.toLong - 1) << UnitMagnitude + + def countsArrayIndex(bucketIndex: Int, subBucketIndex: Int): Int = { + assert(subBucketIndex < SubBucketCount) + assert(bucketIndex == 0 || (subBucketIndex >= SubBucketHalfCount)) + val bucketBaseIndex = (bucketIndex + 1) << SubBucketHalfCountMagnitude + val offsetInBucket = subBucketIndex - SubBucketHalfCount + bucketBaseIndex + offsetInBucket + } + + def getBucketIndex(value: Long): Int = + LeadingZeroCountBase - java.lang.Long.numberOfLeadingZeros(value | SubBucketMask) + + def getSubBucketIndex(value: Long, bucketIndex: Long): Int = + Math.floor(value / Math.pow(2, (bucketIndex + UnitMagnitude))).toInt + + if (value < 0) throw new ArrayIndexOutOfBoundsException("Histogram recorded value cannot be negative.") + val bucketIndex = getBucketIndex(value) + val subBucketIndex = getSubBucketIndex(value, bucketIndex) + countsArrayIndex(bucketIndex, subBucketIndex) + } + } diff --git a/src/main/scala/kamon/apm/reporters/KamonApmMetric.scala b/src/main/scala/kamon/apm/reporters/KamonApmMetric.scala index c407cb2..d8c074a 100644 --- a/src/main/scala/kamon/apm/reporters/KamonApmMetric.scala +++ b/src/main/scala/kamon/apm/reporters/KamonApmMetric.scala @@ -1,4 +1,5 @@ -package kamon.apm.reporters +package kamon.apm +package reporters import java.nio.ByteBuffer import java.time.Duration @@ -120,7 +121,15 @@ private[apm] class KamonApmMetric(codeProvidedPlan: Option[Plan]) extends Metric private def toIngestionMetricValue(metricType: InstrumentType)(metric: MetricValue): IngestionV1.Metric = { valueBuffer.clear() - ZigZag.putLong(valueBuffer, metricScaler.scaleMetricValue(metric).value) + metricType match { + case COUNTER => + ZigZag.putLong(valueBuffer, metricScaler.scaleMetricValue(metric).value) + case GAUGE => + val offset = countsArrayIndex(metricScaler.scaleMetricValue(metric).value) + if(offset > 0) ZigZag.putLong(valueBuffer, -offset) + ZigZag.putLong(valueBuffer, 1) + } + valueBuffer.flip() IngestionV1.Metric.newBuilder()