Skip to content

Instantly share code, notes, and snippets.

@ssledz
Last active December 4, 2019 20:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ssledz/88fa72013912a238367b76e65128a291 to your computer and use it in GitHub Desktop.
Save ssledz/88fa72013912a238367b76e65128a291 to your computer and use it in GitHub Desktop.
MetricsService driven by io.dropwizard.metrics
val dropwizardMetrics = Seq(
"metrics-graphite",
"metrics-core",
"metrics-jvm"
).map("io.dropwizard.metrics" % _ % "4.0.5")
val logging = "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2"
val logback = "ch.qos.logback" % "logback-classic" % "1.2.3"
import java.net.{InetAddress, InetSocketAddress}
import java.util.concurrent.TimeUnit
import cats.effect.{IO, Resource}
import cats.implicits._
import com.codahale.metrics.graphite.{Graphite, GraphiteReporter}
import com.codahale.metrics.jvm.{GarbageCollectorMetricSet, MemoryUsageGaugeSet}
import com.codahale.metrics.{MetricFilter, MetricRegistry, ScheduledReporter, Slf4jReporter}
import com.typesafe.scalalogging.LazyLogging
import io.github.ssledz.MetricsService.MetricsConfig.GraphiteConfig
import org.slf4j.LoggerFactory
trait MetricsService {
def measureAndLog[A](timerName: String)(op: => A): A
def measureAndLogF[A](timerName: String)(fa: IO[A]): IO[A]
def meter(name: String, n: Long = 1): Unit
def close(): Unit
}
object MetricsService extends LazyLogging {
val Nop: MetricsService = new MetricsService {
def measureAndLog[A](timerName: String)(op: => A): A = op
def meter(name: String, n: Long): Unit = ()
def measureAndLogF[A](timerName: String)(fa: IO[A]): IO[A] = fa
def close(): Unit = ()
}
val NopIO: Resource[IO, MetricsService] = Resource.make(IO(Nop))(_ => IO.unit)
def resource(cfg: MetricsConfig): Resource[IO, MetricsService] = {
val registry: MetricRegistry = new MetricRegistry
val slf4j = IO(slf4jReporter(registry, cfg))
val start: ScheduledReporter => IO[ScheduledReporter] = r =>
IO {
r.start(cfg.schedule.toLong, TimeUnit.SECONDS)
r
}
val reporter = cfg.graphite match {
case Some(c) =>
IO(graphiteReporter(registry, c))
.handleErrorWith { ex =>
IO(logger.error("Error during initialization of metrics service, fallback to slf4j reporter", ex)) >> slf4j
}
case None => IO(logger.info("Using slf4j metrics reporter")) >> slf4j
}
registry.register("jvm.gc", new GarbageCollectorMetricSet)
registry.register("jvm.mem", new MemoryUsageGaugeSet)
Resource.make[IO, MetricsService](reporter >>= start >>= (r => IO(new DefaultMetricsService(registry, r))))(r => IO(r.close()))
}
private def graphiteReporter(registry: MetricRegistry, cfg: GraphiteConfig): GraphiteReporter = {
val graphite = new Graphite(new InetSocketAddress(cfg.host, cfg.port))
GraphiteReporter
.forRegistry(registry)
.prefixedWith(cfg.resolvePrefix)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.filter(MetricFilter.ALL)
.build(graphite)
}
private def slf4jReporter(registry: MetricRegistry, cfg: MetricsConfig): Slf4jReporter =
Slf4jReporter
.forRegistry(registry)
.outputTo(LoggerFactory.getLogger(cfg.slf4jLoggerName))
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build
private class DefaultMetricsService(registry: MetricRegistry, reporter: ScheduledReporter) extends MetricsService {
def measureAndLog[A](timerName: String)(op: => A): A = registry.timer(timerName).time(() => op)
def meter(name: String, n: Long): Unit = registry.meter(name).mark(n)
def close(): Unit = reporter.close()
def measureAndLogF[A](timerName: String)(fa: IO[A]): IO[A] =
for {
t <- IO(registry.timer(timerName).time())
a <- fa
_ <- IO(t.stop)
} yield a
}
case class MetricsConfig(graphite: Option[GraphiteConfig], slf4jLoggerName: String, schedule: Int = 60)
object MetricsConfig {
case class GraphiteConfig(port: Int, host: String, prefix: String) {
def resolvePrefix: String = prefix.replace("[ip]", InetAddress.getLocalHost.getHostName)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment