|
| 1 | +/* |
| 2 | + * Copyright 2013-2021 The Kamon Project <https://kamon.io> |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
| 16 | +package kamon.otel |
| 17 | + |
| 18 | +import io.opentelemetry.sdk.common.InstrumentationScopeInfo |
| 19 | +import io.opentelemetry.sdk.metrics.data._ |
| 20 | +import io.opentelemetry.sdk.metrics.internal.data._ |
| 21 | +import io.opentelemetry.sdk.resources.Resource |
| 22 | +import kamon.metric.Instrument.Snapshot |
| 23 | +import kamon.metric.{Distribution, MeasurementUnit, MetricSnapshot, PeriodSnapshot} |
| 24 | +import kamon.tag.Lookups |
| 25 | +import kamon.trace.Span.TagKeys |
| 26 | + |
| 27 | +import java.lang.{Double => JDouble, Long => JLong} |
| 28 | +import java.time.Instant |
| 29 | +import java.util.{Collection => JCollection} |
| 30 | +import scala.collection.JavaConverters._ |
| 31 | + |
| 32 | +class WithResourceMetricsConverter(resource: Resource, kamonVersion: String, from: Instant, to: Instant) { |
| 33 | + private val fromNs = from.toEpochMilli * 1000000 |
| 34 | + private val toNs = to.toEpochMilli * 1000000 |
| 35 | + |
| 36 | + private def instrumentationScopeInfo(snapshot: MetricSnapshot[_, _]): InstrumentationScopeInfo = { |
| 37 | + // 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 |
| 38 | + InstrumentationScopeInfo.create(snapshot.instruments.headOption.flatMap(_.tags.get(Lookups.option(TagKeys.Component))) getOrElse "kamon-instrumentation", kamonVersion, null) |
| 39 | + } |
| 40 | + |
| 41 | + private def toString(unit: MeasurementUnit): String = unit.magnitude.name |
| 42 | + |
| 43 | + def toGaugeDatum(g: Snapshot[Double]): DoublePointData = ImmutableDoublePointData.create(fromNs, toNs, SpanConverter.toAttributes(g.tags), g.value) |
| 44 | + |
| 45 | + def toGaugeData(g: Seq[Snapshot[Double]]): GaugeData[DoublePointData] = ImmutableGaugeData.create(g.map(toGaugeDatum).asJava) |
| 46 | + |
| 47 | + def convertGauge(gauge: MetricSnapshot.Values[Double]): MetricData = |
| 48 | + ImmutableMetricData.createDoubleGauge( |
| 49 | + resource, |
| 50 | + instrumentationScopeInfo(gauge), |
| 51 | + gauge.name, |
| 52 | + gauge.description, |
| 53 | + toString(gauge.settings.unit), |
| 54 | + toGaugeData(gauge.instruments)) |
| 55 | + |
| 56 | + def toHistogramDatum(s: Snapshot[Distribution]): HistogramPointData = |
| 57 | + ImmutableHistogramPointData.create( |
| 58 | + fromNs, |
| 59 | + toNs, |
| 60 | + SpanConverter.toAttributes(s.tags), |
| 61 | + JDouble valueOf s.value.sum.toDouble, |
| 62 | + JDouble valueOf s.value.min.toDouble, |
| 63 | + JDouble valueOf s.value.max.toDouble, |
| 64 | + s.value.buckets.map(JDouble valueOf _.value.toDouble).asJava, |
| 65 | + s.value.buckets.map(JLong valueOf _.frequency).asJava |
| 66 | + ) |
| 67 | + |
| 68 | + def toHistogramData(any: Seq[Snapshot[Distribution]]): HistogramData = |
| 69 | + ImmutableHistogramData.create(AggregationTemporality.CUMULATIVE, any.map(toHistogramDatum).asJava) |
| 70 | + |
| 71 | + def convertHistogram(histogram: MetricSnapshot.Distributions): MetricData = |
| 72 | + ImmutableMetricData.createDoubleHistogram( |
| 73 | + resource, |
| 74 | + instrumentationScopeInfo(histogram), |
| 75 | + histogram.name, |
| 76 | + histogram.description, |
| 77 | + toString(histogram.settings.unit), |
| 78 | + toHistogramData(histogram.instruments)) |
| 79 | + |
| 80 | + def toCounterDatum(g: Snapshot[Long]): LongPointData = |
| 81 | + ImmutableLongPointData.create(fromNs, toNs, SpanConverter.toAttributes(g.tags), g.value) |
| 82 | + |
| 83 | + def toCounterData(g: Seq[Snapshot[Long]]): SumData[LongPointData] = |
| 84 | + ImmutableSumData.create(false, AggregationTemporality.CUMULATIVE, g.map(toCounterDatum).asJava) |
| 85 | + |
| 86 | + def convertCounter(counter: MetricSnapshot.Values[Long]): MetricData = |
| 87 | + ImmutableMetricData.createLongSum( |
| 88 | + resource, |
| 89 | + instrumentationScopeInfo(counter), |
| 90 | + counter.name, |
| 91 | + counter.description, |
| 92 | + toString(counter.settings.unit), |
| 93 | + toCounterData(counter.instruments)) |
| 94 | + |
| 95 | +} |
| 96 | + |
| 97 | +/** |
| 98 | + * Converts Kamon metrics to OpenTelemetry [[MetricData]]s |
| 99 | + */ |
| 100 | +private[otel] object MetricsConverter { |
| 101 | + def convert(resource: Resource, kamonVersion: String)(metrics: PeriodSnapshot): JCollection[MetricData] = { |
| 102 | + val converter = new WithResourceMetricsConverter(resource, kamonVersion, metrics.from, metrics.to) |
| 103 | + val gauges = metrics.gauges.map(converter.convertGauge) |
| 104 | + val histograms = (metrics.histograms ++ metrics.timers ++ metrics.rangeSamplers).map(converter.convertHistogram) |
| 105 | + val counters = metrics.counters.map(converter.convertCounter) |
| 106 | + |
| 107 | + (gauges ++ histograms ++ counters).asJava |
| 108 | + } |
| 109 | +} |
0 commit comments