From 993e87078728cf895775c68214780967d55504c1 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Fri, 10 Sep 2021 20:42:00 +0200 Subject: [PATCH 1/8] Migrated Promeheus-syle JVM metrics from zio-zmx --- .../scala/zio/metrics/jvm/BufferPools.scala | 47 +++++++ .../scala/zio/metrics/jvm/ClassLoading.scala | 43 ++++++ .../zio/metrics/jvm/DefaultJvmMetrics.scala | 24 ++++ .../zio/metrics/jvm/GarbageCollector.scala | 42 ++++++ .../scala/zio/metrics/jvm/JvmMetrics.scala | 9 ++ .../zio/metrics/jvm/MemoryAllocation.scala | 92 ++++++++++++ .../scala/zio/metrics/jvm/MemoryPools.scala | 94 +++++++++++++ .../main/scala/zio/metrics/jvm/Standard.scala | 133 ++++++++++++++++++ .../main/scala/zio/metrics/jvm/Thread.scala | 79 +++++++++++ .../scala/zio/metrics/jvm/VersionInfo.scala | 31 ++++ 10 files changed, 594 insertions(+) create mode 100644 core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala create mode 100644 core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala create mode 100644 core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala create mode 100644 core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala create mode 100644 core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala create mode 100644 core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala create mode 100644 core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala create mode 100644 core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala create mode 100644 core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala create mode 100644 core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala b/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala new file mode 100644 index 000000000000..32a10f5ce4a4 --- /dev/null +++ b/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala @@ -0,0 +1,47 @@ +package zio.metrics.jvm + +import zio._ + +import java.lang.management.{BufferPoolMXBean, ManagementFactory} + +import scala.collection.JavaConverters._ + +object BufferPools extends JvmMetrics { + + /** Used bytes of a given JVM buffer pool. */ + private def bufferPoolUsedBytes(pool: String): ZIOMetric.Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_buffer_pool_used_bytes", MetricLabel("pool", pool))(_.toDouble) + + /** Bytes capacity of a given JVM buffer pool. */ + private def bufferPoolCapacityBytes(pool: String): ZIOMetric.Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_buffer_pool_capacity_bytes", MetricLabel("pool", pool))(_.toDouble) + + /** Used buffers of a given JVM buffer pool. */ + private def bufferPoolUsedBuffers(pool: String): ZIOMetric.Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_buffer_pool_used_buffers", MetricLabel("pool", pool))(_.toDouble) + + private def reportBufferPoolMetrics( + bufferPoolMXBeans: List[BufferPoolMXBean] + ): ZIO[Any, Throwable, Unit] = + ZIO.foreachParDiscard(bufferPoolMXBeans) { bufferPoolMXBean => + for { + name <- Task(bufferPoolMXBean.getName) + _ <- Task(bufferPoolMXBean.getMemoryUsed) @@ bufferPoolUsedBytes(name) + _ <- Task(bufferPoolMXBean.getTotalCapacity) @@ bufferPoolCapacityBytes(name) + _ <- Task(bufferPoolMXBean.getCount) @@ bufferPoolUsedBuffers(name) + } yield () + } + + val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = + ZManaged.acquireReleaseWith { + for { + bufferPoolMXBeans <- + Task(ManagementFactory.getPlatformMXBeans(classOf[BufferPoolMXBean]).asScala.toList) + fiber <- + reportBufferPoolMetrics(bufferPoolMXBeans) + .repeat(collectionSchedule) + .interruptible + .forkDaemon + } yield fiber + }(_.interrupt).unit +} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala b/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala new file mode 100644 index 000000000000..86b4176fcb30 --- /dev/null +++ b/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala @@ -0,0 +1,43 @@ +package zio.metrics.jvm + +import zio.ZIOMetric.Gauge +import zio._ + +import java.lang.management.{ClassLoadingMXBean, ManagementFactory} + +/** Exports metrics related to JVM class loading */ +object ClassLoading extends JvmMetrics { + + /** The number of classes that are currently loaded in the JVM */ + private val loadedClassCount: Gauge[Int] = + ZIOMetric.setGaugeWith("jvm_classes_loaded")(_.toDouble) + + /** The total number of classes that have been loaded since the JVM has started execution */ + private val totalLoadedClassCount: Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_classes_loaded_total")(_.toDouble) + + /** The total number of classes that have been unloaded since the JVM has started execution */ + private val unloadedClassCount: Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_classes_unloaded_total")(_.toDouble) + + private def reportClassLoadingMetrics( + classLoadingMXBean: ClassLoadingMXBean + ): ZIO[Any, Throwable, Unit] = + for { + _ <- Task(classLoadingMXBean.getLoadedClassCount) @@ loadedClassCount + _ <- Task(classLoadingMXBean.getTotalLoadedClassCount) @@ totalLoadedClassCount + _ <- Task(classLoadingMXBean.getUnloadedClassCount) @@ unloadedClassCount + } yield () + + val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = + ZManaged.acquireReleaseWith { + for { + classLoadingMXBean <- + Task(ManagementFactory.getPlatformMXBean(classOf[ClassLoadingMXBean])) + fiber <- reportClassLoadingMetrics(classLoadingMXBean) + .repeat(collectionSchedule) + .interruptible + .forkDaemon + } yield fiber + }(_.interrupt).unit +} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala b/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala new file mode 100644 index 000000000000..26115d4d0ea4 --- /dev/null +++ b/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala @@ -0,0 +1,24 @@ +package zio.metrics.jvm + +import zio._ + +trait DefaultJvmMetrics + +/** JVM metrics, compatible with the prometheus-hotspot library */ +object DefaultJvmMetrics { + val collectDefaultJvmMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = + ( + BufferPools.collectMetrics <&> + ClassLoading.collectMetrics <&> + GarbageCollector.collectMetrics <&> + MemoryAllocation.collectMetrics <&> + MemoryPools.collectMetrics <&> + Standard.collectMetrics <&> + Thread.collectMetrics <&> + VersionInfo.collectMetrics + ).unit + + /** Layer that starts collecting the same JVM metrics as the Prometheus Java client's default exporters */ + val live: ZLayer[Has[Clock] with Has[System], Throwable, Has[DefaultJvmMetrics]] = + collectDefaultJvmMetrics.as(new DefaultJvmMetrics {}).toLayer +} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala b/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala new file mode 100644 index 000000000000..ef275d8ccb44 --- /dev/null +++ b/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala @@ -0,0 +1,42 @@ +package zio.metrics.jvm + +import zio.ZIOMetric.Gauge +import zio._ + +import java.lang.management.{GarbageCollectorMXBean, ManagementFactory} + +import scala.collection.JavaConverters._ + +/** Exports metrics related to the garbage collector */ +object GarbageCollector extends JvmMetrics { + + /** Time spent in a given JVM garbage collector in seconds. */ + private def gcCollectionSecondsSum(gc: String): Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_gc_collection_seconds_sum", MetricLabel("gc", gc))((ms: Long) => ms.toDouble / 1000.0) + + private def gcCollectionSecondsCount(gc: String): Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_gc_collection_seconds_count", MetricLabel("gc", gc))(_.toDouble) + + private def reportGarbageCollectionMetrics( + garbageCollectors: List[GarbageCollectorMXBean] + ): ZIO[Any, Throwable, Unit] = + ZIO.foreachParDiscard(garbageCollectors) { gc => + for { + name <- Task(gc.getName) + _ <- Task(gc.getCollectionCount) @@ gcCollectionSecondsCount(name) + _ <- Task(gc.getCollectionTime) @@ gcCollectionSecondsSum(name) + } yield () + } + + val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = + ZManaged.acquireReleaseWith { + for { + classLoadingMXBean <- Task(ManagementFactory.getGarbageCollectorMXBeans.asScala.toList) + fiber <- + reportGarbageCollectionMetrics(classLoadingMXBean) + .repeat(collectionSchedule) + .interruptible + .forkDaemon + } yield fiber + }(_.interrupt).unit +} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala b/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala new file mode 100644 index 000000000000..ea0386ce788e --- /dev/null +++ b/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala @@ -0,0 +1,9 @@ +package zio.metrics.jvm + +import zio._ + +trait JvmMetrics { + protected val collectionSchedule: Schedule[Any, Any, Unit] = Schedule.fixed(10.seconds).unit + + val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] +} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala new file mode 100644 index 000000000000..03de4d2e2d97 --- /dev/null +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala @@ -0,0 +1,92 @@ +package zio.metrics.jvm + +import com.sun.management.GarbageCollectionNotificationInfo +import zio._ +import zio.ZIOMetric.Counter + +import java.lang.management.ManagementFactory +import javax.management.openmbean.CompositeData +import javax.management.{Notification, NotificationEmitter, NotificationListener} +import scala.collection.mutable +import scala.collection.JavaConverters._ + +object MemoryAllocation extends JvmMetrics { + + /** Total bytes allocated in a given JVM memory pool. Only updated after GC, not continuously. */ + private def countAllocations(pool: String): Counter[Long] = + ZIOMetric.countValueWith("jvm_memory_pool_allocated_bytes_total", MetricLabel("pool", pool))(_.toDouble) + + private class Listener(runtime: Runtime[Any]) extends NotificationListener { + private val lastMemoryUsage: mutable.Map[String, Long] = mutable.HashMap.empty + + override def handleNotification(notification: Notification, handback: Any): Unit = { + val info = + GarbageCollectionNotificationInfo.from(notification.getUserData.asInstanceOf[CompositeData]) + val gcInfo = info.getGcInfo + val memoryUsageBeforeGc = gcInfo.getMemoryUsageBeforeGc + val memoryUsageAfterGc = gcInfo.getMemoryUsageAfterGc + for (entry <- memoryUsageBeforeGc.entrySet.asScala) { + val memoryPool = entry.getKey + val before = entry.getValue.getUsed + val after = memoryUsageAfterGc.get(memoryPool).getUsed + handleMemoryPool(memoryPool, before, after) + } + } + + private def handleMemoryPool(memoryPool: String, before: Long, after: Long): Unit = { + /* + * Calculate increase in the memory pool by comparing memory used + * after last GC, before this GC, and after this GC. + * See ascii illustration below. + * Make sure to count only increases and ignore decreases. + * (Typically a pool will only increase between GCs or during GCs, not both. + * E.g. eden pools between GCs. Survivor and old generation pools during GCs.) + * + * |<-- diff1 -->|<-- diff2 -->| + * Timeline: |-- last GC --| |---- GC -----| + * ___^__ ___^____ ___^___ + * Mem. usage vars: / last \ / before \ / after \ + */ + // Get last memory usage after GC and remember memory used after for next time + val last = lastMemoryUsage.getOrElse(memoryPool, 0L) + lastMemoryUsage.put(memoryPool, after) + // Difference since last GC + var diff1 = before - last + // Difference during this GC + var diff2 = after - before + // Make sure to only count increases + if (diff1 < 0) diff1 = 0 + if (diff2 < 0) diff2 = 0 + val increase = diff1 + diff2 + if (increase > 0) { + runtime.unsafeRun { + (UIO(increase) @@ countAllocations(memoryPool)).unit + } + } + } + } + + override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = + ZManaged + .acquireReleaseWith( + for { + runtime <- ZIO.runtime[Any] + listener = new Listener(runtime) + garbageCollectorMXBeans <- Task(ManagementFactory.getGarbageCollectorMXBeans.asScala) + _ <- ZIO.foreachDiscard(garbageCollectorMXBeans) { + case emitter: NotificationEmitter => + Task(emitter.addNotificationListener(listener, null, null)) + case _ => ZIO.unit + } + } yield (listener, garbageCollectorMXBeans) + ) { case (listener, garbageCollectorMXBeans) => + ZIO + .foreachDiscard(garbageCollectorMXBeans) { + case emitter: NotificationEmitter => + Task(emitter.removeNotificationListener(listener)) + case _ => ZIO.unit + } + .orDie + } + .unit +} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala new file mode 100644 index 000000000000..5fe0017e09e5 --- /dev/null +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala @@ -0,0 +1,94 @@ +package zio.metrics.jvm + +import zio.ZIOMetric.Gauge +import zio._ + +import java.lang.management.{ManagementFactory, MemoryMXBean, MemoryPoolMXBean, MemoryUsage} + +import scala.collection.JavaConverters._ + +object MemoryPools extends JvmMetrics { + + sealed private trait Area { val label: String } + private case object Heap extends Area { override val label: String = "heap" } + private case object NonHeap extends Area { override val label: String = "nonheap" } + + /** Used bytes of a given JVM memory area. */ + private def memoryBytesUsed(area: Area): Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_memory_bytes_used", MetricLabel("area", area.label))(_.toDouble) + + /** Committed (bytes) of a given JVM memory area. */ + private def memoryBytesCommitted(area: Area): Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_memory_bytes_committed", MetricLabel("area", area.label))(_.toDouble) + + /** Max (bytes) of a given JVM memory area. */ + private def memoryBytesMax(area: Area): Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_memory_bytes_max", MetricLabel("area", area.label))(_.toDouble) + + /** Initial bytes of a given JVM memory area. */ + private def memoryBytesInit(area: Area): Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_memory_bytes_init", MetricLabel("area", area.label))(_.toDouble) + + /** Used bytes of a given JVM memory pool. */ + private def poolBytesUsed(pool: String): Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_memory_pool_bytes_used", MetricLabel("pool", pool))(_.toDouble) + + /** Committed bytes of a given JVM memory pool. */ + private def poolBytesCommitted(pool: String): Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_memory_pool_bytes_committed", MetricLabel("pool", pool))(_.toDouble) + + /** Max bytes of a given JVM memory pool. */ + private def poolBytesMax(pool: String): Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_memory_pool_bytes_max", MetricLabel("pool", pool))(_.toDouble) + + /** Initial bytes of a given JVM memory pool. */ + private def poolBytesInit(pool: String): Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_memory_pool_bytes_init", MetricLabel("pool", pool))(_.toDouble) + + private def reportMemoryUsage(usage: MemoryUsage, area: Area): ZIO[Any, Nothing, Unit] = + for { + _ <- UIO(usage.getUsed) @@ memoryBytesUsed(area) + _ <- UIO(usage.getCommitted) @@ memoryBytesCommitted(area) + _ <- UIO(usage.getMax) @@ memoryBytesMax(area) + _ <- UIO(usage.getInit) @@ memoryBytesInit(area) + } yield () + + private def reportPoolUsage(usage: MemoryUsage, pool: String): ZIO[Any, Nothing, Unit] = + for { + _ <- UIO(usage.getUsed) @@ poolBytesUsed(pool) + _ <- UIO(usage.getCommitted) @@ poolBytesCommitted(pool) + _ <- UIO(usage.getMax) @@ poolBytesMax(pool) + _ <- UIO(usage.getInit) @@ poolBytesInit(pool) + } yield () + + private def reportMemoryMetrics( + memoryMXBean: MemoryMXBean, + poolMXBeans: List[MemoryPoolMXBean] + ): ZIO[Any, Throwable, Unit] = + for { + heapUsage <- Task(memoryMXBean.getHeapMemoryUsage) + nonHeapUsage <- Task(memoryMXBean.getNonHeapMemoryUsage) + _ <- reportMemoryUsage(heapUsage, Heap) + _ <- reportMemoryUsage(nonHeapUsage, NonHeap) + _ <- ZIO.foreachParDiscard(poolMXBeans) { pool => + for { + name <- Task(pool.getName) + usage <- Task(pool.getUsage) + _ <- reportPoolUsage(usage, name) + } yield () + } + } yield () + + val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = + ZManaged.acquireReleaseWith { + for { + memoryMXBean <- Task(ManagementFactory.getMemoryMXBean) + poolMXBeans <- Task(ManagementFactory.getMemoryPoolMXBeans.asScala.toList) + fiber <- + reportMemoryMetrics(memoryMXBean, poolMXBeans) + .repeat(collectionSchedule) + .interruptible + .forkDaemon + } yield fiber + }(_.interrupt).unit +} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala b/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala new file mode 100644 index 000000000000..9d77b69d3bee --- /dev/null +++ b/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala @@ -0,0 +1,133 @@ +package zio.metrics.jvm + +import zio.ZIOMetric.Gauge +import zio._ + +import java.lang.management.{ManagementFactory, PlatformManagedObject, RuntimeMXBean} +import java.lang.reflect.Method +import java.nio.charset.StandardCharsets +import scala.util.{Failure, Success, Try} + +object Standard extends JvmMetrics { + + /** Total user and system CPU time spent in seconds. */ + private val cpuSecondsTotal: Gauge[Long] = + ZIOMetric.setGaugeWith("process_cpu_seconds_total")(_.toDouble / 1.0e09) + + /** Start time of the process since unix epoch in seconds. */ + private val processStartTime: Gauge[Long] = + ZIOMetric.setGaugeWith("process_start_time_seconds")(_.toDouble / 1000.0) + + /** Number of open file descriptors. */ + private val openFdCount: Gauge[Long] = + ZIOMetric.setGaugeWith("process_open_fds")(_.toDouble) + + /** Maximum number of open file descriptors. */ + private val maxFdCount: Gauge[Long] = + ZIOMetric.setGaugeWith("process_max_fds")(_.toDouble) + + /** Virtual memory size in bytes. */ + private val virtualMemorySize: Gauge[Double] = + ZIOMetric.setGauge("process_virtual_memory_bytes") + + /** Resident memory size in bytes. */ + private val residentMemorySize: Gauge[Double] = + ZIOMetric.setGauge("process_resident_memory_bytes") + + class MXReflection(getterName: String, obj: PlatformManagedObject) { + private val cls: Class[_ <: PlatformManagedObject] = obj.getClass + private val method: Option[Method] = findGetter(Try(cls.getMethod(getterName))) + + def isAvailable: Boolean = method.isDefined + + def unsafeGet: Task[Long] = + method match { + case Some(getter) => Task(getter.invoke(obj).asInstanceOf[Long]) + case None => + ZIO.fail(new IllegalStateException(s"MXReflection#get called on unavailable metri")) + } + + private def findGetter(getter: Try[Method]): Option[Method] = + getter match { + case Failure(_) => + None + case Success(method) => + try { + val _ = method.invoke(obj).asInstanceOf[Long] + Some(method) + } catch { + case _: IllegalAccessException => + method.getDeclaringClass.getInterfaces.toStream.flatMap { iface => + findGetter(Try(iface.getMethod(getterName))) + }.headOption + } + } + } + + private def reportStandardMetrics( + runtimeMXBean: RuntimeMXBean, + getProcessCPUTime: MXReflection, + getOpenFileDescriptorCount: MXReflection, + getMaxFileDescriptorCount: MXReflection, + isLinux: Boolean + ): ZIO[Any, Throwable, Unit] = + for { + _ <- (getProcessCPUTime.unsafeGet @@ cpuSecondsTotal).when(getProcessCPUTime.isAvailable) + _ <- Task(runtimeMXBean.getStartTime) @@ processStartTime + _ <- (getOpenFileDescriptorCount.unsafeGet @@ openFdCount).when( + getOpenFileDescriptorCount.isAvailable + ) + _ <- (getMaxFileDescriptorCount.unsafeGet @@ maxFdCount).when( + getMaxFileDescriptorCount.isAvailable + ) + _ <- collectMemoryMetricsLinux().when(isLinux) + } yield () + + private def collectMemoryMetricsLinux(): ZIO[Any, Throwable, Unit] = + ZManaged.readFile("/proc/self/status").use { stream => + stream + .readAll(8192) + .catchAll { + case None => ZIO.succeed(Chunk.empty) + case Some(error) => ZIO.fail(error) + } + .flatMap { bytes => + Task(new String(bytes.toArray, StandardCharsets.US_ASCII)).flatMap { raw => + ZIO.foreachDiscard(raw.split('\n')) { line => + if (line.startsWith("VmSize:")) { + Task(line.split("\\s+")(1).toDouble * 1024.0) @@ virtualMemorySize + } else if (line.startsWith("VmRSS:")) { + Task(line.split("\\s+")(1).toDouble * 1024.0) @@ residentMemorySize + } else { + ZIO.unit + } + } + } + } + } + + override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = + ZManaged.acquireReleaseWith { + for { + runtimeMXBean <- Task(ManagementFactory.getRuntimeMXBean) + operatingSystemMXBean <- Task(ManagementFactory.getOperatingSystemMXBean) + getProcessCpuTime = new MXReflection("getProcessCpuTime", operatingSystemMXBean) + getOpenFileDescriptorCount = + new MXReflection("getOpenFileDescriptorCount", operatingSystemMXBean) + getMaxFileDescriptorCount = + new MXReflection("getMaxFileDescriptorCount", operatingSystemMXBean) + isLinux <- Task(operatingSystemMXBean.getName.indexOf("Linux") == 0) + fiber <- + reportStandardMetrics( + runtimeMXBean, + getProcessCpuTime, + getOpenFileDescriptorCount, + getMaxFileDescriptorCount, + isLinux + ) + .repeat(collectionSchedule) + .interruptible + .forkDaemon + } yield fiber + }(_.interrupt).unit +} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala b/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala new file mode 100644 index 000000000000..e23ac33c4ed2 --- /dev/null +++ b/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala @@ -0,0 +1,79 @@ +package zio.metrics.jvm + +import zio.ZIOMetric.Gauge +import zio._ + +import java.lang.management.{ManagementFactory, ThreadMXBean} + +object Thread extends JvmMetrics { + + /** Current thread count of a JVM */ + private val threadsCurrent: Gauge[Int] = + ZIOMetric.setGaugeWith("jvm_threads_current")(_.toDouble) + + /** Daemon thread count of a JVM */ + private val threadsDaemon: Gauge[Int] = + ZIOMetric.setGaugeWith("jvm_threads_daemon")(_.toDouble) + + /** Peak thread count of a JVM */ + private val threadsPeak: Gauge[Int] = + ZIOMetric.setGaugeWith("jvm_threads_peak")(_.toDouble) + + /** Started thread count of a JVM */ + private val threadsStartedTotal: Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_threads_started_total")( + _.toDouble + ) // NOTE: this is a counter in the prometheus hotspot library (but explicitly set to an actual value) + + /** Cycles of JVM-threads that are in deadlock waiting to acquire object monitors or ownable synchronizers */ + private val threadsDeadlocked: Gauge[Int] = + ZIOMetric.setGaugeWith("jvm_threads_deadlocked")(_.toDouble) + + /** Cycles of JVM-threads that are in deadlock waiting to acquire object monitors */ + private val threadsDeadlockedMonitor: Gauge[Int] = + ZIOMetric.setGaugeWith("jvm_threads_deadlocked_monitor")(_.toDouble) + + /** Current count of threads by state */ + private def threadsState(state: java.lang.Thread.State): Gauge[Long] = + ZIOMetric.setGaugeWith("jvm_threads_state", MetricLabel("state", state.name()))(_.toDouble) + + private def getThreadStateCounts( + threadMXBean: ThreadMXBean + ): Task[Map[java.lang.Thread.State, Long]] = + for { + allThreads <- Task(threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds, 0)) + initial = java.lang.Thread.State.values().map(_ -> 0L).toMap + result = allThreads.foldLeft(initial) { (result, thread) => + if (thread != null) { + result.updated(thread.getThreadState, result(thread.getThreadState) + 1) + } else result + } + } yield result + + private def reportThreadMetrics(threadMXBean: ThreadMXBean): ZIO[Any, Throwable, Unit] = + for { + _ <- Task(threadMXBean.getThreadCount) @@ threadsCurrent + _ <- Task(threadMXBean.getDaemonThreadCount) @@ threadsDaemon + _ <- Task(threadMXBean.getPeakThreadCount) @@ threadsPeak + _ <- Task(threadMXBean.getTotalStartedThreadCount) @@ threadsStartedTotal + _ <- Task( + Option(threadMXBean.findDeadlockedThreads()).map(_.length).getOrElse(0) + ) @@ threadsDeadlocked + _ <- Task( + Option(threadMXBean.findMonitorDeadlockedThreads()).map(_.length).getOrElse(0) + ) @@ threadsDeadlockedMonitor + threadStateCounts <- getThreadStateCounts(threadMXBean) + _ <- ZIO.foreachDiscard(threadStateCounts) { case (state, count) => + UIO(count) @@ threadsState(state) + } + } yield () + + override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = + ZManaged.acquireReleaseWith { + for { + threadMXBean <- Task(ManagementFactory.getThreadMXBean) + fiber <- + reportThreadMetrics(threadMXBean).repeat(collectionSchedule).interruptible.forkDaemon + } yield fiber + }(_.interrupt).unit +} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala b/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala new file mode 100644 index 000000000000..eff086b3c5f8 --- /dev/null +++ b/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala @@ -0,0 +1,31 @@ +package zio.metrics.jvm + +import zio.ZIOMetric.Gauge +import zio._ + +object VersionInfo extends JvmMetrics { + + /** JVM version info */ + def jvmInfo(version: String, vendor: String, runtime: String): Gauge[Unit] = + ZIOMetric.setGaugeWith( + "jvm_info", + MetricLabel("version", version), + MetricLabel("vendor", vendor), + MetricLabel("runtime", runtime) + )(_ => 1.0) + + private def reportVersions(): ZIO[Has[System], Throwable, Unit] = + for { + version <- System.propertyOrElse("java.runtime.version", "unknown") + vendor <- System.propertyOrElse("java.vm.vendor", "unknown") + runtime <- System.propertyOrElse("java.runtime.name", "unknown") + _ <- ZIO.unit @@ jvmInfo(version, vendor, runtime) + } yield () + + override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = + ZManaged.acquireReleaseWith { + for { + fiber <- reportVersions().repeat(collectionSchedule).interruptible.forkDaemon + } yield fiber + }(_.interrupt).unit +} From b281742342ae255e5c15f51c90ce28a64480aa9c Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Sat, 11 Sep 2021 09:45:22 +0200 Subject: [PATCH 2/8] Fixed Scala version specific deprecation warnings --- .../metrics/jvm/JvmMetricsVersionSpecific.scala | 11 +++++++++++ .../metrics/jvm/JvmMetricsVersionSpecific.scala | 11 +++++++++++ .../main/scala/zio/metrics/jvm/BufferPools.scala | 4 +--- .../scala/zio/metrics/jvm/GarbageCollector.scala | 4 +--- .../main/scala/zio/metrics/jvm/JvmMetrics.scala | 2 +- .../scala/zio/metrics/jvm/MemoryAllocation.scala | 10 ++++------ .../main/scala/zio/metrics/jvm/MemoryPools.scala | 4 +--- .../src/main/scala/zio/metrics/jvm/Standard.scala | 14 +++++++++++--- 8 files changed, 41 insertions(+), 19 deletions(-) create mode 100644 core/jvm/src/main/scala-2.11-2.12/zio/metrics/jvm/JvmMetricsVersionSpecific.scala create mode 100644 core/jvm/src/main/scala-2.13+/zio/metrics/jvm/JvmMetricsVersionSpecific.scala diff --git a/core/jvm/src/main/scala-2.11-2.12/zio/metrics/jvm/JvmMetricsVersionSpecific.scala b/core/jvm/src/main/scala-2.11-2.12/zio/metrics/jvm/JvmMetricsVersionSpecific.scala new file mode 100644 index 000000000000..a8c4e2f4bce4 --- /dev/null +++ b/core/jvm/src/main/scala-2.11-2.12/zio/metrics/jvm/JvmMetricsVersionSpecific.scala @@ -0,0 +1,11 @@ +package zio.metrics.jvm + +import scala.collection.JavaConverters._ + +trait JvmMetricsVersionSpecific { + def fromJavaList[A](jlist: java.util.List[A]): Iterable[A] = + jlist.asScala + + def fromJavaSet[A](jset: java.util.Set[A]): Iterable[A] = + jset.asScala +} diff --git a/core/jvm/src/main/scala-2.13+/zio/metrics/jvm/JvmMetricsVersionSpecific.scala b/core/jvm/src/main/scala-2.13+/zio/metrics/jvm/JvmMetricsVersionSpecific.scala new file mode 100644 index 000000000000..cc1051c03cd4 --- /dev/null +++ b/core/jvm/src/main/scala-2.13+/zio/metrics/jvm/JvmMetricsVersionSpecific.scala @@ -0,0 +1,11 @@ +package zio.metrics.jvm + +import scala.jdk.CollectionConverters._ + +trait JvmMetricsVersionSpecific { + def fromJavaList[A](jlist: java.util.List[A]): Iterable[A] = + jlist.asScala + + def fromJavaSet[A](jset: java.util.Set[A]): Iterable[A] = + jset.asScala +} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala b/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala index 32a10f5ce4a4..bca452cee878 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala @@ -4,8 +4,6 @@ import zio._ import java.lang.management.{BufferPoolMXBean, ManagementFactory} -import scala.collection.JavaConverters._ - object BufferPools extends JvmMetrics { /** Used bytes of a given JVM buffer pool. */ @@ -36,7 +34,7 @@ object BufferPools extends JvmMetrics { ZManaged.acquireReleaseWith { for { bufferPoolMXBeans <- - Task(ManagementFactory.getPlatformMXBeans(classOf[BufferPoolMXBean]).asScala.toList) + Task(fromJavaList(ManagementFactory.getPlatformMXBeans(classOf[BufferPoolMXBean])).toList) fiber <- reportBufferPoolMetrics(bufferPoolMXBeans) .repeat(collectionSchedule) diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala b/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala index ef275d8ccb44..df32b7053f68 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala @@ -5,8 +5,6 @@ import zio._ import java.lang.management.{GarbageCollectorMXBean, ManagementFactory} -import scala.collection.JavaConverters._ - /** Exports metrics related to the garbage collector */ object GarbageCollector extends JvmMetrics { @@ -31,7 +29,7 @@ object GarbageCollector extends JvmMetrics { val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = ZManaged.acquireReleaseWith { for { - classLoadingMXBean <- Task(ManagementFactory.getGarbageCollectorMXBeans.asScala.toList) + classLoadingMXBean <- Task(fromJavaList(ManagementFactory.getGarbageCollectorMXBeans).toList) fiber <- reportGarbageCollectionMetrics(classLoadingMXBean) .repeat(collectionSchedule) diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala b/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala index ea0386ce788e..7ced99397c76 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala @@ -2,7 +2,7 @@ package zio.metrics.jvm import zio._ -trait JvmMetrics { +trait JvmMetrics extends JvmMetricsVersionSpecific { protected val collectionSchedule: Schedule[Any, Any, Unit] = Schedule.fixed(10.seconds).unit val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala index 03de4d2e2d97..e4edfcc45765 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala @@ -8,7 +8,6 @@ import java.lang.management.ManagementFactory import javax.management.openmbean.CompositeData import javax.management.{Notification, NotificationEmitter, NotificationListener} import scala.collection.mutable -import scala.collection.JavaConverters._ object MemoryAllocation extends JvmMetrics { @@ -25,7 +24,7 @@ object MemoryAllocation extends JvmMetrics { val gcInfo = info.getGcInfo val memoryUsageBeforeGc = gcInfo.getMemoryUsageBeforeGc val memoryUsageAfterGc = gcInfo.getMemoryUsageAfterGc - for (entry <- memoryUsageBeforeGc.entrySet.asScala) { + for (entry <- fromJavaSet(memoryUsageBeforeGc.entrySet)) { val memoryPool = entry.getKey val before = entry.getValue.getUsed val after = memoryUsageAfterGc.get(memoryPool).getUsed @@ -59,9 +58,8 @@ object MemoryAllocation extends JvmMetrics { if (diff2 < 0) diff2 = 0 val increase = diff1 + diff2 if (increase > 0) { - runtime.unsafeRun { - (UIO(increase) @@ countAllocations(memoryPool)).unit - } + val effect: ZIO[Any, Nothing, Long] = UIO(increase) @@ countAllocations(memoryPool) + runtime.unsafeRun(effect.unit) } } } @@ -72,7 +70,7 @@ object MemoryAllocation extends JvmMetrics { for { runtime <- ZIO.runtime[Any] listener = new Listener(runtime) - garbageCollectorMXBeans <- Task(ManagementFactory.getGarbageCollectorMXBeans.asScala) + garbageCollectorMXBeans <- Task(fromJavaList(ManagementFactory.getGarbageCollectorMXBeans)) _ <- ZIO.foreachDiscard(garbageCollectorMXBeans) { case emitter: NotificationEmitter => Task(emitter.addNotificationListener(listener, null, null)) diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala index 5fe0017e09e5..518ca77bdbce 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala @@ -5,8 +5,6 @@ import zio._ import java.lang.management.{ManagementFactory, MemoryMXBean, MemoryPoolMXBean, MemoryUsage} -import scala.collection.JavaConverters._ - object MemoryPools extends JvmMetrics { sealed private trait Area { val label: String } @@ -83,7 +81,7 @@ object MemoryPools extends JvmMetrics { ZManaged.acquireReleaseWith { for { memoryMXBean <- Task(ManagementFactory.getMemoryMXBean) - poolMXBeans <- Task(ManagementFactory.getMemoryPoolMXBeans.asScala.toList) + poolMXBeans <- Task(fromJavaList(ManagementFactory.getMemoryPoolMXBeans).toList) fiber <- reportMemoryMetrics(memoryMXBean, poolMXBeans) .repeat(collectionSchedule) diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala b/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala index 9d77b69d3bee..5cea45877fab 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala @@ -57,9 +57,17 @@ object Standard extends JvmMetrics { Some(method) } catch { case _: IllegalAccessException => - method.getDeclaringClass.getInterfaces.toStream.flatMap { iface => - findGetter(Try(iface.getMethod(getterName))) - }.headOption + var result: Option[Method] = None + var idx = 0 + val ifaces = method.getDeclaringClass.getInterfaces + + while (idx < ifaces.length && result.isEmpty) { + val iface = ifaces(idx) + result = findGetter(Try(iface.getMethod(getterName))) + idx = idx + 1 + } + + result } } } From c9c1563ea73e6fd952ceb5e7521fb848d71fafa3 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Sat, 11 Sep 2021 12:04:36 +0200 Subject: [PATCH 3/8] A ZIOApp that collects the default JVM metrics --- .../scala/zio/metrics/jvm/DefaultJvmMetrics.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala b/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala index 26115d4d0ea4..d342d4766c00 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala @@ -22,3 +22,16 @@ object DefaultJvmMetrics { val live: ZLayer[Has[Clock] with Has[System], Throwable, Has[DefaultJvmMetrics]] = collectDefaultJvmMetrics.as(new DefaultJvmMetrics {}).toLayer } + +/** A ZIO application that collects the same JVM metrics as the Prometheus Java client's default exporters. */ +object DefaultJvmMetricsExporter extends ZIOApp { + override val tag: Tag[Environment] = Tag[Has[DefaultJvmMetrics]] + + override type Environment = Has[DefaultJvmMetrics] + + override def layer: ZLayer[Has[ZIOAppArgs], Any, Has[DefaultJvmMetrics]] = + Clock.live ++ System.live >>> DefaultJvmMetrics.live + + override def run: ZIO[Has[DefaultJvmMetrics] with Has[ZIOAppArgs], Any, Any] = + ZIO.unit +} From 5a0dce7ace83123351a5c9470c560ef803ec26f2 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Mon, 27 Sep 2021 21:08:08 +0200 Subject: [PATCH 4/8] Deleted JvmMetricsVersionSpecific, using silencer instead --- .../zio/metrics/jvm/JvmMetricsVersionSpecific.scala | 11 ----------- .../zio/metrics/jvm/JvmMetricsVersionSpecific.scala | 11 ----------- .../src/main/scala/zio/metrics/jvm/BufferPools.scala | 7 ++++++- .../main/scala/zio/metrics/jvm/GarbageCollector.scala | 7 ++++++- .../src/main/scala/zio/metrics/jvm/JvmMetrics.scala | 2 +- .../main/scala/zio/metrics/jvm/MemoryAllocation.scala | 9 +++++++-- .../src/main/scala/zio/metrics/jvm/MemoryPools.scala | 7 ++++++- 7 files changed, 26 insertions(+), 28 deletions(-) delete mode 100644 core/jvm/src/main/scala-2.11-2.12/zio/metrics/jvm/JvmMetricsVersionSpecific.scala delete mode 100644 core/jvm/src/main/scala-2.13+/zio/metrics/jvm/JvmMetricsVersionSpecific.scala diff --git a/core/jvm/src/main/scala-2.11-2.12/zio/metrics/jvm/JvmMetricsVersionSpecific.scala b/core/jvm/src/main/scala-2.11-2.12/zio/metrics/jvm/JvmMetricsVersionSpecific.scala deleted file mode 100644 index a8c4e2f4bce4..000000000000 --- a/core/jvm/src/main/scala-2.11-2.12/zio/metrics/jvm/JvmMetricsVersionSpecific.scala +++ /dev/null @@ -1,11 +0,0 @@ -package zio.metrics.jvm - -import scala.collection.JavaConverters._ - -trait JvmMetricsVersionSpecific { - def fromJavaList[A](jlist: java.util.List[A]): Iterable[A] = - jlist.asScala - - def fromJavaSet[A](jset: java.util.Set[A]): Iterable[A] = - jset.asScala -} diff --git a/core/jvm/src/main/scala-2.13+/zio/metrics/jvm/JvmMetricsVersionSpecific.scala b/core/jvm/src/main/scala-2.13+/zio/metrics/jvm/JvmMetricsVersionSpecific.scala deleted file mode 100644 index cc1051c03cd4..000000000000 --- a/core/jvm/src/main/scala-2.13+/zio/metrics/jvm/JvmMetricsVersionSpecific.scala +++ /dev/null @@ -1,11 +0,0 @@ -package zio.metrics.jvm - -import scala.jdk.CollectionConverters._ - -trait JvmMetricsVersionSpecific { - def fromJavaList[A](jlist: java.util.List[A]): Iterable[A] = - jlist.asScala - - def fromJavaSet[A](jset: java.util.Set[A]): Iterable[A] = - jset.asScala -} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala b/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala index bca452cee878..f6b6397da503 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala @@ -1,9 +1,13 @@ package zio.metrics.jvm +import com.github.ghik.silencer.silent + import zio._ import java.lang.management.{BufferPoolMXBean, ManagementFactory} +import scala.collection.JavaConverters._ + object BufferPools extends JvmMetrics { /** Used bytes of a given JVM buffer pool. */ @@ -30,11 +34,12 @@ object BufferPools extends JvmMetrics { } yield () } + @silent("JavaConverters") val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = ZManaged.acquireReleaseWith { for { bufferPoolMXBeans <- - Task(fromJavaList(ManagementFactory.getPlatformMXBeans(classOf[BufferPoolMXBean])).toList) + Task(ManagementFactory.getPlatformMXBeans(classOf[BufferPoolMXBean]).asScala.toList) fiber <- reportBufferPoolMetrics(bufferPoolMXBeans) .repeat(collectionSchedule) diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala b/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala index df32b7053f68..e03d9ba4bad1 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala @@ -1,10 +1,14 @@ package zio.metrics.jvm +import com.github.ghik.silencer.silent + import zio.ZIOMetric.Gauge import zio._ import java.lang.management.{GarbageCollectorMXBean, ManagementFactory} +import scala.collection.JavaConverters._ + /** Exports metrics related to the garbage collector */ object GarbageCollector extends JvmMetrics { @@ -26,10 +30,11 @@ object GarbageCollector extends JvmMetrics { } yield () } + @silent("JavaConverters") val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = ZManaged.acquireReleaseWith { for { - classLoadingMXBean <- Task(fromJavaList(ManagementFactory.getGarbageCollectorMXBeans).toList) + classLoadingMXBean <- Task(ManagementFactory.getGarbageCollectorMXBeans.asScala.toList) fiber <- reportGarbageCollectionMetrics(classLoadingMXBean) .repeat(collectionSchedule) diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala b/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala index 7ced99397c76..ea0386ce788e 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala @@ -2,7 +2,7 @@ package zio.metrics.jvm import zio._ -trait JvmMetrics extends JvmMetricsVersionSpecific { +trait JvmMetrics { protected val collectionSchedule: Schedule[Any, Any, Unit] = Schedule.fixed(10.seconds).unit val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala index e4edfcc45765..24cb9c0cf890 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala @@ -1,5 +1,7 @@ package zio.metrics.jvm +import com.github.ghik.silencer.silent + import com.sun.management.GarbageCollectionNotificationInfo import zio._ import zio.ZIOMetric.Counter @@ -8,6 +10,7 @@ import java.lang.management.ManagementFactory import javax.management.openmbean.CompositeData import javax.management.{Notification, NotificationEmitter, NotificationListener} import scala.collection.mutable +import scala.collection.JavaConverters._ object MemoryAllocation extends JvmMetrics { @@ -18,13 +21,14 @@ object MemoryAllocation extends JvmMetrics { private class Listener(runtime: Runtime[Any]) extends NotificationListener { private val lastMemoryUsage: mutable.Map[String, Long] = mutable.HashMap.empty + @silent("JavaConverters") override def handleNotification(notification: Notification, handback: Any): Unit = { val info = GarbageCollectionNotificationInfo.from(notification.getUserData.asInstanceOf[CompositeData]) val gcInfo = info.getGcInfo val memoryUsageBeforeGc = gcInfo.getMemoryUsageBeforeGc val memoryUsageAfterGc = gcInfo.getMemoryUsageAfterGc - for (entry <- fromJavaSet(memoryUsageBeforeGc.entrySet)) { + for (entry <- memoryUsageBeforeGc.entrySet.asScala) { val memoryPool = entry.getKey val before = entry.getValue.getUsed val after = memoryUsageAfterGc.get(memoryPool).getUsed @@ -64,13 +68,14 @@ object MemoryAllocation extends JvmMetrics { } } + @silent("JavaConverters") override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = ZManaged .acquireReleaseWith( for { runtime <- ZIO.runtime[Any] listener = new Listener(runtime) - garbageCollectorMXBeans <- Task(fromJavaList(ManagementFactory.getGarbageCollectorMXBeans)) + garbageCollectorMXBeans <- Task(ManagementFactory.getGarbageCollectorMXBeans.asScala) _ <- ZIO.foreachDiscard(garbageCollectorMXBeans) { case emitter: NotificationEmitter => Task(emitter.addNotificationListener(listener, null, null)) diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala index 518ca77bdbce..c60c794c51f6 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala @@ -1,10 +1,14 @@ package zio.metrics.jvm +import com.github.ghik.silencer.silent + import zio.ZIOMetric.Gauge import zio._ import java.lang.management.{ManagementFactory, MemoryMXBean, MemoryPoolMXBean, MemoryUsage} +import scala.collection.JavaConverters._ + object MemoryPools extends JvmMetrics { sealed private trait Area { val label: String } @@ -77,11 +81,12 @@ object MemoryPools extends JvmMetrics { } } yield () + @silent("JavaConverters") val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = ZManaged.acquireReleaseWith { for { memoryMXBean <- Task(ManagementFactory.getMemoryMXBean) - poolMXBeans <- Task(fromJavaList(ManagementFactory.getMemoryPoolMXBeans).toList) + poolMXBeans <- Task(ManagementFactory.getMemoryPoolMXBeans.asScala.toList) fiber <- reportMemoryMetrics(memoryMXBean, poolMXBeans) .repeat(collectionSchedule) From 67be6313f1fac03c0abfaf8a5d9781e44eefde2e Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Mon, 27 Sep 2021 21:17:13 +0200 Subject: [PATCH 5/8] Simplified collectMetrics with forkManaged --- .../scala/zio/metrics/jvm/BufferPools.scala | 20 ++++----- .../scala/zio/metrics/jvm/ClassLoading.scala | 18 ++++---- .../zio/metrics/jvm/GarbageCollector.scala | 17 +++---- .../scala/zio/metrics/jvm/MemoryPools.scala | 19 ++++---- .../main/scala/zio/metrics/jvm/Standard.scala | 44 +++++++++---------- .../main/scala/zio/metrics/jvm/Thread.scala | 12 +++-- .../scala/zio/metrics/jvm/VersionInfo.scala | 6 +-- 7 files changed, 59 insertions(+), 77 deletions(-) diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala b/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala index f6b6397da503..629658637347 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala @@ -36,15 +36,13 @@ object BufferPools extends JvmMetrics { @silent("JavaConverters") val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = - ZManaged.acquireReleaseWith { - for { - bufferPoolMXBeans <- - Task(ManagementFactory.getPlatformMXBeans(classOf[BufferPoolMXBean]).asScala.toList) - fiber <- - reportBufferPoolMetrics(bufferPoolMXBeans) - .repeat(collectionSchedule) - .interruptible - .forkDaemon - } yield fiber - }(_.interrupt).unit + for { + bufferPoolMXBeans <- Task( + ManagementFactory.getPlatformMXBeans(classOf[BufferPoolMXBean]).asScala.toList + ).toManaged + _ <- reportBufferPoolMetrics(bufferPoolMXBeans) + .repeat(collectionSchedule) + .interruptible + .forkManaged + } yield () } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala b/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala index 86b4176fcb30..8ba201610bf8 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala @@ -30,14 +30,12 @@ object ClassLoading extends JvmMetrics { } yield () val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = - ZManaged.acquireReleaseWith { - for { - classLoadingMXBean <- - Task(ManagementFactory.getPlatformMXBean(classOf[ClassLoadingMXBean])) - fiber <- reportClassLoadingMetrics(classLoadingMXBean) - .repeat(collectionSchedule) - .interruptible - .forkDaemon - } yield fiber - }(_.interrupt).unit + for { + classLoadingMXBean <- + Task(ManagementFactory.getPlatformMXBean(classOf[ClassLoadingMXBean])).toManaged + _ <- reportClassLoadingMetrics(classLoadingMXBean) + .repeat(collectionSchedule) + .interruptible + .forkManaged + } yield () } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala b/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala index e03d9ba4bad1..a0b8bdbe2fe5 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala @@ -32,14 +32,11 @@ object GarbageCollector extends JvmMetrics { @silent("JavaConverters") val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = - ZManaged.acquireReleaseWith { - for { - classLoadingMXBean <- Task(ManagementFactory.getGarbageCollectorMXBeans.asScala.toList) - fiber <- - reportGarbageCollectionMetrics(classLoadingMXBean) - .repeat(collectionSchedule) - .interruptible - .forkDaemon - } yield fiber - }(_.interrupt).unit + for { + classLoadingMXBean <- Task(ManagementFactory.getGarbageCollectorMXBeans.asScala.toList).toManaged + _ <- reportGarbageCollectionMetrics(classLoadingMXBean) + .repeat(collectionSchedule) + .interruptible + .forkManaged + } yield () } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala index c60c794c51f6..5a614fd015a8 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala @@ -83,15 +83,12 @@ object MemoryPools extends JvmMetrics { @silent("JavaConverters") val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = - ZManaged.acquireReleaseWith { - for { - memoryMXBean <- Task(ManagementFactory.getMemoryMXBean) - poolMXBeans <- Task(ManagementFactory.getMemoryPoolMXBeans.asScala.toList) - fiber <- - reportMemoryMetrics(memoryMXBean, poolMXBeans) - .repeat(collectionSchedule) - .interruptible - .forkDaemon - } yield fiber - }(_.interrupt).unit + for { + memoryMXBean <- Task(ManagementFactory.getMemoryMXBean).toManaged + poolMXBeans <- Task(ManagementFactory.getMemoryPoolMXBeans.asScala.toList).toManaged + _ <- reportMemoryMetrics(memoryMXBean, poolMXBeans) + .repeat(collectionSchedule) + .interruptible + .forkManaged + } yield () } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala b/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala index 5cea45877fab..26fee64ee7c3 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala @@ -115,27 +115,25 @@ object Standard extends JvmMetrics { } override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = - ZManaged.acquireReleaseWith { - for { - runtimeMXBean <- Task(ManagementFactory.getRuntimeMXBean) - operatingSystemMXBean <- Task(ManagementFactory.getOperatingSystemMXBean) - getProcessCpuTime = new MXReflection("getProcessCpuTime", operatingSystemMXBean) - getOpenFileDescriptorCount = - new MXReflection("getOpenFileDescriptorCount", operatingSystemMXBean) - getMaxFileDescriptorCount = - new MXReflection("getMaxFileDescriptorCount", operatingSystemMXBean) - isLinux <- Task(operatingSystemMXBean.getName.indexOf("Linux") == 0) - fiber <- - reportStandardMetrics( - runtimeMXBean, - getProcessCpuTime, - getOpenFileDescriptorCount, - getMaxFileDescriptorCount, - isLinux - ) - .repeat(collectionSchedule) - .interruptible - .forkDaemon - } yield fiber - }(_.interrupt).unit + for { + runtimeMXBean <- Task(ManagementFactory.getRuntimeMXBean).toManaged + operatingSystemMXBean <- Task(ManagementFactory.getOperatingSystemMXBean).toManaged + getProcessCpuTime = new MXReflection("getProcessCpuTime", operatingSystemMXBean) + getOpenFileDescriptorCount = + new MXReflection("getOpenFileDescriptorCount", operatingSystemMXBean) + getMaxFileDescriptorCount = + new MXReflection("getMaxFileDescriptorCount", operatingSystemMXBean) + isLinux <- Task(operatingSystemMXBean.getName.indexOf("Linux") == 0).toManaged + _ <- + reportStandardMetrics( + runtimeMXBean, + getProcessCpuTime, + getOpenFileDescriptorCount, + getMaxFileDescriptorCount, + isLinux + ) + .repeat(collectionSchedule) + .interruptible + .forkManaged + } yield () } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala b/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala index e23ac33c4ed2..16bbefff4b33 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala @@ -69,11 +69,9 @@ object Thread extends JvmMetrics { } yield () override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = - ZManaged.acquireReleaseWith { - for { - threadMXBean <- Task(ManagementFactory.getThreadMXBean) - fiber <- - reportThreadMetrics(threadMXBean).repeat(collectionSchedule).interruptible.forkDaemon - } yield fiber - }(_.interrupt).unit + for { + threadMXBean <- Task(ManagementFactory.getThreadMXBean).toManaged + _ <- + reportThreadMetrics(threadMXBean).repeat(collectionSchedule).interruptible.forkManaged + } yield () } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala b/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala index eff086b3c5f8..26960e030dcc 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala @@ -23,9 +23,5 @@ object VersionInfo extends JvmMetrics { } yield () override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = - ZManaged.acquireReleaseWith { - for { - fiber <- reportVersions().repeat(collectionSchedule).interruptible.forkDaemon - } yield fiber - }(_.interrupt).unit + reportVersions().repeat(collectionSchedule).interruptible.forkManaged.unit } From 8695c2d135de02776fd30b1a9db947c3f44482c0 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Mon, 27 Sep 2021 21:40:49 +0200 Subject: [PATCH 6/8] All JVM metric collectors expose layer and ZIOApp interface --- .../scala/zio/metrics/jvm/BufferPools.scala | 8 ++- .../scala/zio/metrics/jvm/ClassLoading.scala | 8 ++- .../zio/metrics/jvm/DefaultJvmMetrics.scala | 66 +++++++++++-------- .../zio/metrics/jvm/GarbageCollector.scala | 8 ++- .../scala/zio/metrics/jvm/JvmMetrics.scala | 21 +++++- .../zio/metrics/jvm/MemoryAllocation.scala | 8 ++- .../scala/zio/metrics/jvm/MemoryPools.scala | 8 ++- .../main/scala/zio/metrics/jvm/Standard.scala | 8 ++- .../main/scala/zio/metrics/jvm/Thread.scala | 8 ++- .../scala/zio/metrics/jvm/VersionInfo.scala | 8 ++- 10 files changed, 107 insertions(+), 44 deletions(-) diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala b/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala index 629658637347..520531adabeb 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala @@ -8,7 +8,11 @@ import java.lang.management.{BufferPoolMXBean, ManagementFactory} import scala.collection.JavaConverters._ +trait BufferPools + object BufferPools extends JvmMetrics { + override type Feature = BufferPools + override val featureTag: Tag[BufferPools] = Tag[BufferPools] /** Used bytes of a given JVM buffer pool. */ private def bufferPoolUsedBytes(pool: String): ZIOMetric.Gauge[Long] = @@ -35,7 +39,7 @@ object BufferPools extends JvmMetrics { } @silent("JavaConverters") - val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = + val collectMetrics: ZManaged[Has[Clock], Throwable, BufferPools] = for { bufferPoolMXBeans <- Task( ManagementFactory.getPlatformMXBeans(classOf[BufferPoolMXBean]).asScala.toList @@ -44,5 +48,5 @@ object BufferPools extends JvmMetrics { .repeat(collectionSchedule) .interruptible .forkManaged - } yield () + } yield new BufferPools {} } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala b/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala index 8ba201610bf8..370537e56b78 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala @@ -5,8 +5,12 @@ import zio._ import java.lang.management.{ClassLoadingMXBean, ManagementFactory} +trait ClassLoading + /** Exports metrics related to JVM class loading */ object ClassLoading extends JvmMetrics { + override type Feature = ClassLoading + override val featureTag: Tag[ClassLoading] = Tag[ClassLoading] /** The number of classes that are currently loaded in the JVM */ private val loadedClassCount: Gauge[Int] = @@ -29,7 +33,7 @@ object ClassLoading extends JvmMetrics { _ <- Task(classLoadingMXBean.getUnloadedClassCount) @@ unloadedClassCount } yield () - val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = + val collectMetrics: ZManaged[Has[Clock], Throwable, ClassLoading] = for { classLoadingMXBean <- Task(ManagementFactory.getPlatformMXBean(classOf[ClassLoadingMXBean])).toManaged @@ -37,5 +41,5 @@ object ClassLoading extends JvmMetrics { .repeat(collectionSchedule) .interruptible .forkManaged - } yield () + } yield new ClassLoading {} } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala b/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala index d342d4766c00..356612b0a965 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala @@ -2,36 +2,48 @@ package zio.metrics.jvm import zio._ -trait DefaultJvmMetrics - /** JVM metrics, compatible with the prometheus-hotspot library */ object DefaultJvmMetrics { + + /** + * While acquired it starts fibers periodically updating the same JVM metrics + * as the Prometheus Java client's default exporters + */ val collectDefaultJvmMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = - ( - BufferPools.collectMetrics <&> - ClassLoading.collectMetrics <&> - GarbageCollector.collectMetrics <&> - MemoryAllocation.collectMetrics <&> - MemoryPools.collectMetrics <&> - Standard.collectMetrics <&> - Thread.collectMetrics <&> - VersionInfo.collectMetrics - ).unit + ZManaged.foreachParDiscard( + Seq( + BufferPools, + ClassLoading, + GarbageCollector, + MemoryAllocation, + MemoryPools, + Standard, + Thread, + VersionInfo + ) + )(_.collectMetrics) /** Layer that starts collecting the same JVM metrics as the Prometheus Java client's default exporters */ - val live: ZLayer[Has[Clock] with Has[System], Throwable, Has[DefaultJvmMetrics]] = - collectDefaultJvmMetrics.as(new DefaultJvmMetrics {}).toLayer -} - -/** A ZIO application that collects the same JVM metrics as the Prometheus Java client's default exporters. */ -object DefaultJvmMetricsExporter extends ZIOApp { - override val tag: Tag[Environment] = Tag[Has[DefaultJvmMetrics]] - - override type Environment = Has[DefaultJvmMetrics] - - override def layer: ZLayer[Has[ZIOAppArgs], Any, Has[DefaultJvmMetrics]] = - Clock.live ++ System.live >>> DefaultJvmMetrics.live - - override def run: ZIO[Has[DefaultJvmMetrics] with Has[ZIOAppArgs], Any, Any] = - ZIO.unit + val live: ZLayer[Has[Clock] with Has[System], Throwable, Has[BufferPools] with Has[ClassLoading] with Has[ + GarbageCollector + ] with Has[MemoryAllocation] with Has[MemoryPools] with Has[Standard] with Has[Thread] with Has[VersionInfo]] = + BufferPools.live ++ + ClassLoading.live ++ + GarbageCollector.live ++ + MemoryAllocation.live ++ + MemoryPools.live ++ + Standard.live ++ + Thread.live ++ + VersionInfo.live + + /** A ZIO application that collects the same JVM metrics as the Prometheus Java client's default exporters. */ + val app: ZIOApp = + BufferPools.app <> + ClassLoading.app <> + GarbageCollector.app <> + MemoryAllocation.app <> + MemoryPools.app <> + Standard.app <> + Thread.app <> + VersionInfo.app } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala b/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala index a0b8bdbe2fe5..75a39d52d686 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala @@ -9,8 +9,12 @@ import java.lang.management.{GarbageCollectorMXBean, ManagementFactory} import scala.collection.JavaConverters._ +trait GarbageCollector + /** Exports metrics related to the garbage collector */ object GarbageCollector extends JvmMetrics { + override type Feature = GarbageCollector + override val featureTag: zio.Tag[GarbageCollector] = Tag[GarbageCollector] /** Time spent in a given JVM garbage collector in seconds. */ private def gcCollectionSecondsSum(gc: String): Gauge[Long] = @@ -31,12 +35,12 @@ object GarbageCollector extends JvmMetrics { } @silent("JavaConverters") - val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = + val collectMetrics: ZManaged[Has[Clock], Throwable, GarbageCollector] = for { classLoadingMXBean <- Task(ManagementFactory.getGarbageCollectorMXBeans.asScala.toList).toManaged _ <- reportGarbageCollectionMetrics(classLoadingMXBean) .repeat(collectionSchedule) .interruptible .forkManaged - } yield () + } yield new GarbageCollector {} } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala b/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala index ea0386ce788e..6e2d15d92a5d 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala @@ -1,9 +1,28 @@ package zio.metrics.jvm +import com.github.ghik.silencer.silent import zio._ trait JvmMetrics { + type Feature + val featureTag: Tag[Feature] + protected val collectionSchedule: Schedule[Any, Any, Unit] = Schedule.fixed(10.seconds).unit - val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] + val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Feature] + + /** A layer that when constructed forks a fiber that periodically updates the JVM metrics */ + lazy val live: ZLayer[Has[Clock] with Has[System], Throwable, Has[Feature]] = + collectMetrics.toLayer(featureTag) + + /** A ZIO application that periodically updates the JVM metrics */ + lazy val app: ZIOApp = new ZIOApp { + @silent private implicit val ftag: zio.Tag[Feature] = featureTag + override val tag: Tag[Environment] = Tag[Environment] + override type Environment = Has[Clock] with Has[System] with Has[Feature] + override val layer: ZLayer[Has[ZIOAppArgs], Any, Environment] = { + Clock.live ++ System.live >+> live + } + override def run: ZIO[Environment with Has[ZIOAppArgs], Any, Any] = ZIO.unit + } } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala index 24cb9c0cf890..4f22aad12daf 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala @@ -12,7 +12,11 @@ import javax.management.{Notification, NotificationEmitter, NotificationListener import scala.collection.mutable import scala.collection.JavaConverters._ +trait MemoryAllocation + object MemoryAllocation extends JvmMetrics { + override type Feature = MemoryAllocation + override val featureTag: Tag[MemoryAllocation] = Tag[MemoryAllocation] /** Total bytes allocated in a given JVM memory pool. Only updated after GC, not continuously. */ private def countAllocations(pool: String): Counter[Long] = @@ -69,7 +73,7 @@ object MemoryAllocation extends JvmMetrics { } @silent("JavaConverters") - override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = + override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, MemoryAllocation] = ZManaged .acquireReleaseWith( for { @@ -91,5 +95,5 @@ object MemoryAllocation extends JvmMetrics { } .orDie } - .unit + .as(new MemoryAllocation {}) } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala index 5a614fd015a8..3af1f7365c12 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala @@ -9,7 +9,11 @@ import java.lang.management.{ManagementFactory, MemoryMXBean, MemoryPoolMXBean, import scala.collection.JavaConverters._ +trait MemoryPools + object MemoryPools extends JvmMetrics { + override type Feature = MemoryPools + override val featureTag: Tag[MemoryPools] = Tag[MemoryPools] sealed private trait Area { val label: String } private case object Heap extends Area { override val label: String = "heap" } @@ -82,7 +86,7 @@ object MemoryPools extends JvmMetrics { } yield () @silent("JavaConverters") - val collectMetrics: ZManaged[Has[Clock], Throwable, Unit] = + val collectMetrics: ZManaged[Has[Clock], Throwable, MemoryPools] = for { memoryMXBean <- Task(ManagementFactory.getMemoryMXBean).toManaged poolMXBeans <- Task(ManagementFactory.getMemoryPoolMXBeans.asScala.toList).toManaged @@ -90,5 +94,5 @@ object MemoryPools extends JvmMetrics { .repeat(collectionSchedule) .interruptible .forkManaged - } yield () + } yield new MemoryPools {} } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala b/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala index 26fee64ee7c3..1e77dd1b9af9 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala @@ -8,7 +8,11 @@ import java.lang.reflect.Method import java.nio.charset.StandardCharsets import scala.util.{Failure, Success, Try} +trait Standard + object Standard extends JvmMetrics { + override type Feature = Standard + override val featureTag: Tag[Standard] = Tag[Standard] /** Total user and system CPU time spent in seconds. */ private val cpuSecondsTotal: Gauge[Long] = @@ -114,7 +118,7 @@ object Standard extends JvmMetrics { } } - override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = + override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Standard] = for { runtimeMXBean <- Task(ManagementFactory.getRuntimeMXBean).toManaged operatingSystemMXBean <- Task(ManagementFactory.getOperatingSystemMXBean).toManaged @@ -135,5 +139,5 @@ object Standard extends JvmMetrics { .repeat(collectionSchedule) .interruptible .forkManaged - } yield () + } yield new Standard {} } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala b/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala index 16bbefff4b33..b8f1aed1cd45 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala @@ -5,7 +5,11 @@ import zio._ import java.lang.management.{ManagementFactory, ThreadMXBean} +trait Thread + object Thread extends JvmMetrics { + override type Feature = Thread + override val featureTag: Tag[Thread] = Tag[Thread] /** Current thread count of a JVM */ private val threadsCurrent: Gauge[Int] = @@ -68,10 +72,10 @@ object Thread extends JvmMetrics { } } yield () - override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = + override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Thread] = for { threadMXBean <- Task(ManagementFactory.getThreadMXBean).toManaged _ <- reportThreadMetrics(threadMXBean).repeat(collectionSchedule).interruptible.forkManaged - } yield () + } yield new Thread {} } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala b/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala index 26960e030dcc..65e4c15cb906 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala @@ -3,7 +3,11 @@ package zio.metrics.jvm import zio.ZIOMetric.Gauge import zio._ +trait VersionInfo + object VersionInfo extends JvmMetrics { + override type Feature = VersionInfo + override val featureTag: Tag[VersionInfo] = Tag[VersionInfo] /** JVM version info */ def jvmInfo(version: String, vendor: String, runtime: String): Gauge[Unit] = @@ -22,6 +26,6 @@ object VersionInfo extends JvmMetrics { _ <- ZIO.unit @@ jvmInfo(version, vendor, runtime) } yield () - override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = - reportVersions().repeat(collectionSchedule).interruptible.forkManaged.unit + override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, VersionInfo] = + reportVersions().repeat(collectionSchedule).interruptible.forkManaged.as(new VersionInfo {}) } From 395a685d83358b068fb583fca13e988ef53874cd Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Mon, 27 Sep 2021 22:12:42 +0200 Subject: [PATCH 7/8] Configurable schedule --- .../scala/zio/metrics/jvm/BufferPools.scala | 12 +++-- .../scala/zio/metrics/jvm/ClassLoading.scala | 14 +++-- .../zio/metrics/jvm/DefaultJvmMetrics.scala | 52 ++++++++----------- .../zio/metrics/jvm/GarbageCollector.scala | 14 +++-- .../scala/zio/metrics/jvm/JvmMetrics.scala | 13 ++++- .../zio/metrics/jvm/MemoryAllocation.scala | 12 +++-- .../scala/zio/metrics/jvm/MemoryPools.scala | 12 +++-- .../zio/metrics/jvm/MultipleJvmMetrics.scala | 20 +++++++ .../main/scala/zio/metrics/jvm/Standard.scala | 12 +++-- .../main/scala/zio/metrics/jvm/Thread.scala | 12 +++-- .../scala/zio/metrics/jvm/VersionInfo.scala | 12 +++-- 11 files changed, 118 insertions(+), 67 deletions(-) create mode 100644 core/jvm/src/main/scala/zio/metrics/jvm/MultipleJvmMetrics.scala diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala b/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala index 520531adabeb..2bbf7c867057 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/BufferPools.scala @@ -8,9 +8,7 @@ import java.lang.management.{BufferPoolMXBean, ManagementFactory} import scala.collection.JavaConverters._ -trait BufferPools - -object BufferPools extends JvmMetrics { +trait BufferPools extends JvmMetrics { override type Feature = BufferPools override val featureTag: Tag[BufferPools] = Tag[BufferPools] @@ -48,5 +46,11 @@ object BufferPools extends JvmMetrics { .repeat(collectionSchedule) .interruptible .forkManaged - } yield new BufferPools {} + } yield this +} + +object BufferPools extends BufferPools with JvmMetrics.DefaultSchedule { + def withSchedule(schedule: Schedule[Any, Any, Unit]): BufferPools = new BufferPools { + override protected val collectionSchedule: Schedule[Any, Any, Unit] = schedule + } } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala b/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala index 370537e56b78..6883cc89eb0a 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/ClassLoading.scala @@ -5,10 +5,7 @@ import zio._ import java.lang.management.{ClassLoadingMXBean, ManagementFactory} -trait ClassLoading - -/** Exports metrics related to JVM class loading */ -object ClassLoading extends JvmMetrics { +trait ClassLoading extends JvmMetrics { override type Feature = ClassLoading override val featureTag: Tag[ClassLoading] = Tag[ClassLoading] @@ -41,5 +38,12 @@ object ClassLoading extends JvmMetrics { .repeat(collectionSchedule) .interruptible .forkManaged - } yield new ClassLoading {} + } yield this +} + +/** Exports metrics related to JVM class loading */ +object ClassLoading extends ClassLoading with JvmMetrics.DefaultSchedule { + def withSchedule(schedule: Schedule[Any, Any, Unit]): ClassLoading = new ClassLoading { + override protected val collectionSchedule: Schedule[Any, Any, Unit] = schedule + } } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala b/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala index 356612b0a965..67aee95c5463 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/DefaultJvmMetrics.scala @@ -2,31 +2,26 @@ package zio.metrics.jvm import zio._ -/** JVM metrics, compatible with the prometheus-hotspot library */ -object DefaultJvmMetrics { +/** JVM metrics, compatible with the prometheus-hotspot library, with configurable schedule */ +trait DefaultJvmMetrics extends MultipleJvmMetrics { + protected val collectionSchedule: Schedule[Any, Any, Unit] - /** - * While acquired it starts fibers periodically updating the same JVM metrics - * as the Prometheus Java client's default exporters - */ - val collectDefaultJvmMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = - ZManaged.foreachParDiscard( - Seq( - BufferPools, - ClassLoading, - GarbageCollector, - MemoryAllocation, - MemoryPools, - Standard, - Thread, - VersionInfo - ) - )(_.collectMetrics) + override protected lazy val collectors: NonEmptyChunk[JvmMetrics] = + NonEmptyChunk( + BufferPools.withSchedule(collectionSchedule), + ClassLoading.withSchedule(collectionSchedule), + GarbageCollector.withSchedule(collectionSchedule), + MemoryAllocation.withSchedule(collectionSchedule), + MemoryPools.withSchedule(collectionSchedule), + Standard.withSchedule(collectionSchedule), + Thread.withSchedule(collectionSchedule), + VersionInfo.withSchedule(collectionSchedule) + ) /** Layer that starts collecting the same JVM metrics as the Prometheus Java client's default exporters */ - val live: ZLayer[Has[Clock] with Has[System], Throwable, Has[BufferPools] with Has[ClassLoading] with Has[ + lazy val live: ZLayer[Has[Clock] with Has[System], Throwable, Has[BufferPools] with Has[ClassLoading] with Has[ GarbageCollector - ] with Has[MemoryAllocation] with Has[MemoryPools] with Has[Standard] with Has[Thread] with Has[VersionInfo]] = + ] with Has[MemoryAllocation] with Has[MemoryPools] with Has[Standard] with Has[Thread] with Has[VersionInfo]] = { BufferPools.live ++ ClassLoading.live ++ GarbageCollector.live ++ @@ -35,15 +30,10 @@ object DefaultJvmMetrics { Standard.live ++ Thread.live ++ VersionInfo.live + } +} - /** A ZIO application that collects the same JVM metrics as the Prometheus Java client's default exporters. */ - val app: ZIOApp = - BufferPools.app <> - ClassLoading.app <> - GarbageCollector.app <> - MemoryAllocation.app <> - MemoryPools.app <> - Standard.app <> - Thread.app <> - VersionInfo.app +/** JVM metrics, compatible with the prometheus-hotspot library */ +object DefaultJvmMetrics extends DefaultJvmMetrics { + override protected val collectionSchedule: Schedule[Any, Any, Unit] = JvmMetrics.defaultSchedule } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala b/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala index 75a39d52d686..6455f60f8395 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/GarbageCollector.scala @@ -9,10 +9,7 @@ import java.lang.management.{GarbageCollectorMXBean, ManagementFactory} import scala.collection.JavaConverters._ -trait GarbageCollector - -/** Exports metrics related to the garbage collector */ -object GarbageCollector extends JvmMetrics { +trait GarbageCollector extends JvmMetrics { override type Feature = GarbageCollector override val featureTag: zio.Tag[GarbageCollector] = Tag[GarbageCollector] @@ -42,5 +39,12 @@ object GarbageCollector extends JvmMetrics { .repeat(collectionSchedule) .interruptible .forkManaged - } yield new GarbageCollector {} + } yield this +} + +/** Exports metrics related to the garbage collector */ +object GarbageCollector extends GarbageCollector with JvmMetrics.DefaultSchedule { + def withSchedule(schedule: Schedule[Any, Any, Unit]): GarbageCollector = new GarbageCollector { + override protected val collectionSchedule: Schedule[Any, Any, Unit] = schedule + } } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala b/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala index 6e2d15d92a5d..db38f476753c 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/JvmMetrics.scala @@ -3,11 +3,11 @@ package zio.metrics.jvm import com.github.ghik.silencer.silent import zio._ -trait JvmMetrics { +trait JvmMetrics { self => type Feature val featureTag: Tag[Feature] - protected val collectionSchedule: Schedule[Any, Any, Unit] = Schedule.fixed(10.seconds).unit + protected val collectionSchedule: Schedule[Any, Any, Unit] val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Feature] @@ -26,3 +26,12 @@ trait JvmMetrics { override def run: ZIO[Environment with Has[ZIOAppArgs], Any, Any] = ZIO.unit } } + +object JvmMetrics { + val defaultSchedule: Schedule[Any, Any, Unit] = Schedule.fixed(10.seconds).unit + + trait DefaultSchedule { + self: JvmMetrics => + override protected val collectionSchedule: Schedule[Any, Any, Unit] = defaultSchedule + } +} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala index 4f22aad12daf..e2b6d5ab8771 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryAllocation.scala @@ -12,9 +12,7 @@ import javax.management.{Notification, NotificationEmitter, NotificationListener import scala.collection.mutable import scala.collection.JavaConverters._ -trait MemoryAllocation - -object MemoryAllocation extends JvmMetrics { +trait MemoryAllocation extends JvmMetrics { override type Feature = MemoryAllocation override val featureTag: Tag[MemoryAllocation] = Tag[MemoryAllocation] @@ -95,5 +93,11 @@ object MemoryAllocation extends JvmMetrics { } .orDie } - .as(new MemoryAllocation {}) + .as(this) +} + +object MemoryAllocation extends MemoryAllocation with JvmMetrics.DefaultSchedule { + def withSchedule(schedule: Schedule[Any, Any, Unit]): MemoryAllocation = new MemoryAllocation { + override protected val collectionSchedule: Schedule[Any, Any, Unit] = schedule + } } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala index 3af1f7365c12..cb9d4167a9ff 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MemoryPools.scala @@ -9,9 +9,7 @@ import java.lang.management.{ManagementFactory, MemoryMXBean, MemoryPoolMXBean, import scala.collection.JavaConverters._ -trait MemoryPools - -object MemoryPools extends JvmMetrics { +trait MemoryPools extends JvmMetrics { override type Feature = MemoryPools override val featureTag: Tag[MemoryPools] = Tag[MemoryPools] @@ -94,5 +92,11 @@ object MemoryPools extends JvmMetrics { .repeat(collectionSchedule) .interruptible .forkManaged - } yield new MemoryPools {} + } yield this +} + +object MemoryPools extends MemoryPools with JvmMetrics.DefaultSchedule { + def withSchedule(schedule: Schedule[Any, Any, Unit]): MemoryPools = new MemoryPools { + override protected val collectionSchedule: Schedule[Any, Any, Unit] = schedule + } } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MultipleJvmMetrics.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MultipleJvmMetrics.scala new file mode 100644 index 000000000000..ceb59665aa07 --- /dev/null +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MultipleJvmMetrics.scala @@ -0,0 +1,20 @@ +package zio.metrics.jvm + +import zio.{Clock, Has, NonEmptyChunk, System, ZIOApp, ZManaged} + +/** Base trait for managing multiple JvmMetrics collectors together */ +trait MultipleJvmMetrics { + protected val collectors: NonEmptyChunk[JvmMetrics] + + /** + * While acquired it starts fibers periodically updating the same JVM metrics + * as the Prometheus Java client's default exporters + */ + lazy val collectDefaultJvmMetrics: ZManaged[Has[Clock] with Has[System], Throwable, Unit] = + ZManaged.foreachParDiscard(collectors)(_.collectMetrics) + + /** A ZIO application that collects the same JVM metrics as the Prometheus Java client's default exporters. */ + lazy val app: ZIOApp = + collectors.tail.map(_.app).foldLeft(collectors.head.app)(_ <> _) + +} diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala b/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala index 1e77dd1b9af9..c2caaf2fd7b8 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/Standard.scala @@ -8,9 +8,7 @@ import java.lang.reflect.Method import java.nio.charset.StandardCharsets import scala.util.{Failure, Success, Try} -trait Standard - -object Standard extends JvmMetrics { +trait Standard extends JvmMetrics { override type Feature = Standard override val featureTag: Tag[Standard] = Tag[Standard] @@ -139,5 +137,11 @@ object Standard extends JvmMetrics { .repeat(collectionSchedule) .interruptible .forkManaged - } yield new Standard {} + } yield this +} + +object Standard extends Standard with JvmMetrics.DefaultSchedule { + def withSchedule(schedule: Schedule[Any, Any, Unit]): Standard = new Standard { + override protected val collectionSchedule: Schedule[Any, Any, Unit] = schedule + } } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala b/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala index b8f1aed1cd45..fa82995feed5 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/Thread.scala @@ -5,9 +5,7 @@ import zio._ import java.lang.management.{ManagementFactory, ThreadMXBean} -trait Thread - -object Thread extends JvmMetrics { +trait Thread extends JvmMetrics { override type Feature = Thread override val featureTag: Tag[Thread] = Tag[Thread] @@ -77,5 +75,11 @@ object Thread extends JvmMetrics { threadMXBean <- Task(ManagementFactory.getThreadMXBean).toManaged _ <- reportThreadMetrics(threadMXBean).repeat(collectionSchedule).interruptible.forkManaged - } yield new Thread {} + } yield this +} + +object Thread extends Thread with JvmMetrics.DefaultSchedule { + def withSchedule(schedule: Schedule[Any, Any, Unit]): Thread = new Thread { + override protected val collectionSchedule: Schedule[Any, Any, Unit] = schedule + } } diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala b/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala index 65e4c15cb906..57d97d5ae492 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/VersionInfo.scala @@ -3,9 +3,7 @@ package zio.metrics.jvm import zio.ZIOMetric.Gauge import zio._ -trait VersionInfo - -object VersionInfo extends JvmMetrics { +trait VersionInfo extends JvmMetrics { override type Feature = VersionInfo override val featureTag: Tag[VersionInfo] = Tag[VersionInfo] @@ -27,5 +25,11 @@ object VersionInfo extends JvmMetrics { } yield () override val collectMetrics: ZManaged[Has[Clock] with Has[System], Throwable, VersionInfo] = - reportVersions().repeat(collectionSchedule).interruptible.forkManaged.as(new VersionInfo {}) + reportVersions().repeat(collectionSchedule).interruptible.forkManaged.as(this) +} + +object VersionInfo extends VersionInfo with JvmMetrics.DefaultSchedule { + def withSchedule(schedule: Schedule[Any, Any, Unit]): VersionInfo = new VersionInfo { + override protected val collectionSchedule: Schedule[Any, Any, Unit] = schedule + } } From 48764770c5e2cb6e4a7302ff0e7eece59793b2e2 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Tue, 28 Sep 2021 08:57:23 +0200 Subject: [PATCH 8/8] Fix for Scala 3 --- .../jvm/src/main/scala/zio/metrics/jvm/MultipleJvmMetrics.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/jvm/src/main/scala/zio/metrics/jvm/MultipleJvmMetrics.scala b/core/jvm/src/main/scala/zio/metrics/jvm/MultipleJvmMetrics.scala index ceb59665aa07..0dabf76fe02e 100644 --- a/core/jvm/src/main/scala/zio/metrics/jvm/MultipleJvmMetrics.scala +++ b/core/jvm/src/main/scala/zio/metrics/jvm/MultipleJvmMetrics.scala @@ -4,7 +4,7 @@ import zio.{Clock, Has, NonEmptyChunk, System, ZIOApp, ZManaged} /** Base trait for managing multiple JvmMetrics collectors together */ trait MultipleJvmMetrics { - protected val collectors: NonEmptyChunk[JvmMetrics] + protected def collectors: NonEmptyChunk[JvmMetrics] /** * While acquired it starts fibers periodically updating the same JVM metrics