11package org .typelevel .keypool
22
3- import java .util .concurrent .TimeUnit
4-
5- import cats .syntax .all ._
63import cats .effect ._
7- import io .opentelemetry .sdk .OpenTelemetrySdk
84import io .opentelemetry .sdk .metrics .{
95 Aggregation ,
106 InstrumentSelector ,
117 InstrumentType ,
12- SdkMeterProvider ,
8+ SdkMeterProviderBuilder ,
139 View
1410}
15- import io .opentelemetry .sdk .testing .exporter .InMemoryMetricReader
1611import munit .CatsEffectSuite
1712import org .typelevel .otel4s .{MeterProvider , Otel4s }
1813import org .typelevel .otel4s .java .OtelJava
14+ import org .typelevel .otel4s .testkit .{HistogramPointData , Metric , MetricData , Sdk }
1915import scala .concurrent .duration ._
2016import scala .jdk .CollectionConverters ._
2117import scala .util .control .NoStackTrace
@@ -24,10 +20,12 @@ class PoolMetricsSpec extends CatsEffectSuite {
2420 import PoolMetricsSpec ._
2521
2622 test(" Metrics should be empty for unused pool" ) {
27- val expectedSnapshot = MetricsSnapshot (0 , 0 , Nil , 0 , Nil )
23+ val sdk = createSdk
24+ val expectedSnapshot =
25+ MetricsSnapshot (0 , 0 , Nil , 0 , Nil )
2826
2927 for {
30- snapshot <- setupSdk.use(sdk => mkPool(sdk.otel.meterProvider).use(_ => sdk.snapshot) )
28+ snapshot <- mkPool(sdk.otel.meterProvider).use(_ => sdk.snapshot)
3129 } yield assertEquals(snapshot, expectedSnapshot)
3230 }
3331
@@ -100,45 +98,46 @@ class PoolMetricsSpec extends CatsEffectSuite {
10098 }
10199
102100 test(" Generate valid metric snapshots" ) {
103- setupSdk.use { sdk =>
104- mkPool(sdk.otel.meterProvider)
105- .use(pool => pool.take.use(_ => sdk.snapshot.delayBy(500 .millis)).product(sdk.snapshot))
106- .map { case (inUse, afterUse) =>
107- val acquireDuration =
108- List (HistogramSnapshot (0 , 1 , HistogramBuckets , List (0 , 1 , 0 , 0 , 0 )))
109-
110- val expectedInUse = MetricsSnapshot (
111- idle = 0 ,
112- inUse = 1 ,
113- inUseDuration = Nil ,
114- acquiredTotal = 1 ,
115- acquireDuration = acquireDuration
116- )
101+ val sdk = createSdk
102+
103+ mkPool(sdk.otel.meterProvider)
104+ .use(pool => pool.take.use(_ => sdk.snapshot.delayBy(500 .millis)).product(sdk.snapshot))
105+ .map { case (inUse, afterUse) =>
106+ val acquireDuration =
107+ List (HistogramPointData (0 , 1 , HistogramBuckets , List (0 , 1 , 0 , 0 , 0 )))
108+
109+ val expectedInUse = MetricsSnapshot (
110+ idle = 0 ,
111+ inUse = 1 ,
112+ inUseDuration = Nil ,
113+ acquiredTotal = 1 ,
114+ acquireDuration = acquireDuration
115+ )
117116
118- val expectedAfterUser = MetricsSnapshot (
119- idle = 1 ,
120- inUse = 0 ,
121- inUseDuration = List (HistogramSnapshot (0 , 1 , HistogramBuckets , List (0 , 0 , 0 , 1 , 0 ))),
122- acquiredTotal = 1 ,
123- acquireDuration = acquireDuration
124- )
117+ val expectedAfterUser = MetricsSnapshot (
118+ idle = 1 ,
119+ inUse = 0 ,
120+ inUseDuration = List (HistogramPointData (0 , 1 , HistogramBuckets , List (0 , 0 , 0 , 1 , 0 ))),
121+ acquiredTotal = 1 ,
122+ acquireDuration = acquireDuration
123+ )
125124
126- assertEquals(inUse.zeroSumHistogram, expectedInUse)
127- assertEquals(afterUse.zeroSumHistogram, expectedAfterUser)
128- assert(afterUse.inUseDuration.forall(r => r.sum >= 500 && r.sum <= 700 ))
129- }
130- }
125+ assertEquals(inUse.zeroSumHistogram, expectedInUse)
126+ assertEquals(afterUse.zeroSumHistogram, expectedAfterUser)
127+ assert(afterUse.inUseDuration.forall(r => r.sum >= 500 && r.sum <= 700 ))
128+ }
131129 }
132130
133131 private def poolTest (
134132 customize : Pool .Builder [IO , Ref [IO , Int ]] => Pool .Builder [IO , Ref [IO , Int ]] = identity
135- )(scenario : (OtelSdk [IO ], Pool [IO , Ref [IO , Int ]]) => IO [Unit ]): IO [Unit ] =
136- setupSdk.use { sdk =>
137- val builder =
138- Pool .Builder (Ref .of[IO , Int ](1 ), nothing).withMeterProvider(sdk.otel.meterProvider)
133+ )(scenario : (OtelSdk [IO ], Pool [IO , Ref [IO , Int ]]) => IO [Unit ]): IO [Unit ] = {
134+ val sdk = createSdk
139135
140- customize(builder).build.use(pool => scenario(sdk, pool))
141- }
136+ val builder =
137+ Pool .Builder (Ref .of[IO , Int ](1 ), nothing).withMeterProvider(sdk.otel.meterProvider)
138+
139+ customize(builder).build.use(pool => scenario(sdk, pool))
140+ }
142141
143142 private def mkPool (meterProvider : MeterProvider [IO ]) =
144143 Pool
@@ -150,13 +149,9 @@ class PoolMetricsSpec extends CatsEffectSuite {
150149 .withMaxTotal(10 )
151150 .build
152151
153- private def setupSdk : Resource [IO , OtelSdk [IO ]] = {
154- val acquire = IO .delay {
155- val mr = InMemoryMetricReader .create()
156-
157- val mp = SdkMeterProvider
158- .builder()
159- .registerMetricReader(mr)
152+ private def createSdk : OtelSdk [IO ] = {
153+ def customize (builder : SdkMeterProviderBuilder ) =
154+ builder
160155 .registerView(
161156 InstrumentSelector .builder().setType(InstrumentType .HISTOGRAM ).build(),
162157 View
@@ -166,63 +161,43 @@ class PoolMetricsSpec extends CatsEffectSuite {
166161 )
167162 .build()
168163 )
169- .build()
170-
171- val sdk = OpenTelemetrySdk
172- .builder()
173- .setMeterProvider(mp)
174- .build()
175-
176- new OtelSdk [IO ] {
177- def metricReader : InMemoryMetricReader = mr
178- def otel : Otel4s [IO ] = OtelJava .forSync[IO ](sdk)
179- def flush : IO [Unit ] = IO .blocking {
180- val _ = mp.forceFlush().join(5 , TimeUnit .SECONDS )
181- ()
182- }
183164
184- def snapshot : IO [MetricsSnapshot ] = {
185- IO .delay {
186- val items = metricReader.collectAllMetrics().asScala.toList
187-
188- def counterValue (name : String ): Long =
189- items
190- .find(_.getName === name)
191- .flatMap(_.getLongSumData.getPoints.asScala.headOption.map(_.getValue))
192- .getOrElse(0L )
193-
194- def histogramSnapshot (name : String ): List [HistogramSnapshot ] =
195- items
196- .find(_.getName === name)
197- .map { metric =>
198- val points = metric.getHistogramData.getPoints.asScala.toList
199-
200- points.map { hdp =>
201- HistogramSnapshot (
202- hdp.getSum,
203- hdp.getCount,
204- hdp.getBoundaries.asScala.toList.map(Double .unbox),
205- hdp.getCounts.asScala.toList.map(Long .unbox)
206- )
207- }
208- }
209- .getOrElse(Nil )
210-
211- MetricsSnapshot (
212- counterValue(" idle" ),
213- counterValue(" in_use" ),
214- histogramSnapshot(" in_use_duration" ),
215- counterValue(" acquired_total" ),
216- histogramSnapshot(" acquire_duration" )
217- )
218- }
165+ val sdk = Sdk .create[IO ](customize)
166+
167+ new OtelSdk [IO ] {
168+ val otel : Otel4s [IO ] = OtelJava .forSync[IO ](sdk.sdk)
169+
170+ def snapshot : IO [MetricsSnapshot ] =
171+ for {
172+ metrics <- sdk.metrics
173+ } yield {
174+ def counterValue (name : String ): Long =
175+ metrics
176+ .collectFirst {
177+ case Metric (metricName, _, _, _, _, MetricData .LongSum (points))
178+ if metricName == name =>
179+ points.headOption.map(_.value).getOrElse(0L )
180+ }
181+ .getOrElse(0L )
182+
183+ def histogramSnapshot (name : String ): List [HistogramPointData ] =
184+ metrics
185+ .collectFirst {
186+ case Metric (metricName, _, _, _, _, h : MetricData .Histogram )
187+ if metricName == name =>
188+ h.points.map(_.value)
189+ }
190+ .getOrElse(Nil )
191+
192+ MetricsSnapshot (
193+ counterValue(" idle" ),
194+ counterValue(" in_use" ),
195+ histogramSnapshot(" in_use_duration" ),
196+ counterValue(" acquired_total" ),
197+ histogramSnapshot(" acquire_duration" )
198+ )
219199 }
220- }
221200 }
222-
223- def release (sdk : OtelSdk [IO ]) = sdk.flush
224-
225- Resource .make(acquire)(release)
226201 }
227202
228203 private val HistogramBuckets : List [Double ] =
@@ -236,34 +211,23 @@ class PoolMetricsSpec extends CatsEffectSuite {
236211object PoolMetricsSpec {
237212
238213 trait OtelSdk [F [_]] {
239- def metricReader : InMemoryMetricReader
240214 def otel : Otel4s [F ]
241- def flush : F [Unit ]
242215 def snapshot : F [MetricsSnapshot ]
243216 }
244217
245218 final case class MetricsSnapshot (
246219 idle : Long ,
247220 inUse : Long ,
248- inUseDuration : List [HistogramSnapshot ],
221+ inUseDuration : List [HistogramPointData ],
249222 acquiredTotal : Long ,
250- acquireDuration : List [HistogramSnapshot ]
223+ acquireDuration : List [HistogramPointData ]
251224 ) {
252225 // use 0 for `histogram#sum` to simplify the comparison
253226 def zeroSumHistogram : MetricsSnapshot =
254227 copy(
255- inUseDuration = inUseDuration.map(_.zeroSum ),
256- acquireDuration = acquireDuration.map(_.zeroSum )
228+ inUseDuration = inUseDuration.map(_.copy(sum = 0 ) ),
229+ acquireDuration = acquireDuration.map(_.copy(sum = 0 ) )
257230 )
258231 }
259232
260- final case class HistogramSnapshot (
261- sum : Double ,
262- count : Long ,
263- boundaries : List [Double ],
264- counts : List [Long ]
265- ) {
266- def zeroSum : HistogramSnapshot = copy(sum = 0 )
267- }
268-
269233}
0 commit comments