Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Metric.timer(name, chronounit) broken #7697

@domdorn

Description

@domdorn

I'm trying to integrate a timer metric that should track the duration of a effect in my system so I can visualize it in Grafana.

The code to create a timer is Metric is this:

  /**
   * Creates a timer metric, based on a histogram, which keeps track of
   * durations in the specified unit of time (milliseconds, seconds, etc.). The
   * unit of time will automatically be added to the metric as a tag
   * ("time_unit: milliseconds").
   */
  def timer(
    name: String,
    chronoUnit: ChronoUnit
  ): Metric[MetricKeyType.Histogram, Duration, MetricState.Histogram] = {
    val boundaries = Histogram.Boundaries.exponential(1.0, 2.0, 100)
    val base       = histogram(name, boundaries).tagged(MetricLabel("time_unit", chronoUnit.toString.toLowerCase()))

    base.contramap[Duration] { (duration: Duration) =>
      duration.get(chronoUnit).toDouble
    }
  }

I want to create a timer that is tracking the durations in milliseconds, so naturally I am creating it like this:

  private type M = Metric[MetricKeyType.Histogram, zio.Duration, MetricState.Histogram]              
                                                                                                     
  val loadingMDS: M                        = Metric.timer("rtd_loading_mds", ChronoUnit.MILLIS)      

The problem now is the line

      duration.get(chronoUnit).toDouble

because duration.get(chronounit) does not convert the duration to the requested unit but it does try to access a field in the duration.

    //-----------------------------------------------------------------------
    /**
     * Gets the value of the requested unit.
     * <p>
     * This returns a value for each of the two supported units,
     * {@link ChronoUnit#SECONDS SECONDS} and {@link ChronoUnit#NANOS NANOS}.
     * All other units throw an exception.
     *
     * @param unit the {@code TemporalUnit} for which to return the value
     * @return the long value of the unit
     * @throws DateTimeException if the unit is not supported
     * @throws UnsupportedTemporalTypeException if the unit is not supported
     */
    @Override
    public long get(TemporalUnit unit) {
        if (unit == SECONDS) {
            return seconds;
        } else if (unit == NANOS) {
            return nanos;
        } else {
            throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
        }
    }

So any usage of Metric.timer(name, chronounit) with a ChronoUnit different from ChronoUnit.SECONDS or ChronoUnit.NANOS will throw the UnsupportedTemporalTypeException resulting in errors like this:

[info] {"@timestamp":"2023-01-13T16:32:23.335+01:00","@version":"1","message":"Fiber zio-fiber-352 did not handle an error","logger_name":"com.acme.myproject.masterdata.rtd.controllers.hlvis.RunRTDNextController","thread_name":"ZScheduler-Worker-15","level":"DEBUG","level_value":10000,
"stack_trace":"zio.FiberFailure: Unsupported unit: Millis
\tat java.base/java.time.Duration.get(Duration.java:552)
\tat com.acme.myproject.mypackage.modul0_load.LoadDataServiceLive.loadData(LoadDataService.scala:41)
\tat com.acme.myproject.mypackage.modul0_load.LoadDataServiceLive.loadData(LoadDataService.scala:38)
\tat com.acme.myproject.mypackage.RTDOrchestrationLive.runRTD.result(RTDOrchestration.scala:170)
\tat com.acme.myproject.mypackage.RTDOrchestrationLive.runRTD.result(RTDOrchestration.scala:171)
\tat com.acme.myproject.mypackage.RTDOrchestrationLive.runRTD.result(RTDOrchestration.scala:172)
\tat com.acme.myproject.mypackage.RTDOrchestrationLive.runRTD.result(RTDOrchestration.scala:173)
\tat com.acme.myproject.mypackage.RTDOrchestrationLive.runRTD.result(RTDOrchestration.scala:168)
\tat com.acme.myproject.mypackage.RTDOrchestrationLive.runRTD.resultStream(RTDOrchestration.scala:308)
\tat com.acme.myproject.mypackage.RTDOrchestrationLive.runRTD(RTDOrchestration.scala:149)
Caused by: java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Millis
\tat java.base/java.time.Duration.get(Duration.java:552)
\tat zio.metrics.Metric$.$anonfun$timer$1(Metric.scala:539)
\tat zio.metrics.Metric$.$anonfun$timer$1$adapted(Metric.scala:538)
\tat zio.metrics.Metric$$anon$1$$anon$2.update(Metric.scala:73)
\tat zio.metrics.Metric$$anon$13.$anonfun$apply$8(Metric.scala:265)
\tat zio.ZIO.$anonfun$map$1(ZIO.scala:959)
\tat zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1127)
\tat zio.internal.FiberRuntime.evaluateEffect(FiberRuntime.scala:385)
\tat zio.internal.FiberRuntime.evaluateMessageWhileSuspended(FiberRuntime.scala:508)
\tat zio.internal.FiberRuntime.drainQueueOnCurrentThread(FiberRuntime.scala:225)
\tat zio.internal.FiberRuntime.run(FiberRuntime.scala:138)
\tat zio.internal.ZScheduler$$anon$4.run(ZScheduler.scala:476)
",

I'm calling the metric like this:

for { 
... 
 _ <- myEffect @@ loadingMDS.trackDuration 
...

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions