From e07a3bb2002c37a8b2d604162387fb45dcd03b6e Mon Sep 17 00:00:00 2001 From: Hugh Simpson Date: Fri, 4 Aug 2023 13:44:38 +0100 Subject: [PATCH] initial implementation of actual conversion --- .../scala/kamon/otel/MetricsConverter.scala | 87 ++++++++++++++++++- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/reporters/kamon-opentelemetry/src/main/scala/kamon/otel/MetricsConverter.scala b/reporters/kamon-opentelemetry/src/main/scala/kamon/otel/MetricsConverter.scala index 20c02f951..d21afc6ac 100644 --- a/reporters/kamon-opentelemetry/src/main/scala/kamon/otel/MetricsConverter.scala +++ b/reporters/kamon-opentelemetry/src/main/scala/kamon/otel/MetricsConverter.scala @@ -15,16 +15,95 @@ */ package kamon.otel -import io.opentelemetry.sdk.metrics.data.MetricData +import io.opentelemetry.sdk.common.InstrumentationScopeInfo +import io.opentelemetry.sdk.metrics.data._ +import io.opentelemetry.sdk.metrics.internal.data._ import io.opentelemetry.sdk.resources.Resource -import kamon.metric.PeriodSnapshot +import kamon.metric.Instrument.Snapshot +import kamon.metric.{Distribution, MeasurementUnit, MetricSnapshot, PeriodSnapshot} +import kamon.tag.Lookups +import kamon.trace.Span.TagKeys +import java.lang.{Double => JDouble, Long => JLong} +import java.time.Instant import java.util.{Collection => JCollection} +import scala.collection.JavaConverters._ + +class WithResourceMetricsConverter(resource: Resource, kamonVersion: String, from: Instant, to: Instant) { + private val fromNs = from.toEpochMilli * 1000000 + private val toNs = to.toEpochMilli * 1000000 + + private def instrumentationScopeInfo(snapshot: MetricSnapshot[_, _]): InstrumentationScopeInfo = { + // logic for looking up the component doesn't really seem to make sense - to be compliant we should probably be grouping the metrics by component before calling this + InstrumentationScopeInfo.create(snapshot.instruments.headOption.flatMap(_.tags.get(Lookups.option(TagKeys.Component))) getOrElse "kamon-instrumentation", kamonVersion, null) + } + + private def toString(unit: MeasurementUnit): String = unit.magnitude.name + + def toGaugeDatum(g: Snapshot[Double]): DoublePointData = ImmutableDoublePointData.create(fromNs, toNs, SpanConverter.toAttributes(g.tags), g.value) + + def toGaugeData(g: Seq[Snapshot[Double]]): GaugeData[DoublePointData] = ImmutableGaugeData.create(g.map(toGaugeDatum).asJava) + + def convertGauge(gauge: MetricSnapshot.Values[Double]): MetricData = + ImmutableMetricData.createDoubleGauge( + resource, + instrumentationScopeInfo(gauge), + gauge.name, + gauge.description, + toString(gauge.settings.unit), + toGaugeData(gauge.instruments)) + + def toHistogramDatum(s: Snapshot[Distribution]): HistogramPointData = + ImmutableHistogramPointData.create( + fromNs, + toNs, + SpanConverter.toAttributes(s.tags), + JDouble valueOf s.value.sum.toDouble, + JDouble valueOf s.value.min.toDouble, + JDouble valueOf s.value.max.toDouble, + s.value.buckets.map(JDouble valueOf _.value.toDouble).asJava, + s.value.buckets.map(JLong valueOf _.frequency).asJava + ) + + def toHistogramData(any: Seq[Snapshot[Distribution]]): HistogramData = + ImmutableHistogramData.create(AggregationTemporality.CUMULATIVE, any.map(toHistogramDatum).asJava) + + def convertHistogram(histogram: MetricSnapshot.Distributions): MetricData = + ImmutableMetricData.createDoubleHistogram( + resource, + instrumentationScopeInfo(histogram), + histogram.name, + histogram.description, + toString(histogram.settings.unit), + toHistogramData(histogram.instruments)) + + def toCounterDatum(g: Snapshot[Long]): LongPointData = + ImmutableLongPointData.create(fromNs, toNs, SpanConverter.toAttributes(g.tags), g.value) + + def toCounterData(g: Seq[Snapshot[Long]]): SumData[LongPointData] = + ImmutableSumData.create(false, AggregationTemporality.CUMULATIVE, g.map(toCounterDatum).asJava) + + def convertCounter(counter: MetricSnapshot.Values[Long]): MetricData = + ImmutableMetricData.createLongSum( + resource, + instrumentationScopeInfo(counter), + counter.name, + counter.description, + toString(counter.settings.unit), + toCounterData(counter.instruments)) + +} /** * Converts Kamon metrics to OpenTelemetry [[MetricData]]s */ private[otel] object MetricsConverter { - def convert(resource: Resource, kamonVersion: String)(metrics: PeriodSnapshot): JCollection[MetricData] = - ??? + def convert(resource: Resource, kamonVersion: String)(metrics: PeriodSnapshot): JCollection[MetricData] = { + val converter = new WithResourceMetricsConverter(resource, kamonVersion, metrics.from, metrics.to) + val gauges = metrics.gauges.map(converter.convertGauge) + val histograms = (metrics.histograms ++ metrics.timers ++ metrics.rangeSamplers).map(converter.convertHistogram) + val counters = metrics.counters.map(converter.convertCounter) + + (gauges ++ histograms ++ counters).asJava + } }