This repository has been archived by the owner on Jan 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
647 additions
and
291 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 13 additions & 20 deletions
33
app/com/github/stijndehaes/playprometheusfilters/filters/LatencyFilter.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,21 @@ | ||
package com.github.stijndehaes.playprometheusfilters.filters | ||
|
||
import akka.stream.Materializer | ||
import com.google.inject.{Inject, Singleton} | ||
import io.prometheus.client.{CollectorRegistry, Histogram} | ||
import play.api.mvc.{Filter, RequestHeader, Result} | ||
import com.github.stijndehaes.playprometheusfilters.metrics.DefaultUnmatchedDefaults | ||
import com.github.stijndehaes.playprometheusfilters.metrics.LatencyRequestMetrics.LatencyOnlyRequestMetricsBuilder | ||
import javax.inject.{ Inject, Singleton } | ||
import io.prometheus.client.CollectorRegistry | ||
import play.api.Configuration | ||
|
||
import scala.concurrent.{ExecutionContext, Future} | ||
import scala.concurrent.ExecutionContext | ||
|
||
/** | ||
* A simple [[MetricsFilter]] using a histogram metric to record latency without any labels. | ||
*/ | ||
@Singleton | ||
class LatencyFilter @Inject()(registry: CollectorRegistry) (implicit val mat: Materializer, ec: ExecutionContext) extends Filter { | ||
|
||
private[filters] val requestLatency = Histogram.build | ||
.name("requests_latency_seconds") | ||
.help("Request latency in seconds.") | ||
.register(registry) | ||
|
||
def apply(nextFilter: RequestHeader => Future[Result]) | ||
(requestHeader: RequestHeader): Future[Result] = { | ||
|
||
val requestTimer = requestLatency.startTimer | ||
nextFilter(requestHeader).map { result => | ||
requestTimer.observeDuration() | ||
result | ||
} | ||
} | ||
class LatencyFilter @Inject()(registry: CollectorRegistry, configuration: Configuration)(implicit mat: Materializer, ec: ExecutionContext) extends MetricsFilter(configuration) { | ||
|
||
override val metrics = List( | ||
LatencyOnlyRequestMetricsBuilder.build(registry, DefaultUnmatchedDefaults) | ||
) | ||
} |
68 changes: 68 additions & 0 deletions
68
app/com/github/stijndehaes/playprometheusfilters/filters/MetricsFilter.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package com.github.stijndehaes.playprometheusfilters.filters | ||
import akka.stream.Materializer | ||
import com.github.stijndehaes.playprometheusfilters.metrics.RequestMetric | ||
import io.prometheus.client.Collector | ||
import play.api.Configuration | ||
import play.api.mvc.{Filter, RequestHeader, Result} | ||
|
||
import scala.concurrent.{ExecutionContext, Future} | ||
|
||
/** | ||
* Generic filter implementation to add metrics for a request. | ||
* Subclasses only have to define the `metrics` property to apply metrics. | ||
* | ||
* {{{ | ||
* @Singleton | ||
* class MyFilter @Inject()(registry: CollectorRegistry)(implicit mat: Materializer, ec: ExecutionContext) extends MetricsFilter { | ||
* | ||
* override val metrics = List( | ||
* LatencyOnlyRequestMetricsBuilder.build(registry, DefaultUnmatchedDefaults) | ||
* ) | ||
* } | ||
* }}} | ||
* | ||
* Metrics can be created by using a [[com.github.stijndehaes.playprometheusfilters.metrics.RequestMetricBuilder RequestMetricBuilder]]. | ||
* The builder creates and configures the metrics for the class instance. | ||
* | ||
* See [[com.github.stijndehaes.playprometheusfilters.metrics.CounterRequestMetrics CounterRequestMetrics]] and | ||
* [[com.github.stijndehaes.playprometheusfilters.metrics.LatencyRequestMetrics LatencyRequestMetrics]] for some provided | ||
* builders. | ||
* | ||
* @param mat | ||
* @param ec | ||
*/ | ||
abstract class MetricsFilter(configuration: Configuration)(implicit val mat: Materializer, ec: ExecutionContext) extends Filter { | ||
|
||
val metrics: List[RequestMetric[_]] | ||
|
||
val excludePaths = { | ||
import collection.JavaConverters._ | ||
Option(configuration.underlying) | ||
.map(_.getStringList("play-prometheus-filters.exclude.paths")) | ||
.map(_.asScala.toSet) | ||
.map(_.map(_.r)) | ||
.getOrElse(Set.empty) | ||
} | ||
|
||
def apply(nextFilter: RequestHeader => Future[Result]) | ||
(requestHeader: RequestHeader): Future[Result] = { | ||
|
||
// check if current uri is excluded from metrics | ||
def urlIsExcluded = excludePaths.exists(_.findFirstMatchIn(requestHeader.uri).isDefined) | ||
|
||
val startTime = System.nanoTime | ||
|
||
nextFilter(requestHeader).map { implicit result => | ||
implicit val rh = requestHeader | ||
|
||
if (!urlIsExcluded) { | ||
val endTime = System.nanoTime | ||
val requestTime = (endTime - startTime) / Collector.NANOSECONDS_PER_SECOND | ||
|
||
metrics.foreach(_.mark(requestTime)) | ||
} | ||
|
||
result | ||
} | ||
} | ||
} |
43 changes: 14 additions & 29 deletions
43
app/com/github/stijndehaes/playprometheusfilters/filters/RouteLatencyFilter.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,22 @@ | ||
package com.github.stijndehaes.playprometheusfilters.filters | ||
|
||
import akka.stream.Materializer | ||
import com.google.inject.{Inject, Singleton} | ||
import io.prometheus.client.{CollectorRegistry, Histogram} | ||
import play.api.mvc.{Filter, RequestHeader, Result} | ||
import play.api.routing.Router | ||
import com.github.stijndehaes.playprometheusfilters.metrics.DefaultUnmatchedDefaults | ||
import com.github.stijndehaes.playprometheusfilters.metrics.LatencyRequestMetrics.RouteLatencyRequestMetricsBuilder | ||
import javax.inject.{ Inject, Singleton } | ||
import io.prometheus.client.CollectorRegistry | ||
import play.api.Configuration | ||
|
||
import scala.concurrent.{ExecutionContext, Future} | ||
import scala.concurrent.ExecutionContext | ||
|
||
/** | ||
* A simple [[MetricsFilter]] using a counter metric to count requests. | ||
* Only adds a 'route' label. | ||
*/ | ||
@Singleton | ||
class RouteLatencyFilter @Inject()(registry: CollectorRegistry) (implicit val mat: Materializer, ec: ExecutionContext) extends Filter { | ||
|
||
private[filters] val requestLatency = Histogram.build | ||
.name("requests_latency_seconds") | ||
.help("Request latency in seconds.") | ||
.labelNames("RouteActionMethod") | ||
.register(registry) | ||
|
||
def apply(nextFilter: RequestHeader => Future[Result]) | ||
(requestHeader: RequestHeader): Future[Result] = { | ||
val routeLabel = requestHeader.attrs | ||
.get(Router.Attrs.HandlerDef) | ||
.map(_.method) | ||
.getOrElse(RouteLatencyFilter.unmatchedRoute) | ||
val requestTimer = requestLatency.labels(routeLabel).startTimer | ||
nextFilter(requestHeader).map { result => | ||
requestTimer.observeDuration() | ||
result | ||
} | ||
} | ||
class RouteLatencyFilter @Inject()(registry: CollectorRegistry, configuration: Configuration)(implicit mat: Materializer, ec: ExecutionContext) extends MetricsFilter(configuration) { | ||
|
||
override val metrics = List( | ||
RouteLatencyRequestMetricsBuilder.build(registry, DefaultUnmatchedDefaults) | ||
) | ||
} | ||
|
||
object RouteLatencyFilter { | ||
val unmatchedRoute: String = "unmatchedRoute" | ||
} |
66 changes: 16 additions & 50 deletions
66
app/com/github/stijndehaes/playprometheusfilters/filters/StatusAndRouteCounterFilter.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,22 @@ | ||
package com.github.stijndehaes.playprometheusfilters.filters | ||
|
||
import akka.stream.Materializer | ||
import com.google.inject.{Inject, Singleton} | ||
import io.prometheus.client.{CollectorRegistry, Counter} | ||
import play.api.mvc.{Filter, RequestHeader, Result} | ||
import play.api.routing.Router | ||
|
||
import scala.concurrent.{ExecutionContext, Future} | ||
|
||
import com.github.stijndehaes.playprometheusfilters.metrics.CounterRequestMetrics.CounterRequestMetricBuilder | ||
import com.github.stijndehaes.playprometheusfilters.metrics.DefaultUnmatchedDefaults | ||
import javax.inject.{ Inject, Singleton } | ||
import io.prometheus.client.CollectorRegistry | ||
import play.api.Configuration | ||
|
||
import scala.concurrent.ExecutionContext | ||
|
||
/** | ||
* A [[MetricsFilter]] using a counter metric to count requests. | ||
* Adds a 'method', 'status', 'controller', 'path' and 'verb' labels. | ||
*/ | ||
@Singleton | ||
class StatusAndRouteCounterFilter @Inject()(registry: CollectorRegistry)(implicit val mat: Materializer, ec: ExecutionContext) extends Filter { | ||
|
||
private[filters] val requestCounter = Counter.build() | ||
.name("http_requests_total") | ||
.help("Total amount of requests") | ||
.labelNames("method", "status", "controller", "path", "verb") | ||
.register(registry) | ||
|
||
def apply(nextFilter: RequestHeader => Future[Result]) | ||
(requestHeader: RequestHeader): Future[Result] = { | ||
|
||
nextFilter(requestHeader).map { result => | ||
val methodLabel = requestHeader.attrs | ||
.get(Router.Attrs.HandlerDef) | ||
.map(_.method) | ||
.getOrElse(StatusAndRouteLatencyFilter.unmatchedRoute) | ||
val statusLabel = result.header.status.toString | ||
val controllerLabel = requestHeader.attrs | ||
.get(Router.Attrs.HandlerDef) | ||
.map(_.controller) | ||
.getOrElse(StatusAndRouteLatencyFilter.unmatchedController) | ||
val pathLabel = requestHeader.attrs | ||
.get(Router.Attrs.HandlerDef) | ||
.map(_.path) | ||
.getOrElse(StatusAndRouteLatencyFilter.unmatchedPath) | ||
val verbLabel = requestHeader.attrs | ||
.get(Router.Attrs.HandlerDef) | ||
.map(_.verb) | ||
.getOrElse(StatusAndRouteLatencyFilter.unmatchedVerb) | ||
requestCounter.labels(methodLabel, statusLabel, controllerLabel, pathLabel, verbLabel).inc() | ||
result | ||
} | ||
} | ||
class StatusAndRouteCounterFilter @Inject()(registry: CollectorRegistry, configuration: Configuration)(implicit mat: Materializer, ec: ExecutionContext) extends MetricsFilter(configuration) { | ||
|
||
override val metrics = List( | ||
CounterRequestMetricBuilder.build(registry, DefaultUnmatchedDefaults) | ||
) | ||
} | ||
|
||
object StatusAndRouteCounterFilter { | ||
val unmatchedRoute: String = "unmatchedRoute" | ||
val unmatchedController: String = "unmatchedController" | ||
val unmatchedPath: String = "unmatchedPath" | ||
val unmatchedVerb: String = "unmatchedVerb" | ||
} | ||
|
||
|
||
|
Oops, something went wrong.