Skip to content

Commit

Permalink
Add IORuntimeMetrics
Browse files Browse the repository at this point in the history
  • Loading branch information
iRevive committed Dec 2, 2024
1 parent 3c6356d commit bb51631
Show file tree
Hide file tree
Showing 13 changed files with 665 additions and 26 deletions.
24 changes: 22 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import com.typesafe.tools.mima.core._

ThisBuild / tlBaseVersion := "0.11"
ThisBuild / tlBaseVersion := "0.12"

ThisBuild / organization := "org.typelevel"
ThisBuild / organizationName := "Typelevel"
Expand Down Expand Up @@ -76,7 +76,7 @@ ThisBuild / mergifyPrRules ++= Seq(
)

val CatsVersion = "2.11.0"
val CatsEffectVersion = "3.5.7"
val CatsEffectVersion = "3.6-28f8f29"
val CatsMtlVersion = "1.4.0"
val FS2Version = "3.11.0"
val MUnitVersion = "1.0.0"
Expand Down Expand Up @@ -130,6 +130,7 @@ lazy val root = tlCrossRootProject
`core-metrics`,
`core-trace`,
core,
`instrumentation-metrics`,
`sdk-common`,
`sdk-metrics`,
`sdk-metrics-testkit`,
Expand Down Expand Up @@ -235,6 +236,23 @@ lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform)
)
.settings(scalafixSettings)

//
// Instrumentation
//

lazy val `instrumentation-metrics` = crossProject(JVMPlatform, JSPlatform, NativePlatform)
.crossType(CrossType.Full)
.in(file("instrumentation/metrics"))
.dependsOn(`core-metrics`)
.settings(munitDependencies)
.settings(
name := "otel4s-instrumentation-metrics",
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-effect-testkit" % CatsEffectVersion % Test
)
)
.settings(scalafixSettings)

//
// SDK
//
Expand Down Expand Up @@ -820,6 +838,7 @@ lazy val docs = project
.dependsOn(
oteljava,
`oteljava-testkit`,
`instrumentation-metrics`.jvm,
sdk.jvm,
`sdk-exporter`.jvm,
`sdk-exporter-prometheus`.jvm,
Expand Down Expand Up @@ -889,6 +908,7 @@ lazy val unidocs = project
`core-metrics`.jvm,
`core-trace`.jvm,
core.jvm,
`instrumentation-metrics`.jvm,
`sdk-common`.jvm,
`sdk-metrics`.jvm,
`sdk-metrics-testkit`.jvm,
Expand Down
1 change: 1 addition & 0 deletions docs/instrumentation/directory.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ laika.title = Instrumentation

laika.navigationOrder = [
metrics.md
metrics-cats-effect-io-runtime.md
tracing.md
tracing-cross-service-propagation.md
]
103 changes: 103 additions & 0 deletions docs/instrumentation/metrics-cats-effect-io-runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Metrics | Cats Effect IO runtime

## Available metrics

## Getting started

Add the following configuration to the favorite build tool:

@:select(build-tool)

@:choice(sbt)

Add settings to the `build.sbt`:

```scala
libraryDependencies ++= Seq(
"org.typelevel" %% "otel4s-instrumentation-metrics" % "@VERSION@" // <1>
)
```

@:choice(scala-cli)

Add directives to the `*.scala` file:

```scala
//> using dep "org.typelevel::otel4s-instrumentation-metrics:@VERSION@" // <1>
```

@:@

1. Add the `otel4s-instrumentation-metrics` library

## Registering metrics collectors

@:select(otel-backend)

@:choice(oteljava)

```scala mdoc:reset:silent
import cats.effect._
import org.typelevel.otel4s.instrumentation.ce.IORuntimeMetrics
import org.typelevel.otel4s.metrics.MeterProvider
import org.typelevel.otel4s.trace.TracerProvider
import org.typelevel.otel4s.oteljava.OtelJava

object Main extends IOApp.Simple {

def run: IO[Unit] =
OtelJava.autoConfigured[IO]().use { otel4s =>
implicit val mp: MeterProvider[IO] = otel4s.meterProvider
IORuntimeMetrics
.register[IO](runtime.metrics, IORuntimeMetrics.Config.default)
.surround {
program(otel4s.meterProvider, otel4s.tracerProvider)
}
}

@annotation.nowarn("cat=unused")
def program(
meterProvider: MeterProvider[IO],
tracerProvider: TracerProvider[IO]
): IO[Unit] =
IO.unit

}
```

@:choice(sdk)

```scala mdoc:reset:silent
import cats.effect._
import org.typelevel.otel4s.instrumentation.ce.IORuntimeMetrics
import org.typelevel.otel4s.metrics.MeterProvider
import org.typelevel.otel4s.trace.TracerProvider
import org.typelevel.otel4s.sdk.OpenTelemetrySdk

object Main extends IOApp.Simple {

def run: IO[Unit] =
OpenTelemetrySdk.autoConfigured[IO]().use { autoConfigured =>
val sdk = autoConfigured.sdk
implicit val mp: MeterProvider[IO] = sdk.meterProvider
IORuntimeMetrics
.register[IO](runtime.metrics, IORuntimeMetrics.Config.default)
.surround {
program(sdk.meterProvider, sdk.tracerProvider)
}
}

@annotation.nowarn("cat=unused")
def program(
meterProvider: MeterProvider[IO],
tracerProvider: TracerProvider[IO]
): IO[Unit] =
IO.unit

}
```

@:@


## Customization
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.typelevel.otel4s.instrumentation.ce

import cats.effect.Resource
import cats.effect.Sync
import cats.effect.unsafe.metrics.{IORuntimeMetrics => CatsIORuntimeMetrics}
import cats.syntax.applicative._
import org.typelevel.otel4s.Attributes
import org.typelevel.otel4s.metrics.MeterProvider

private[ce] trait IORuntimeMetricsPlatform {
self: IORuntimeMetrics.type =>

sealed trait Config {
def cpuStarvationMetrics: Boolean
def cpuStarvationMetricsAttributes: Attributes

def withCpuStarvationMetrics: Config
def withoutCpuStarvationMetrics: Config
def withCpuStarvationMetricsAttributes(attributes: Attributes): Config
}

object Config {
private val Default: Config = Impl(
cpuStarvationMetrics = true,
cpuStarvationMetricsAttributes = Attributes.empty
)

def default: Config = Default

private case class Impl(
cpuStarvationMetrics: Boolean,
cpuStarvationMetricsAttributes: Attributes
) extends Config {
def withCpuStarvationMetrics: Config =
copy(cpuStarvationMetrics = true)

def withoutCpuStarvationMetrics: Config =
copy(cpuStarvationMetrics = false)

def withCpuStarvationMetricsAttributes(attributes: Attributes): Config =
copy(cpuStarvationMetricsAttributes = attributes)
}
}

/** Registers the following collectors depending on the `config`:
* - CPU starvation
*
* @example
* {{{
* object Main extends IOApp.Simple {
* def program(
* meterProvider: MeterProvider[IO],
* tracerProvider: TracerProvider[IO]
* ): IO[Unit] = ???
*
* def run: IO[Unit] =
* OpenTelemetrySdk.autoConfigured[IO]().use { autoConfigured =>
* val sdk = autoConfigured.sdk
* implicit val mp: MeterProvider[IO] = sdk.meterProvider
*
* IORuntimeMetrics
* .register[IO](runtime.metrics, IORuntimeMetrics.Config.default)
* .surround {
* program(sdk.meterProvider, sdk.tracerProvider)
* }
* }
* }
* }}}
*
* =CPU starvation metrics=
*
* Registers the CPU starvation:
* - `cats.effect.runtime.cpu.starvation.count`
* - `cats.effect.runtime.cpu.starvation.clock.drift.current`
* - `cats.effect.runtime.cpu.starvation.clock.drift.max`
*
* To disable CPU starvation metrics, customize a config:
* {{{
* val config = IORuntimeMetrics.Config.default.withoutCpuStarvationMetrics
* IORuntimeMetrics.register[IO](runtime.metrics, config)
* }}}
*
* To attach attributes to CPU starvation metrics, customize a config:
* {{{
* val config = IORuntimeMetrics.Config.default.withCpuStarvationMetricsAttributes(
* Attributes(Attribute("key", "value"))
* )
* IORuntimeMetrics.register[IO](runtime.metrics, config)
* }}}
*/
def register[F[_]: Sync: MeterProvider](
metrics: CatsIORuntimeMetrics,
config: Config
): Resource[F, Unit] =
Resource.eval(MeterProvider[F].get(Const.MeterNamespace)).flatMap { implicit meter =>
cpuStarvationMetrics(
metrics.cpuStarvation,
config.cpuStarvationMetricsAttributes
).whenA(config.cpuStarvationMetrics)
}

}
Loading

0 comments on commit bb51631

Please sign in to comment.